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}