001/*
002 * (C) Copyright 2019 Nuxeo (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Nour Al Kotob
018 */
019
020package org.nuxeo.ecm.platform.thumbnail.action;
021
022import static org.nuxeo.ecm.core.api.CoreSession.ALLOW_VERSION_WRITE;
023import static org.nuxeo.ecm.core.bulk.BulkServiceImpl.STATUS_STREAM;
024import static org.nuxeo.lib.stream.computation.AbstractComputation.INPUT_1;
025import static org.nuxeo.lib.stream.computation.AbstractComputation.OUTPUT_1;
026
027import java.io.IOException;
028import java.io.InputStream;
029import java.io.Serializable;
030import java.util.Arrays;
031import java.util.List;
032import java.util.Map;
033
034import org.nuxeo.ecm.core.api.Blob;
035import org.nuxeo.ecm.core.api.Blobs;
036import org.nuxeo.ecm.core.api.CoreSession;
037import org.nuxeo.ecm.core.api.DocumentModel;
038import org.nuxeo.ecm.core.api.DocumentModelList;
039import org.nuxeo.ecm.core.api.NuxeoException;
040import org.nuxeo.ecm.core.api.VersioningOption;
041import org.nuxeo.ecm.core.api.blobholder.BlobHolder;
042import org.nuxeo.ecm.core.api.thumbnail.ThumbnailAdapter;
043import org.nuxeo.ecm.core.api.versioning.VersioningService;
044import org.nuxeo.ecm.core.blob.BlobManager;
045import org.nuxeo.ecm.core.bulk.action.computation.AbstractBulkComputation;
046import org.nuxeo.ecm.platform.dublincore.listener.DublinCoreListener;
047import org.nuxeo.ecm.platform.ec.notification.NotificationConstants;
048import org.nuxeo.ecm.platform.thumbnail.ThumbnailConstants;
049import org.nuxeo.lib.stream.computation.Topology;
050import org.nuxeo.runtime.api.Framework;
051import org.nuxeo.runtime.stream.StreamProcessorTopology;
052
053/**
054 * Bulk Action processor that generates thumbnails from documents.
055 *
056 * @since 11.1
057 */
058public class RecomputeThumbnailsAction implements StreamProcessorTopology {
059
060    public static final String THUMBNAIL_UPDATED = "thumbnailUpdated";
061
062    public static final String ACTION_NAME = "recomputeThumbnails";
063
064    // @since 11.1
065    public static final String ACTION_FULL_NAME = "bulk/" + ACTION_NAME;
066
067    @Override
068    public Topology getTopology(Map<String, String> options) {
069        return Topology.builder()
070                       .addComputation(RecomputeThumbnailsComputation::new, //
071                               Arrays.asList(INPUT_1 + ":" + ACTION_FULL_NAME, OUTPUT_1 + ":" + STATUS_STREAM))
072                       .build();
073    }
074
075    public static class RecomputeThumbnailsComputation extends AbstractBulkComputation {
076
077        public RecomputeThumbnailsComputation() {
078            super(ACTION_FULL_NAME);
079        }
080
081        @Override
082        protected void compute(CoreSession session, List<String> ids, Map<String, Serializable> properties) {
083            DocumentModelList docs = loadDocuments(session, ids);
084
085            for (DocumentModel doc : docs) {
086                processDoc(session, doc);
087            }
088        }
089
090        protected void processDoc(CoreSession session, DocumentModel doc) {
091            Blob thumbnailBlob = getManagedThumbnail(doc);
092            if (thumbnailBlob == null) {
093                ThumbnailAdapter thumbnailAdapter = doc.getAdapter(ThumbnailAdapter.class);
094                if (thumbnailAdapter == null) {
095                    return;
096                }
097                thumbnailBlob = thumbnailAdapter.computeThumbnail(session);
098            }
099            if (thumbnailBlob != null) {
100                if (!doc.hasFacet(ThumbnailConstants.THUMBNAIL_FACET)) {
101                    doc.addFacet(ThumbnailConstants.THUMBNAIL_FACET);
102                }
103                doc.setPropertyValue(ThumbnailConstants.THUMBNAIL_PROPERTY_NAME, (Serializable) thumbnailBlob);
104            } else {
105                if (doc.hasFacet(ThumbnailConstants.THUMBNAIL_FACET)) {
106                    doc.setPropertyValue(ThumbnailConstants.THUMBNAIL_PROPERTY_NAME, null);
107                    doc.removeFacet(ThumbnailConstants.THUMBNAIL_FACET);
108                }
109            }
110            if (doc.isDirty()) {
111                doc.putContextData(VersioningService.VERSIONING_OPTION, VersioningOption.NONE);
112                doc.putContextData(VersioningService.DISABLE_AUTO_CHECKOUT, Boolean.TRUE);
113                doc.putContextData(DublinCoreListener.DISABLE_DUBLINCORE_LISTENER, Boolean.TRUE);
114                doc.putContextData(NotificationConstants.DISABLE_NOTIFICATION_SERVICE, Boolean.TRUE);
115                doc.putContextData("disableAuditLogger", Boolean.TRUE);
116                if (doc.isVersion()) {
117                    doc.putContextData(ALLOW_VERSION_WRITE, Boolean.TRUE);
118                }
119                doc.putContextData(THUMBNAIL_UPDATED, true);
120                session.saveDocument(doc);
121            }
122        }
123
124        protected Blob getManagedThumbnail(DocumentModel doc) {
125            BlobHolder bh = doc.getAdapter(BlobHolder.class);
126            if (bh == null) {
127                return null;
128            }
129            Blob blob = bh.getBlob();
130            if (blob == null) {
131                return null;
132            }
133            BlobManager blobManager = Framework.getService(BlobManager.class);
134            try (InputStream is = blobManager.getThumbnail(blob)) {
135                if (is == null) {
136                    return null;
137                }
138                return Blobs.createBlob(is);
139            } catch (IOException e) {
140                throw new NuxeoException("Failed to get managed blob thumbnail", e);
141            }
142        }
143    }
144}