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