001
014
015 package com.liferay.portlet.documentlibrary.util;
016
017 import com.liferay.portal.kernel.lar.PortletDataContext;
018 import com.liferay.portal.kernel.log.Log;
019 import com.liferay.portal.kernel.log.LogFactoryUtil;
020 import com.liferay.portal.kernel.messaging.DestinationNames;
021 import com.liferay.portal.kernel.process.ClassPathUtil;
022 import com.liferay.portal.kernel.process.ProcessCallable;
023 import com.liferay.portal.kernel.process.ProcessException;
024 import com.liferay.portal.kernel.process.ProcessExecutor;
025 import com.liferay.portal.kernel.repository.model.FileEntry;
026 import com.liferay.portal.kernel.repository.model.FileVersion;
027 import com.liferay.portal.kernel.util.FileUtil;
028 import com.liferay.portal.kernel.util.PropsKeys;
029 import com.liferay.portal.kernel.util.ServerDetector;
030 import com.liferay.portal.kernel.util.SetUtil;
031 import com.liferay.portal.kernel.util.StreamUtil;
032 import com.liferay.portal.kernel.util.StringBundler;
033 import com.liferay.portal.kernel.util.StringPool;
034 import com.liferay.portal.kernel.util.SystemEnv;
035 import com.liferay.portal.kernel.util.Validator;
036 import com.liferay.portal.kernel.xml.Element;
037 import com.liferay.portal.kernel.xuggler.XugglerUtil;
038 import com.liferay.portal.log.Log4jLogFactoryImpl;
039 import com.liferay.portal.repository.liferayrepository.model.LiferayFileVersion;
040 import com.liferay.portal.util.PropsUtil;
041 import com.liferay.portal.util.PropsValues;
042 import com.liferay.portlet.documentlibrary.NoSuchFileEntryException;
043 import com.liferay.util.log4j.Log4JUtil;
044
045 import java.io.File;
046 import java.io.InputStream;
047
048 import java.util.List;
049 import java.util.Map;
050 import java.util.Properties;
051 import java.util.Set;
052 import java.util.Vector;
053 import java.util.concurrent.CancellationException;
054 import java.util.concurrent.Future;
055
056 import org.apache.commons.lang.time.StopWatch;
057
058
064 public class AudioProcessorImpl
065 extends DLPreviewableProcessor implements AudioProcessor {
066
067 @Override
068 public void afterPropertiesSet() {
069 boolean valid = true;
070
071 if ((_PREVIEW_TYPES.length == 0) || (_PREVIEW_TYPES.length > 2)) {
072 valid = false;
073 }
074 else {
075 for (String previewType : _PREVIEW_TYPES) {
076 if (!previewType.equals("mp3") && !previewType.equals("ogg")) {
077 valid = false;
078
079 break;
080 }
081 }
082 }
083
084 if (!valid && _log.isWarnEnabled()) {
085 StringBundler sb = new StringBundler(5);
086
087 sb.append("Liferay is incorrectly configured to generate video ");
088 sb.append("previews using video containers other than MP3 or ");
089 sb.append("OGG. Please change the property ");
090 sb.append(PropsKeys.DL_FILE_ENTRY_PREVIEW_AUDIO_CONTAINERS);
091 sb.append(" in portal-ext.properties.");
092
093 _log.warn(sb.toString());
094 }
095
096 FileUtil.mkdirs(PREVIEW_TMP_PATH);
097 }
098
099 @Override
100 public void generateAudio(
101 FileVersion sourceFileVersion, FileVersion destinationFileVersion)
102 throws Exception {
103
104 _generateAudio(sourceFileVersion, destinationFileVersion);
105 }
106
107 @Override
108 public Set<String> getAudioMimeTypes() {
109 return _audioMimeTypes;
110 }
111
112 @Override
113 public InputStream getPreviewAsStream(FileVersion fileVersion, String type)
114 throws Exception {
115
116 return doGetPreviewAsStream(fileVersion, type);
117 }
118
119 @Override
120 public long getPreviewFileSize(FileVersion fileVersion, String type)
121 throws Exception {
122
123 return doGetPreviewFileSize(fileVersion, type);
124 }
125
126 @Override
127 public boolean hasAudio(FileVersion fileVersion) {
128 boolean hasAudio = false;
129
130 try {
131 hasAudio = _hasAudio(fileVersion);
132
133 if (!hasAudio && isSupported(fileVersion)) {
134 _queueGeneration(null, fileVersion);
135 }
136 }
137 catch (Exception e) {
138 _log.error(e, e);
139 }
140
141 return hasAudio;
142 }
143
144 @Override
145 public boolean isAudioSupported(FileVersion fileVersion) {
146 return isSupported(fileVersion);
147 }
148
149 @Override
150 public boolean isAudioSupported(String mimeType) {
151 return isSupported(mimeType);
152 }
153
154 @Override
155 public boolean isSupported(String mimeType) {
156 if (Validator.isNull(mimeType)) {
157 return false;
158 }
159
160 try {
161 if (XugglerUtil.isEnabled()) {
162 return _audioMimeTypes.contains(mimeType);
163 }
164 }
165 catch (Exception e) {
166 }
167
168 return false;
169 }
170
171 @Override
172 public void trigger(
173 FileVersion sourceFileVersion, FileVersion destinationFileVersion) {
174
175 super.trigger(sourceFileVersion, destinationFileVersion);
176
177 _queueGeneration(sourceFileVersion, destinationFileVersion);
178 }
179
180 @Override
181 protected void doExportGeneratedFiles(
182 PortletDataContext portletDataContext, FileEntry fileEntry,
183 Element fileEntryElement)
184 throws Exception {
185
186 exportPreviews(portletDataContext, fileEntry, fileEntryElement);
187 }
188
189 @Override
190 protected void doImportGeneratedFiles(
191 PortletDataContext portletDataContext, FileEntry fileEntry,
192 FileEntry importedFileEntry, Element fileEntryElement)
193 throws Exception {
194
195 importPreviews(
196 portletDataContext, fileEntry, importedFileEntry, fileEntryElement);
197 }
198
199 protected void exportPreviews(
200 PortletDataContext portletDataContext, FileEntry fileEntry,
201 Element fileEntryElement)
202 throws Exception {
203
204 FileVersion fileVersion = fileEntry.getFileVersion();
205
206 if (!isSupported(fileVersion) || !hasPreviews(fileVersion)) {
207 return;
208 }
209
210 if (!portletDataContext.isPerformDirectBinaryImport()) {
211 if ((_PREVIEW_TYPES.length == 0) || (_PREVIEW_TYPES.length > 2)) {
212 return;
213 }
214
215 for (String previewType : _PREVIEW_TYPES) {
216 if (previewType.equals("mp3") || previewType.equals("ogg")) {
217 exportPreview(
218 portletDataContext, fileEntry, fileEntryElement,
219 "audio", previewType);
220 }
221 }
222 }
223 }
224
225 @Override
226 protected List<Long> getFileVersionIds() {
227 return _fileVersionIds;
228 }
229
230 @Override
231 protected String getPreviewType(FileVersion fileVersion) {
232 return _PREVIEW_TYPES[0];
233 }
234
235 @Override
236 protected String[] getPreviewTypes() {
237 return _PREVIEW_TYPES;
238 }
239
240 @Override
241 protected String getThumbnailType(FileVersion fileVersion) {
242 return null;
243 }
244
245 protected void importPreviews(
246 PortletDataContext portletDataContext, FileEntry fileEntry,
247 FileEntry importedFileEntry, Element fileEntryElement)
248 throws Exception {
249
250 if ((_PREVIEW_TYPES.length == 0) || (_PREVIEW_TYPES.length > 2)) {
251 return;
252 }
253
254 for (String previewType : _PREVIEW_TYPES) {
255 if (previewType.equals("mp3") || previewType.equals("ogg")) {
256 importPreview(
257 portletDataContext, fileEntry, importedFileEntry,
258 fileEntryElement, "audio", previewType);
259 }
260 }
261 }
262
263 private void _generateAudio(
264 FileVersion sourceFileVersion, FileVersion destinationFileVersion)
265 throws Exception {
266
267 String tempFileId = DLUtil.getTempFileId(
268 destinationFileVersion.getFileEntryId(),
269 destinationFileVersion.getVersion());
270
271 File[] previewTempFiles = new File[_PREVIEW_TYPES.length];
272
273 for (int i = 0; i < _PREVIEW_TYPES.length; i++) {
274 previewTempFiles[i] = getPreviewTempFile(
275 tempFileId, _PREVIEW_TYPES[i]);
276 }
277
278 File audioTempFile = null;
279
280 InputStream inputStream = null;
281
282 try {
283 if (sourceFileVersion != null) {
284 copy(sourceFileVersion, destinationFileVersion);
285
286 return;
287 }
288
289 if (!XugglerUtil.isEnabled() || _hasAudio(destinationFileVersion)) {
290 return;
291 }
292
293 audioTempFile = FileUtil.createTempFile(
294 destinationFileVersion.getExtension());
295
296 if (!hasPreviews(destinationFileVersion)) {
297 File file = null;
298
299 if (destinationFileVersion instanceof LiferayFileVersion) {
300 try {
301 LiferayFileVersion liferayFileVersion =
302 (LiferayFileVersion)destinationFileVersion;
303
304 file = liferayFileVersion.getFile(false);
305 }
306 catch (UnsupportedOperationException uoe) {
307 }
308 }
309
310 if (file == null) {
311 inputStream = destinationFileVersion.getContentStream(
312 false);
313
314 FileUtil.write(audioTempFile, inputStream);
315
316 file = audioTempFile;
317 }
318
319 try {
320 _generateAudioXuggler(
321 destinationFileVersion, file, previewTempFiles);
322 }
323 catch (Exception e) {
324 _log.error(e, e);
325 }
326 }
327 }
328 catch (NoSuchFileEntryException nsfee) {
329 }
330 finally {
331 StreamUtil.cleanUp(inputStream);
332
333 _fileVersionIds.remove(destinationFileVersion.getFileVersionId());
334
335 for (int i = 0; i < previewTempFiles.length; i++) {
336 FileUtil.delete(previewTempFiles[i]);
337 }
338
339 FileUtil.delete(audioTempFile);
340 }
341 }
342
343 private void _generateAudioXuggler(
344 FileVersion fileVersion, File srcFile, File destFile,
345 String containerType)
346 throws Exception {
347
348 if (hasPreview(fileVersion, containerType)) {
349 return;
350 }
351
352 StopWatch stopWatch = null;
353
354 if (_log.isInfoEnabled()) {
355 stopWatch = new StopWatch();
356
357 stopWatch.start();
358 }
359
360 try {
361 if (PropsValues.DL_FILE_ENTRY_PREVIEW_FORK_PROCESS_ENABLED) {
362 ProcessCallable<String> processCallable =
363 new LiferayAudioProcessCallable(
364 ServerDetector.getServerId(),
365 PropsUtil.get(PropsKeys.LIFERAY_HOME),
366 Log4JUtil.getCustomLogSettings(),
367 srcFile.getCanonicalPath(), destFile.getCanonicalPath(),
368 containerType,
369 PropsUtil.getProperties(
370 PropsKeys.DL_FILE_ENTRY_PREVIEW_AUDIO, false));
371
372 Future<String> future = ProcessExecutor.execute(
373 ClassPathUtil.getPortalClassPath(), processCallable);
374
375 String processIdentity = String.valueOf(
376 fileVersion.getFileVersionId());
377
378 futures.put(processIdentity, future);
379
380 future.get();
381 }
382 else {
383 LiferayConverter liferayConverter = new LiferayAudioConverter(
384 srcFile.getCanonicalPath(), destFile.getCanonicalPath(),
385 containerType,
386 PropsUtil.getProperties(
387 PropsKeys.DL_FILE_ENTRY_PREVIEW_AUDIO, false));
388
389 liferayConverter.convert();
390 }
391 }
392 catch (CancellationException ce) {
393 if (_log.isInfoEnabled()) {
394 _log.info(
395 "Cancellation received for " +
396 fileVersion.getFileVersionId() + " " +
397 fileVersion.getTitle());
398 }
399 }
400 catch (Exception e) {
401 _log.error(e, e);
402 }
403
404 addFileToStore(
405 fileVersion.getCompanyId(), PREVIEW_PATH,
406 getPreviewFilePath(fileVersion, containerType), destFile);
407
408 if (_log.isInfoEnabled()) {
409 _log.info(
410 "Xuggler generated a " + containerType + " preview audio for " +
411 fileVersion.getFileVersionId() + " in " +
412 stopWatch.getTime() + "ms");
413 }
414 }
415
416 private void _generateAudioXuggler(
417 FileVersion fileVersion, File srcFile, File[] destFiles) {
418
419 try {
420 for (int i = 0; i < destFiles.length; i++) {
421 _generateAudioXuggler(
422 fileVersion, srcFile, destFiles[i], _PREVIEW_TYPES[i]);
423 }
424 }
425 catch (Exception e) {
426 _log.error(e, e);
427 }
428 }
429
430 private boolean _hasAudio(FileVersion fileVersion) throws Exception {
431 if (!isSupported(fileVersion)) {
432 return false;
433 }
434
435 return hasPreviews(fileVersion);
436 }
437
438 private void _queueGeneration(
439 FileVersion sourceFileVersion, FileVersion destinationFileVersion) {
440
441 if (_fileVersionIds.contains(
442 destinationFileVersion.getFileVersionId()) ||
443 !isSupported(destinationFileVersion)) {
444
445 return;
446 }
447
448 _fileVersionIds.add(destinationFileVersion.getFileVersionId());
449
450 sendGenerationMessage(
451 DestinationNames.DOCUMENT_LIBRARY_AUDIO_PROCESSOR,
452 sourceFileVersion, destinationFileVersion);
453 }
454
455 private static final String[] _PREVIEW_TYPES =
456 PropsValues.DL_FILE_ENTRY_PREVIEW_AUDIO_CONTAINERS;
457
458 private static Log _log = LogFactoryUtil.getLog(AudioProcessorImpl.class);
459
460 private Set<String> _audioMimeTypes = SetUtil.fromArray(
461 PropsValues.DL_FILE_ENTRY_PREVIEW_AUDIO_MIME_TYPES);
462 private List<Long> _fileVersionIds = new Vector<Long>();
463
464 private static class LiferayAudioProcessCallable
465 implements ProcessCallable<String> {
466
467 public LiferayAudioProcessCallable(
468 String serverId, String liferayHome,
469 Map<String, String> customLogSettings, String inputURL,
470 String outputURL, String audioContainer,
471 Properties audioProperties) {
472
473 _serverId = serverId;
474 _liferayHome = liferayHome;
475 _customLogSettings = customLogSettings;
476 _inputURL = inputURL;
477 _outputURL = outputURL;
478 _audioContainer = audioContainer;
479 _audioProperties = audioProperties;
480 }
481
482 @Override
483 public String call() throws ProcessException {
484 Properties systemProperties = System.getProperties();
485
486 SystemEnv.setProperties(systemProperties);
487
488 Class<?> clazz = getClass();
489
490 ClassLoader classLoader = clazz.getClassLoader();
491
492 Log4JUtil.initLog4J(
493 _serverId, _liferayHome, classLoader, new Log4jLogFactoryImpl(),
494 _customLogSettings);
495
496 try {
497 LiferayConverter liferayConverter = new LiferayAudioConverter(
498 _inputURL, _outputURL, _audioContainer, _audioProperties);
499
500 liferayConverter.convert();
501 }
502 catch (Exception e) {
503 throw new ProcessException(e);
504 }
505
506 return StringPool.BLANK;
507 }
508
509 private static final long serialVersionUID = 1L;
510
511 private String _audioContainer;
512 private Properties _audioProperties;
513 private Map<String, String> _customLogSettings;
514 private String _inputURL;
515 private String _liferayHome;
516 private String _outputURL;
517 private String _serverId;
518
519 }
520
521 }