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 = 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(),
363 srcFile.getCanonicalPath(), destFile.getCanonicalPath(),
364 containerType,
365 PropsUtil.getProperties(
366 PropsKeys.DL_FILE_ENTRY_PREVIEW_AUDIO, false));
367
368 Future<String> future = ProcessExecutor.execute(
369 ClassPathUtil.getPortalClassPath(), processCallable);
370
371 String processIdentity = String.valueOf(
372 fileVersion.getFileVersionId());
373
374 futures.put(processIdentity, future);
375
376 future.get();
377 }
378 else {
379 LiferayConverter liferayConverter = new LiferayAudioConverter(
380 srcFile.getCanonicalPath(), destFile.getCanonicalPath(),
381 containerType,
382 PropsUtil.getProperties(
383 PropsKeys.DL_FILE_ENTRY_PREVIEW_AUDIO, false));
384
385 liferayConverter.convert();
386 }
387 }
388 catch (CancellationException ce) {
389 if (_log.isInfoEnabled()) {
390 _log.info(
391 "Cancellation received for " +
392 fileVersion.getFileVersionId() + " " +
393 fileVersion.getTitle());
394 }
395 }
396 catch (Exception e) {
397 _log.error(e, e);
398 }
399
400 addFileToStore(
401 fileVersion.getCompanyId(), PREVIEW_PATH,
402 getPreviewFilePath(fileVersion, containerType), destFile);
403
404 if (_log.isInfoEnabled()) {
405 _log.info(
406 "Xuggler generated a " + containerType + " preview audio for " +
407 fileVersion.getFileVersionId() + " in " +
408 stopWatch.getTime() + "ms");
409 }
410 }
411
412 private void _generateAudioXuggler(
413 FileVersion fileVersion, File srcFile, File[] destFiles) {
414
415 try {
416 for (int i = 0; i < destFiles.length; i++) {
417 _generateAudioXuggler(
418 fileVersion, srcFile, destFiles[i], _PREVIEW_TYPES[i]);
419 }
420 }
421 catch (Exception e) {
422 _log.error(e, e);
423 }
424 }
425
426 private boolean _hasAudio(FileVersion fileVersion) throws Exception {
427 if (!isSupported(fileVersion)) {
428 return false;
429 }
430
431 return hasPreviews(fileVersion);
432 }
433
434 private void _queueGeneration(
435 FileVersion sourceFileVersion, FileVersion destinationFileVersion) {
436
437 if (_fileVersionIds.contains(
438 destinationFileVersion.getFileVersionId()) ||
439 !isSupported(destinationFileVersion)) {
440
441 return;
442 }
443
444 _fileVersionIds.add(destinationFileVersion.getFileVersionId());
445
446 sendGenerationMessage(
447 DestinationNames.DOCUMENT_LIBRARY_AUDIO_PROCESSOR,
448 sourceFileVersion, destinationFileVersion);
449 }
450
451 private static final String[] _PREVIEW_TYPES =
452 PropsValues.DL_FILE_ENTRY_PREVIEW_AUDIO_CONTAINERS;
453
454 private static Log _log = LogFactoryUtil.getLog(AudioProcessorImpl.class);
455
456 private Set<String> _audioMimeTypes = SetUtil.fromArray(
457 PropsValues.DL_FILE_ENTRY_PREVIEW_AUDIO_MIME_TYPES);
458 private List<Long> _fileVersionIds = new Vector<Long>();
459
460 private static class LiferayAudioProcessCallable
461 implements ProcessCallable<String> {
462
463 public LiferayAudioProcessCallable(
464 String serverId, String liferayHome,
465 Map<String, String> customLogSettings, String inputURL,
466 String outputURL, String audioContainer,
467 Properties audioProperties) {
468
469 _serverId = serverId;
470 _liferayHome = liferayHome;
471 _customLogSettings = customLogSettings;
472 _inputURL = inputURL;
473 _outputURL = outputURL;
474 _audioContainer = audioContainer;
475 _audioProperties = audioProperties;
476 }
477
478 @Override
479 public String call() throws ProcessException {
480 Properties systemProperties = System.getProperties();
481
482 SystemEnv.setProperties(systemProperties);
483
484 Class<?> clazz = getClass();
485
486 ClassLoader classLoader = clazz.getClassLoader();
487
488 Log4JUtil.initLog4J(
489 _serverId, _liferayHome, classLoader, new Log4jLogFactoryImpl(),
490 _customLogSettings);
491
492 try {
493 LiferayConverter liferayConverter = new LiferayAudioConverter(
494 _inputURL, _outputURL, _audioContainer, _audioProperties);
495
496 liferayConverter.convert();
497 }
498 catch (Exception e) {
499 throw new ProcessException(e);
500 }
501
502 return StringPool.BLANK;
503 }
504
505 private static final long serialVersionUID = 1L;
506
507 private String _audioContainer;
508 private Properties _audioProperties;
509 private Map<String, String> _customLogSettings;
510 private String _inputURL;
511 private String _liferayHome;
512 private String _outputURL;
513 private String _serverId;
514
515 }
516
517 }