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