001
014
015 package com.liferay.portlet.documentlibrary.util;
016
017 import com.liferay.portal.fabric.InputResource;
018 import com.liferay.portal.fabric.OutputResource;
019 import com.liferay.portal.kernel.log.Log;
020 import com.liferay.portal.kernel.log.LogFactoryUtil;
021 import com.liferay.portal.kernel.messaging.DestinationNames;
022 import com.liferay.portal.kernel.process.ClassPathUtil;
023 import com.liferay.portal.kernel.process.ProcessCallable;
024 import com.liferay.portal.kernel.process.ProcessChannel;
025 import com.liferay.portal.kernel.process.ProcessException;
026 import com.liferay.portal.kernel.process.ProcessExecutorUtil;
027 import com.liferay.portal.kernel.repository.model.FileEntry;
028 import com.liferay.portal.kernel.repository.model.FileVersion;
029 import com.liferay.portal.kernel.util.FileUtil;
030 import com.liferay.portal.kernel.util.PropsKeys;
031 import com.liferay.portal.kernel.util.ServerDetector;
032 import com.liferay.portal.kernel.util.SetUtil;
033 import com.liferay.portal.kernel.util.StreamUtil;
034 import com.liferay.portal.kernel.util.StringBundler;
035 import com.liferay.portal.kernel.util.StringPool;
036 import com.liferay.portal.kernel.util.SystemEnv;
037 import com.liferay.portal.kernel.xml.Element;
038 import com.liferay.portal.kernel.xuggler.XugglerUtil;
039 import com.liferay.portal.log.Log4jLogFactoryImpl;
040 import com.liferay.portal.repository.liferayrepository.model.LiferayFileVersion;
041 import com.liferay.portal.util.PropsUtil;
042 import com.liferay.portal.util.PropsValues;
043 import com.liferay.portlet.documentlibrary.NoSuchFileEntryException;
044 import com.liferay.portlet.documentlibrary.model.DLProcessorConstants;
045 import com.liferay.portlet.exportimport.lar.PortletDataContext;
046 import com.liferay.util.log4j.Log4JUtil;
047
048 import java.io.File;
049 import java.io.InputStream;
050
051 import java.util.List;
052 import java.util.Map;
053 import java.util.Properties;
054 import java.util.Set;
055 import java.util.Vector;
056 import java.util.concurrent.CancellationException;
057 import java.util.concurrent.Future;
058
059 import org.apache.commons.lang.time.StopWatch;
060
061
067 public class AudioProcessorImpl
068 extends DLPreviewableProcessor implements AudioProcessor {
069
070 @Override
071 public void afterPropertiesSet() {
072 boolean valid = true;
073
074 if ((_PREVIEW_TYPES.length == 0) || (_PREVIEW_TYPES.length > 2)) {
075 valid = false;
076 }
077 else {
078 for (String previewType : _PREVIEW_TYPES) {
079 if (!previewType.equals("mp3") && !previewType.equals("ogg")) {
080 valid = false;
081
082 break;
083 }
084 }
085 }
086
087 if (!valid && _log.isWarnEnabled()) {
088 StringBundler sb = new StringBundler(5);
089
090 sb.append("Liferay is incorrectly configured to generate video ");
091 sb.append("previews using video containers other than MP3 or ");
092 sb.append("OGG. Please change the property ");
093 sb.append(PropsKeys.DL_FILE_ENTRY_PREVIEW_AUDIO_CONTAINERS);
094 sb.append(" in portal-ext.properties.");
095
096 _log.warn(sb.toString());
097 }
098
099 FileUtil.mkdirs(PREVIEW_TMP_PATH);
100 }
101
102 @Override
103 public void generateAudio(
104 FileVersion sourceFileVersion, FileVersion destinationFileVersion)
105 throws Exception {
106
107 _generateAudio(sourceFileVersion, destinationFileVersion);
108 }
109
110 @Override
111 public Set<String> getAudioMimeTypes() {
112 return _audioMimeTypes;
113 }
114
115 @Override
116 public InputStream getPreviewAsStream(FileVersion fileVersion, String type)
117 throws Exception {
118
119 return doGetPreviewAsStream(fileVersion, type);
120 }
121
122 @Override
123 public long getPreviewFileSize(FileVersion fileVersion, String type)
124 throws Exception {
125
126 return doGetPreviewFileSize(fileVersion, type);
127 }
128
129 @Override
130 public String getType() {
131 return DLProcessorConstants.AUDIO_PROCESSOR;
132 }
133
134 @Override
135 public boolean hasAudio(FileVersion fileVersion) {
136 boolean hasAudio = false;
137
138 try {
139 hasAudio = _hasAudio(fileVersion);
140
141 if (!hasAudio && isSupported(fileVersion)) {
142 _queueGeneration(null, fileVersion);
143 }
144 }
145 catch (Exception e) {
146 _log.error(e, e);
147 }
148
149 return hasAudio;
150 }
151
152 @Override
153 public boolean isAudioSupported(FileVersion fileVersion) {
154 return isSupported(fileVersion);
155 }
156
157 @Override
158 public boolean isAudioSupported(String mimeType) {
159 return isSupported(mimeType);
160 }
161
162 @Override
163 public boolean isSupported(String mimeType) {
164 if (_audioMimeTypes.contains(mimeType) && XugglerUtil.isEnabled()) {
165 return true;
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 = new StopWatch();
353
354 stopWatch.start();
355
356 try {
357 if (PropsValues.DL_FILE_ENTRY_PREVIEW_FORK_PROCESS_ENABLED) {
358 ProcessCallable<String> processCallable =
359 new LiferayAudioProcessCallable(
360 ServerDetector.getServerId(),
361 PropsUtil.get(PropsKeys.LIFERAY_HOME),
362 Log4JUtil.getCustomLogSettings(), srcFile, destFile,
363 containerType,
364 PropsUtil.getProperties(
365 PropsKeys.DL_FILE_ENTRY_PREVIEW_AUDIO, false));
366
367 ProcessChannel<String> processChannel =
368 ProcessExecutorUtil.execute(
369 ClassPathUtil.getPortalProcessConfig(),
370 processCallable);
371
372 Future<String> future =
373 processChannel.getProcessNoticeableFuture();
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 final Log _log = LogFactoryUtil.getLog(
459 AudioProcessorImpl.class);
460
461 private final Set<String> _audioMimeTypes = SetUtil.fromArray(
462 PropsValues.DL_FILE_ENTRY_PREVIEW_AUDIO_MIME_TYPES);
463 private final List<Long> _fileVersionIds = new Vector<>();
464
465 private static class LiferayAudioProcessCallable
466 implements ProcessCallable<String> {
467
468 public LiferayAudioProcessCallable(
469 String serverId, String liferayHome,
470 Map<String, String> customLogSettings, File inputFile,
471 File outputFile, String audioContainer,
472 Properties audioProperties) {
473
474 _serverId = serverId;
475 _liferayHome = liferayHome;
476 _customLogSettings = customLogSettings;
477 _inputFile = inputFile;
478 _outputFile = outputFile;
479 _audioContainer = audioContainer;
480 _audioProperties = audioProperties;
481 }
482
483 @Override
484 public String call() throws ProcessException {
485 XugglerAutoInstallHelper.installNativeLibraries();
486
487 Properties systemProperties = System.getProperties();
488
489 SystemEnv.setProperties(systemProperties);
490
491 Class<?> clazz = getClass();
492
493 ClassLoader classLoader = clazz.getClassLoader();
494
495 Log4JUtil.initLog4J(
496 _serverId, _liferayHome, classLoader, new Log4jLogFactoryImpl(),
497 _customLogSettings);
498
499 try {
500 LiferayConverter liferayConverter = new LiferayAudioConverter(
501 _inputFile.getCanonicalPath(),
502 _outputFile.getCanonicalPath(), _audioContainer,
503 _audioProperties);
504
505 liferayConverter.convert();
506 }
507 catch (Exception e) {
508 throw new ProcessException(e);
509 }
510
511 return StringPool.BLANK;
512 }
513
514 private static final long serialVersionUID = 1L;
515
516 private final String _audioContainer;
517 private final Properties _audioProperties;
518 private final Map<String, String> _customLogSettings;
519
520 @InputResource
521 private final File _inputFile;
522
523 private final String _liferayHome;
524
525 @OutputResource
526 private final File _outputFile;
527
528 private final String _serverId;
529
530 }
531
532 }