001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
013     */
014    
015    package com.liferay.portlet.documentlibrary.util;
016    
017    import com.liferay.portal.kernel.exception.PortalException;
018    import com.liferay.portal.kernel.exception.SystemException;
019    import com.liferay.portal.kernel.image.ImageBag;
020    import com.liferay.portal.kernel.image.ImageToolUtil;
021    import com.liferay.portal.kernel.lar.PortletDataContext;
022    import com.liferay.portal.kernel.log.Log;
023    import com.liferay.portal.kernel.log.LogFactoryUtil;
024    import com.liferay.portal.kernel.messaging.DestinationNames;
025    import com.liferay.portal.kernel.messaging.MessageBusException;
026    import com.liferay.portal.kernel.messaging.MessageBusUtil;
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.SetUtil;
032    import com.liferay.portal.kernel.util.StreamUtil;
033    import com.liferay.portal.kernel.util.StringBundler;
034    import com.liferay.portal.kernel.util.StringPool;
035    import com.liferay.portal.kernel.util.Validator;
036    import com.liferay.portal.kernel.xml.Element;
037    import com.liferay.portal.util.PropsValues;
038    import com.liferay.portlet.documentlibrary.NoSuchFileEntryException;
039    import com.liferay.portlet.documentlibrary.store.DLStoreUtil;
040    
041    import java.awt.image.RenderedImage;
042    
043    import java.io.File;
044    import java.io.FileOutputStream;
045    import java.io.InputStream;
046    
047    import java.util.List;
048    import java.util.Set;
049    import java.util.Vector;
050    
051    /**
052     * @author Sergio González
053     * @author Alexander Chow
054     */
055    public class ImageProcessorImpl
056            extends DLPreviewableProcessor implements ImageProcessor {
057    
058            public static ImageProcessorImpl getInstance() {
059                    return _instance;
060            }
061    
062            @Override
063            public void cleanUp(FileEntry fileEntry) {
064                    deleteFiles(fileEntry, null);
065            }
066    
067            @Override
068            public void cleanUp(FileVersion fileVersion) {
069                    String type = _instance.getThumbnailType(fileVersion);
070    
071                    deleteFiles(fileVersion, type);
072            }
073    
074            public void generateImages(FileVersion fileVersion) {
075                    _instance._generateImages(fileVersion);
076            }
077    
078            public Set<String> getImageMimeTypes() {
079                    return _instance._imageMimeTypes;
080            }
081    
082            public InputStream getPreviewAsStream(FileVersion fileVersion)
083                    throws Exception {
084    
085                    if (_previewGenerationRequired(fileVersion)) {
086                            String type = getPreviewType(fileVersion);
087    
088                            return _instance.doGetPreviewAsStream(fileVersion, type);
089                    }
090    
091                    return fileVersion.getContentStream(false);
092            }
093    
094            public long getPreviewFileSize(FileVersion fileVersion) throws Exception {
095                    if (_previewGenerationRequired(fileVersion)) {
096                            String type = getPreviewType(fileVersion);
097    
098                            return _instance.doGetPreviewFileSize(fileVersion, type);
099                    }
100    
101                    return fileVersion.getSize();
102            }
103    
104            @Override
105            public String getPreviewType(FileVersion fileVersion) {
106                    return _getType(fileVersion);
107            }
108    
109            public InputStream getThumbnailAsStream(FileVersion fileVersion, int index)
110                    throws Exception {
111    
112                    return _instance.doGetThumbnailAsStream(fileVersion, index);
113            }
114    
115            public long getThumbnailFileSize(FileVersion fileVersion, int index)
116                    throws Exception {
117    
118                    return _instance.doGetThumbnailFileSize(fileVersion, index);
119            }
120    
121            @Override
122            public String getThumbnailType(FileVersion fileVersion) {
123                    return _getType(fileVersion);
124            }
125    
126            public boolean hasImages(FileVersion fileVersion) {
127                    if (!PropsValues.DL_FILE_ENTRY_PREVIEW_ENABLED &&
128                            !PropsValues.DL_FILE_ENTRY_THUMBNAIL_ENABLED) {
129    
130                            return false;
131                    }
132    
133                    boolean hasImages = false;
134    
135                    try {
136                            if (_instance._hasPreview(fileVersion) &&
137                                    hasThumbnails(fileVersion)) {
138    
139                                    hasImages = true;
140                            }
141    
142                            if (!hasImages && _instance.isSupported(fileVersion)) {
143                                    _instance._queueGeneration(fileVersion);
144                            }
145                    }
146                    catch (Exception e) {
147                            _log.error(e, e);
148                    }
149    
150                    return hasImages;
151            }
152    
153            public boolean isImageSupported(FileVersion fileVersion) {
154                    return _instance.isSupported(fileVersion);
155            }
156    
157            public boolean isImageSupported(String mimeType) {
158                    return _instance.isSupported(mimeType);
159            }
160    
161            public boolean isSupported(String mimeType) {
162                    if (Validator.isNull(mimeType)) {
163                            return false;
164                    }
165    
166                    return _imageMimeTypes.contains(mimeType);
167            }
168    
169            public void storeThumbnail(
170                            long companyId, long groupId, long fileEntryId, long fileVersionId,
171                            long custom1ImageId, long custom2ImageId, InputStream is,
172                            String type)
173                    throws Exception {
174    
175                    _instance._storeThumbnail(
176                            companyId, groupId, fileEntryId, fileVersionId, custom1ImageId,
177                            custom2ImageId, is, type);
178            }
179    
180            public void trigger(FileVersion fileVersion) {
181                    _instance._queueGeneration(fileVersion);
182            }
183    
184            @Override
185            protected void doExportGeneratedFiles(
186                            PortletDataContext portletDataContext, FileEntry fileEntry,
187                            Element fileEntryElement)
188                    throws Exception {
189    
190                    exportThumbnails(
191                            portletDataContext, fileEntry, fileEntryElement, "image");
192    
193                    exportPreview(portletDataContext, fileEntry, fileEntryElement);
194            }
195    
196            @Override
197            protected void doImportGeneratedFiles(
198                            PortletDataContext portletDataContext, FileEntry fileEntry,
199                            FileEntry importedFileEntry, Element fileEntryElement)
200                    throws Exception {
201    
202                    importThumbnails(
203                            portletDataContext, fileEntry, importedFileEntry, fileEntryElement,
204                            "image");
205    
206                    FileVersion importedFileVersion = importedFileEntry.getFileVersion();
207    
208                    if (!_previewGenerationRequired(importedFileVersion)) {
209                            return;
210                    }
211    
212                    importPreview(
213                            portletDataContext, fileEntry, importedFileEntry, fileEntryElement,
214                            "image", getPreviewType(importedFileVersion));
215            }
216    
217            protected void exportPreview(
218                            PortletDataContext portletDataContext, FileEntry fileEntry,
219                            Element fileEntryElement)
220                    throws Exception {
221    
222                    FileVersion fileVersion = fileEntry.getFileVersion();
223    
224                    if (!isSupported(fileVersion) ||
225                            !_previewGenerationRequired(fileVersion) ||
226                            !_hasPreview(fileVersion)) {
227    
228                            return;
229                    }
230    
231                    exportPreview(
232                            portletDataContext, fileEntry, fileEntryElement, "image",
233                            getPreviewType(fileVersion));
234            }
235    
236            private ImageProcessorImpl() {
237            }
238    
239            private void _generateImages(FileVersion fileVersion) {
240                    InputStream inputStream = null;
241    
242                    try {
243                            if (!PropsValues.DL_FILE_ENTRY_THUMBNAIL_ENABLED &&
244                                    !PropsValues.DL_FILE_ENTRY_PREVIEW_ENABLED) {
245    
246                                    return;
247                            }
248    
249                            inputStream = fileVersion.getContentStream(false);
250    
251                            byte[] bytes = FileUtil.getBytes(inputStream);
252    
253                            ImageBag imageBag = ImageToolUtil.read(bytes);
254    
255                            RenderedImage renderedImage = imageBag.getRenderedImage();
256    
257                            if (renderedImage == null) {
258                                    return;
259                            }
260    
261                            if (!_hasPreview(fileVersion)) {
262                                    _storePreviewImage(fileVersion, renderedImage);
263                            }
264    
265                            if (!hasThumbnails(fileVersion)) {
266                                    storeThumbnailImages(fileVersion, renderedImage);
267                            }
268                    }
269                    catch (NoSuchFileEntryException nsfee) {
270                    }
271                    catch (Exception e) {
272                            _log.error(e, e);
273                    }
274                    finally {
275                            StreamUtil.cleanUp(inputStream);
276    
277                            _fileVersionIds.remove(fileVersion.getFileVersionId());
278                    }
279            }
280    
281            private String _getType(FileVersion fileVersion) {
282                    String type = "png";
283    
284                    if (fileVersion == null) {
285                            return type;
286                    }
287    
288                    String extension = fileVersion.getExtension();
289    
290                    if (extension.equals("jpeg")) {
291                            type = "jpg";
292                    }
293                    else if (!_previewGenerationRequired(fileVersion)) {
294                            type = extension;
295                    }
296    
297                    return type;
298            }
299    
300            private boolean _hasPreview(FileVersion fileVersion)
301                    throws PortalException, SystemException {
302    
303                    if (PropsValues.DL_FILE_ENTRY_PREVIEW_ENABLED &&
304                            _previewGenerationRequired(fileVersion)) {
305    
306                            String type = getPreviewType(fileVersion);
307    
308                            String previewFilePath = getPreviewFilePath(fileVersion, type);
309    
310                            if (!DLStoreUtil.hasFile(
311                                            fileVersion.getCompanyId(), REPOSITORY_ID,
312                                            previewFilePath)) {
313    
314                                    return false;
315                            }
316                    }
317    
318                    return true;
319            }
320    
321            private boolean _previewGenerationRequired(FileVersion fileVersion) {
322                    String type = fileVersion.getExtension();
323    
324                    if (type.equals("tiff") || type.equals("tif")) {
325                            return true;
326                    }
327                    else {
328                            return false;
329                    }
330            }
331    
332            private void _queueGeneration(FileVersion fileVersion) {
333                    if (!_fileVersionIds.contains(fileVersion.getFileVersionId()) &&
334                            isSupported(fileVersion) && !hasThumbnails(fileVersion)) {
335                            _fileVersionIds.add(fileVersion.getFileVersionId());
336    
337                            if (PropsValues.DL_FILE_ENTRY_PROCESSORS_TRIGGER_SYNCHRONOUSLY) {
338                                    try {
339                                            MessageBusUtil.sendSynchronousMessage(
340                                                    DestinationNames.DOCUMENT_LIBRARY_IMAGE_PROCESSOR,
341                                                    fileVersion);
342                                    }
343                                    catch (MessageBusException mbe) {
344                                            if (_log.isWarnEnabled()) {
345                                                    _log.warn(mbe, mbe);
346                                            }
347                                    }
348                            }
349                            else {
350                                    MessageBusUtil.sendMessage(
351                                            DestinationNames.DOCUMENT_LIBRARY_IMAGE_PROCESSOR,
352                                            fileVersion);
353                            }
354                    }
355            }
356    
357            private void _storePreviewImage(
358                            FileVersion fileVersion, RenderedImage renderedImage)
359                    throws Exception {
360    
361                    String type = getPreviewType(fileVersion);
362    
363                    File file = null;
364    
365                    try {
366                            file = FileUtil.createTempFile(type);
367    
368                            FileOutputStream fos = new FileOutputStream(file);
369    
370                            try {
371                                    ImageToolUtil.write(renderedImage, type, fos);
372                            }
373                            finally {
374                                    fos.close();
375                            }
376    
377                            addFileToStore(
378                                    fileVersion.getCompanyId(), PREVIEW_PATH,
379                                    getPreviewFilePath(fileVersion, type), file);
380                    }
381                    finally {
382                            FileUtil.delete(file);
383                    }
384            }
385    
386            private void _storeThumbnail(
387                            long companyId, long groupId, long fileEntryId, long fileVersionId,
388                            long custom1ImageId, long custom2ImageId, InputStream is,
389                            String type)
390                    throws Exception {
391    
392                    StringBundler sb = new StringBundler(5);
393    
394                    sb.append(getPathSegment(groupId, fileEntryId, fileVersionId, false));
395    
396                    if (custom1ImageId != 0) {
397                            sb.append(StringPool.DASH);
398                            sb.append(1);
399                    }
400                    else if (custom2ImageId != 0) {
401                            sb.append(StringPool.DASH);
402                            sb.append(2);
403                    }
404    
405                    sb.append(StringPool.PERIOD);
406                    sb.append(type);
407    
408                    String filePath = sb.toString();
409    
410                    File file = null;
411    
412                    try {
413                            file = FileUtil.createTempFile(is);
414    
415                            addFileToStore(companyId, THUMBNAIL_PATH, filePath, file);
416                    }
417                    finally {
418                            FileUtil.delete(file);
419                    }
420            }
421    
422            private static Log _log = LogFactoryUtil.getLog(ImageProcessorImpl.class);
423    
424            private static ImageProcessorImpl _instance = new ImageProcessorImpl();
425    
426            static {
427                    InstancePool.put(ImageProcessorImpl.class.getName(), _instance);
428            }
429    
430            private List<Long> _fileVersionIds = new Vector<Long>();
431            private Set<String> _imageMimeTypes = SetUtil.fromArray(
432                    PropsValues.DL_FILE_ENTRY_PREVIEW_IMAGE_MIME_TYPES);
433    
434    }