001/*
002 * (C) Copyright 2017 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 *     Guillaume Renard <grenard@nuxeo.com>
018 */
019package org.nuxeo.ecm.automation.core.work;
020
021import java.io.IOException;
022import java.util.ArrayList;
023import java.util.Collections;
024import java.util.List;
025
026import org.apache.commons.lang3.StringUtils;
027import org.apache.commons.logging.Log;
028import org.apache.commons.logging.LogFactory;
029import org.nuxeo.ecm.core.api.Blob;
030import org.nuxeo.ecm.core.api.DocumentModel;
031import org.nuxeo.ecm.core.api.DocumentRef;
032import org.nuxeo.ecm.core.api.IdRef;
033import org.nuxeo.ecm.core.api.NuxeoException;
034import org.nuxeo.ecm.core.api.impl.blob.AsyncBlob;
035import org.nuxeo.ecm.core.io.download.DownloadService;
036import org.nuxeo.ecm.core.transientstore.api.TransientStore;
037import org.nuxeo.ecm.core.transientstore.api.TransientStoreService;
038import org.nuxeo.ecm.core.transientstore.work.TransientStoreWork;
039import org.nuxeo.ecm.core.utils.BlobUtils;
040import org.nuxeo.runtime.api.Framework;
041
042/**
043 * Work to zip a list of document default blob and store the produced zip into the TransientStore.
044 *
045 * @since 9.3
046 */
047public class BlobListZipWork extends TransientStoreWork {
048
049    private static final Log log = LogFactory.getLog(BlobListZipWork.class);
050
051    public static final String CATEGORY = "blobListZip";
052
053    public static final String CACHE_NAME = "blobListZip";
054
055    private static final long serialVersionUID = 1L;
056
057    protected String filename;
058
059    protected final String key;
060
061    protected final String storeName;
062
063    public BlobListZipWork(String transientStoreKey, String originatingUsername, String filename, List<String> docIds) {
064        this(transientStoreKey, originatingUsername, filename, docIds, filename);
065    }
066
067    public BlobListZipWork(String transientStoreKey, String originatingUsername, String filename, List<String> docIds,
068            String storeName) {
069        this.key = transientStoreKey;
070        this.docIds = docIds;
071        this.originatingUsername = originatingUsername;
072        this.id = "BlobListZipWork-" + this.key + "-" + this.originatingUsername;
073        this.storeName = storeName;
074        if (StringUtils.isNotBlank(filename)) {
075            this.filename = filename.toLowerCase().endsWith(".zip") ? filename : filename + ".zip";
076        }
077
078    }
079
080    @Override
081    public void cleanUp(boolean ok, Exception e) {
082        super.cleanUp(ok, e);
083        if (ok) {
084            return;
085        }
086        List<Blob> blobs = Collections.singletonList(new AsyncBlob(key));
087        TransientStore ts = getTransientStore();
088        ts.putParameter(key, DownloadService.TRANSIENT_STORE_PARAM_ERROR, e.getMessage());
089        updateAndCompleteStoreEntry(blobs);
090    }
091
092    @Override
093    public String getCategory() {
094        return CATEGORY;
095    }
096
097    @Override
098    public String getTitle() {
099        return id;
100    }
101
102    public TransientStore getTransientStore() {
103        TransientStoreService tss = Framework.getService(TransientStoreService.class);
104        return tss.getStore(storeName != null ? storeName : CACHE_NAME);
105    }
106
107    protected void updateAndCompleteStoreEntry(List<Blob> blobs) {
108        TransientStore ts = getTransientStore();
109        if (!ts.exists(key)) {
110            throw new NuxeoException("Zip TransientStore entry can not be null");
111        }
112        ts.putBlobs(key, blobs);
113        ts.putParameter(key, DownloadService.TRANSIENT_STORE_PARAM_PROGRESS, 100);
114        ts.setCompleted(key, true);
115    }
116
117    @Override
118    public void work() {
119        openUserSession();
120        List<Blob> blobList = new ArrayList<>();
121        DownloadService downloadService = Framework.getService(DownloadService.class);
122        for (String docId : docIds) {
123            DocumentRef docRef = new IdRef(docId);
124            if (!session.exists(docRef)) {
125                if (log.isDebugEnabled()) {
126                    log.debug(String.format("Cannot retrieve document '%s', probably deleted in the meanwhile", docId));
127                }
128                continue;
129            }
130            DocumentModel doc = session.getDocument(docRef);
131            Blob blob = downloadService.resolveBlob(doc);
132            if (blob == null) {
133                log.trace("Not able to resolve blob");
134                continue;
135            } else if (!downloadService.checkPermission(doc, null, blob, "download", Collections.emptyMap())) {
136                if (log.isDebugEnabled()) {
137                    log.debug(
138                            String.format("Not allowed to bulk download blob for document %s", doc.getPathAsString()));
139                }
140                continue;
141            }
142            blobList.add(blob);
143        }
144        if (blobList.isEmpty()) {
145            log.debug("No blob to be zipped");
146            updateAndCompleteStoreEntry(Collections.emptyList());
147            return;
148        }
149        Blob blob;
150        String finalFilename = StringUtils.isNotBlank(this.filename) ? this.filename : this.id;
151        try {
152            blob = BlobUtils.zip(blobList, finalFilename);
153        } catch (IOException e) {
154            TransientStore ts = getTransientStore();
155            ts.putParameter(key, DownloadService.TRANSIENT_STORE_PARAM_ERROR, e.getMessage());
156            throw new NuxeoException("Exception while zipping blob list", e);
157        }
158        updateAndCompleteStoreEntry(Collections.singletonList(blob));
159    }
160
161}