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 List<String> docIds; 058 059 protected String filename; 060 061 protected final String key; 062 063 protected final String storeName; 064 065 public BlobListZipWork(String transientStoreKey, String originatingUsername, String filename, List<String> docIds) { 066 this(transientStoreKey, originatingUsername, filename, docIds, filename); 067 } 068 069 public BlobListZipWork(String transientStoreKey, String originatingUsername, String filename, List<String> docIds, 070 String storeName) { 071 this.key = transientStoreKey; 072 this.docIds = docIds; 073 this.originatingUsername = originatingUsername; 074 this.id = "BlobListZipWork-" + this.key + "-" + this.originatingUsername; 075 this.storeName = storeName; 076 if (StringUtils.isNotBlank(filename)) { 077 this.filename = filename.toLowerCase().endsWith(".zip") ? filename : filename + ".zip"; 078 } 079 080 } 081 082 @Override 083 public void cleanUp(boolean ok, Exception e) { 084 super.cleanUp(ok, e); 085 if (ok) { 086 return; 087 } 088 List<Blob> blobs = Collections.singletonList(new AsyncBlob(key)); 089 TransientStore ts = getTransientStore(); 090 ts.putParameter(key, DownloadService.TRANSIENT_STORE_PARAM_ERROR, e.getMessage()); 091 updateAndCompleteStoreEntry(blobs); 092 } 093 094 @Override 095 public String getCategory() { 096 return CATEGORY; 097 } 098 099 @Override 100 public String getTitle() { 101 return id; 102 } 103 104 public TransientStore getTransientStore() { 105 TransientStoreService tss = Framework.getService(TransientStoreService.class); 106 return tss.getStore(storeName != null ? storeName : CACHE_NAME); 107 } 108 109 protected void updateAndCompleteStoreEntry(List<Blob> blobs) { 110 TransientStore ts = getTransientStore(); 111 if (!ts.exists(key)) { 112 throw new NuxeoException("Zip TransientStore entry can not be null"); 113 } 114 ts.putBlobs(key, blobs); 115 ts.putParameter(key, DownloadService.TRANSIENT_STORE_PARAM_PROGRESS, 100); 116 ts.setCompleted(key, true); 117 } 118 119 @Override 120 public void work() { 121 openUserSession(); 122 List<Blob> blobList = new ArrayList<>(); 123 DownloadService downloadService = Framework.getService(DownloadService.class); 124 for (String docId : docIds) { 125 DocumentRef docRef = new IdRef(docId); 126 if (!session.exists(docRef)) { 127 if (log.isDebugEnabled()) { 128 log.debug(String.format("Cannot retrieve document '%s', probably deleted in the meanwhile", docId)); 129 } 130 continue; 131 } 132 DocumentModel doc = session.getDocument(docRef); 133 Blob blob = downloadService.resolveBlob(doc); 134 if (blob == null) { 135 log.trace("Not able to resolve blob"); 136 continue; 137 } else if (!downloadService.checkPermission(doc, null, blob, "download", Collections.emptyMap())) { 138 if (log.isDebugEnabled()) { 139 log.debug( 140 String.format("Not allowed to bulk download blob for document %s", doc.getPathAsString())); 141 } 142 continue; 143 } 144 blobList.add(blob); 145 } 146 if (blobList.isEmpty()) { 147 log.debug("No blob to be zipped"); 148 updateAndCompleteStoreEntry(Collections.emptyList()); 149 return; 150 } 151 Blob blob; 152 String finalFilename = StringUtils.isNotBlank(this.filename) ? this.filename : this.id; 153 if (blobList.size() == 1) { 154 blob = blobList.get(0); 155 blob.setFilename(finalFilename); 156 } else { 157 try { 158 blob = BlobUtils.zip(blobList, finalFilename); 159 } catch (IOException e) { 160 TransientStore ts = getTransientStore(); 161 ts.putParameter(key, DownloadService.TRANSIENT_STORE_PARAM_ERROR, e.getMessage()); 162 throw new NuxeoException("Exception while zipping blob list", e); 163 } 164 } 165 updateAndCompleteStoreEntry(Collections.singletonList(blob)); 166 } 167 168}