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.lar.PortletDataContext;
020 import com.liferay.portal.kernel.log.Log;
021 import com.liferay.portal.kernel.log.LogFactoryUtil;
022 import com.liferay.portal.kernel.messaging.DestinationNames;
023 import com.liferay.portal.kernel.process.ClassPathUtil;
024 import com.liferay.portal.kernel.process.ProcessCallable;
025 import com.liferay.portal.kernel.process.ProcessChannel;
026 import com.liferay.portal.kernel.process.ProcessException;
027 import com.liferay.portal.kernel.process.ProcessExecutorUtil;
028 import com.liferay.portal.kernel.repository.model.FileEntry;
029 import com.liferay.portal.kernel.repository.model.FileVersion;
030 import com.liferay.portal.kernel.util.FileUtil;
031 import com.liferay.portal.kernel.util.PropsKeys;
032 import com.liferay.portal.kernel.util.ServerDetector;
033 import com.liferay.portal.kernel.util.SetUtil;
034 import com.liferay.portal.kernel.util.StreamUtil;
035 import com.liferay.portal.kernel.util.StringBundler;
036 import com.liferay.portal.kernel.util.StringPool;
037 import com.liferay.portal.kernel.util.SystemEnv;
038 import com.liferay.portal.kernel.util.Validator;
039 import com.liferay.portal.kernel.xml.Element;
040 import com.liferay.portal.kernel.xuggler.XugglerUtil;
041 import com.liferay.portal.log.Log4jLogFactoryImpl;
042 import com.liferay.portal.repository.liferayrepository.model.LiferayFileVersion;
043 import com.liferay.portal.util.PropsUtil;
044 import com.liferay.portal.util.PropsValues;
045 import com.liferay.portlet.documentlibrary.NoSuchFileEntryException;
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 boolean hasAudio(FileVersion fileVersion) {
131 boolean hasAudio = false;
132
133 try {
134 hasAudio = _hasAudio(fileVersion);
135
136 if (!hasAudio && isSupported(fileVersion)) {
137 _queueGeneration(null, fileVersion);
138 }
139 }
140 catch (Exception e) {
141 _log.error(e, e);
142 }
143
144 return hasAudio;
145 }
146
147 @Override
148 public boolean isAudioSupported(FileVersion fileVersion) {
149 return isSupported(fileVersion);
150 }
151
152 @Override
153 public boolean isAudioSupported(String mimeType) {
154 return isSupported(mimeType);
155 }
156
157 @Override
158 public boolean isSupported(String mimeType) {
159 if (Validator.isNull(mimeType)) {
160 return false;
161 }
162
163 try {
164 if (XugglerUtil.isEnabled()) {
165 return _audioMimeTypes.contains(mimeType);
166 }
167 }
168 catch (Exception e) {
169 }
170
171 return false;
172 }
173
174 @Override
175 public void trigger(
176 FileVersion sourceFileVersion, FileVersion destinationFileVersion) {
177
178 super.trigger(sourceFileVersion, destinationFileVersion);
179
180 _queueGeneration(sourceFileVersion, destinationFileVersion);
181 }
182
183 @Override
184 protected void doExportGeneratedFiles(
185 PortletDataContext portletDataContext, FileEntry fileEntry,
186 Element fileEntryElement)
187 throws Exception {
188
189 exportPreviews(portletDataContext, fileEntry, fileEntryElement);
190 }
191
192 @Override
193 protected void doImportGeneratedFiles(
194 PortletDataContext portletDataContext, FileEntry fileEntry,
195 FileEntry importedFileEntry, Element fileEntryElement)
196 throws Exception {
197
198 importPreviews(
199 portletDataContext, fileEntry, importedFileEntry, fileEntryElement);
200 }
201
202 protected void exportPreviews(
203 PortletDataContext portletDataContext, FileEntry fileEntry,
204 Element fileEntryElement)
205 throws Exception {
206
207 FileVersion fileVersion = fileEntry.getFileVersion();
208
209 if (!isSupported(fileVersion) || !hasPreviews(fileVersion)) {
210 return;
211 }
212
213 if (!portletDataContext.isPerformDirectBinaryImport()) {
214 if ((_PREVIEW_TYPES.length == 0) || (_PREVIEW_TYPES.length > 2)) {
215 return;
216 }
217
218 for (String previewType : _PREVIEW_TYPES) {
219 if (previewType.equals("mp3") || previewType.equals("ogg")) {
220 exportPreview(
221 portletDataContext, fileEntry, fileEntryElement,
222 "audio", previewType);
223 }
224 }
225 }
226 }
227
228 @Override
229 protected List<Long> getFileVersionIds() {
230 return _fileVersionIds;
231 }
232
233 @Override
234 protected String getPreviewType(FileVersion fileVersion) {
235 return _PREVIEW_TYPES[0];
236 }
237
238 @Override
239 protected String[] getPreviewTypes() {
240 return _PREVIEW_TYPES;
241 }
242
243 @Override
244 protected String getThumbnailType(FileVersion fileVersion) {
245 return null;
246 }
247
248 protected void importPreviews(
249 PortletDataContext portletDataContext, FileEntry fileEntry,
250 FileEntry importedFileEntry, Element fileEntryElement)
251 throws Exception {
252
253 if ((_PREVIEW_TYPES.length == 0) || (_PREVIEW_TYPES.length > 2)) {
254 return;
255 }
256
257 for (String previewType : _PREVIEW_TYPES) {
258 if (previewType.equals("mp3") || previewType.equals("ogg")) {
259 importPreview(
260 portletDataContext, fileEntry, importedFileEntry,
261 fileEntryElement, "audio", previewType);
262 }
263 }
264 }
265
266 private void _generateAudio(
267 FileVersion sourceFileVersion, FileVersion destinationFileVersion)
268 throws Exception {
269
270 String tempFileId = DLUtil.getTempFileId(
271 destinationFileVersion.getFileEntryId(),
272 destinationFileVersion.getVersion());
273
274 File[] previewTempFiles = new File[_PREVIEW_TYPES.length];
275
276 for (int i = 0; i < _PREVIEW_TYPES.length; i++) {
277 previewTempFiles[i] = getPreviewTempFile(
278 tempFileId, _PREVIEW_TYPES[i]);
279 }
280
281 File audioTempFile = null;
282
283 InputStream inputStream = null;
284
285 try {
286 if (sourceFileVersion != null) {
287 copy(sourceFileVersion, destinationFileVersion);
288
289 return;
290 }
291
292 if (!XugglerUtil.isEnabled() || _hasAudio(destinationFileVersion)) {
293 return;
294 }
295
296 audioTempFile = FileUtil.createTempFile(
297 destinationFileVersion.getExtension());
298
299 if (!hasPreviews(destinationFileVersion)) {
300 File file = null;
301
302 if (destinationFileVersion instanceof LiferayFileVersion) {
303 try {
304 LiferayFileVersion liferayFileVersion =
305 (LiferayFileVersion)destinationFileVersion;
306
307 file = liferayFileVersion.getFile(false);
308 }
309 catch (UnsupportedOperationException uoe) {
310 }
311 }
312
313 if (file == null) {
314 inputStream = destinationFileVersion.getContentStream(
315 false);
316
317 FileUtil.write(audioTempFile, inputStream);
318
319 file = audioTempFile;
320 }
321
322 try {
323 _generateAudioXuggler(
324 destinationFileVersion, file, previewTempFiles);
325 }
326 catch (Exception e) {
327 _log.error(e, e);
328 }
329 }
330 }
331 catch (NoSuchFileEntryException nsfee) {
332 }
333 finally {
334 StreamUtil.cleanUp(inputStream);
335
336 _fileVersionIds.remove(destinationFileVersion.getFileVersionId());
337
338 for (int i = 0; i < previewTempFiles.length; i++) {
339 FileUtil.delete(previewTempFiles[i]);
340 }
341
342 FileUtil.delete(audioTempFile);
343 }
344 }
345
346 private void _generateAudioXuggler(
347 FileVersion fileVersion, File srcFile, File destFile,
348 String containerType)
349 throws Exception {
350
351 if (hasPreview(fileVersion, containerType)) {
352 return;
353 }
354
355 StopWatch stopWatch = new StopWatch();
356
357 stopWatch.start();
358
359 try {
360 if (PropsValues.DL_FILE_ENTRY_PREVIEW_FORK_PROCESS_ENABLED) {
361 ProcessCallable<String> processCallable =
362 new LiferayAudioProcessCallable(
363 ServerDetector.getServerId(),
364 PropsUtil.get(PropsKeys.LIFERAY_HOME),
365 Log4JUtil.getCustomLogSettings(), srcFile, destFile,
366 containerType,
367 PropsUtil.getProperties(
368 PropsKeys.DL_FILE_ENTRY_PREVIEW_AUDIO, false));
369
370 ProcessChannel<String> processChannel =
371 ProcessExecutorUtil.execute(
372 ClassPathUtil.getPortalProcessConfig(),
373 processCallable);
374
375 Future<String> future =
376 processChannel.getProcessNoticeableFuture();
377
378 String processIdentity = String.valueOf(
379 fileVersion.getFileVersionId());
380
381 futures.put(processIdentity, future);
382
383 future.get();
384 }
385 else {
386 LiferayConverter liferayConverter = new LiferayAudioConverter(
387 srcFile.getCanonicalPath(), destFile.getCanonicalPath(),
388 containerType,
389 PropsUtil.getProperties(
390 PropsKeys.DL_FILE_ENTRY_PREVIEW_AUDIO, false));
391
392 liferayConverter.convert();
393 }
394 }
395 catch (CancellationException ce) {
396 if (_log.isInfoEnabled()) {
397 _log.info(
398 "Cancellation received for " +
399 fileVersion.getFileVersionId() + " " +
400 fileVersion.getTitle());
401 }
402 }
403 catch (Exception e) {
404 _log.error(e, e);
405 }
406
407 addFileToStore(
408 fileVersion.getCompanyId(), PREVIEW_PATH,
409 getPreviewFilePath(fileVersion, containerType), destFile);
410
411 if (_log.isInfoEnabled()) {
412 _log.info(
413 "Xuggler generated a " + containerType + " preview audio for " +
414 fileVersion.getFileVersionId() + " in " +
415 stopWatch.getTime() + "ms");
416 }
417 }
418
419 private void _generateAudioXuggler(
420 FileVersion fileVersion, File srcFile, File[] destFiles) {
421
422 try {
423 for (int i = 0; i < destFiles.length; i++) {
424 _generateAudioXuggler(
425 fileVersion, srcFile, destFiles[i], _PREVIEW_TYPES[i]);
426 }
427 }
428 catch (Exception e) {
429 _log.error(e, e);
430 }
431 }
432
433 private boolean _hasAudio(FileVersion fileVersion) throws Exception {
434 if (!isSupported(fileVersion)) {
435 return false;
436 }
437
438 return hasPreviews(fileVersion);
439 }
440
441 private void _queueGeneration(
442 FileVersion sourceFileVersion, FileVersion destinationFileVersion) {
443
444 if (_fileVersionIds.contains(
445 destinationFileVersion.getFileVersionId()) ||
446 !isSupported(destinationFileVersion)) {
447
448 return;
449 }
450
451 _fileVersionIds.add(destinationFileVersion.getFileVersionId());
452
453 sendGenerationMessage(
454 DestinationNames.DOCUMENT_LIBRARY_AUDIO_PROCESSOR,
455 sourceFileVersion, destinationFileVersion);
456 }
457
458 private static final String[] _PREVIEW_TYPES =
459 PropsValues.DL_FILE_ENTRY_PREVIEW_AUDIO_CONTAINERS;
460
461 private static Log _log = LogFactoryUtil.getLog(AudioProcessorImpl.class);
462
463 private Set<String> _audioMimeTypes = SetUtil.fromArray(
464 PropsValues.DL_FILE_ENTRY_PREVIEW_AUDIO_MIME_TYPES);
465 private List<Long> _fileVersionIds = new Vector<Long>();
466
467 private static class LiferayAudioProcessCallable
468 implements ProcessCallable<String> {
469
470 public LiferayAudioProcessCallable(
471 String serverId, String liferayHome,
472 Map<String, String> customLogSettings, File inputFile,
473 File outputFile, String audioContainer,
474 Properties audioProperties) {
475
476 _serverId = serverId;
477 _liferayHome = liferayHome;
478 _customLogSettings = customLogSettings;
479 _inputFile = inputFile;
480 _outputFile = outputFile;
481 _audioContainer = audioContainer;
482 _audioProperties = audioProperties;
483 }
484
485 @Override
486 public String call() throws ProcessException {
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 String _audioContainer;
517 private Properties _audioProperties;
518 private Map<String, String> _customLogSettings;
519
520 @InputResource
521 private File _inputFile;
522
523 private String _liferayHome;
524
525 @OutputResource
526 private File _outputFile;
527
528 private String _serverId;
529
530 }
531
532 }