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