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