001/* 002 * (C) Copyright 2015 Nuxeo SA (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 * <a href="mailto:[email protected]">Tiry</a> 018 */ 019package org.nuxeo.ecm.platform.rendition.lazy; 020 021import java.util.ArrayList; 022import java.util.List; 023 024import org.apache.commons.logging.Log; 025import org.apache.commons.logging.LogFactory; 026import org.nuxeo.ecm.core.api.Blob; 027import org.nuxeo.ecm.core.api.CoreSession; 028import org.nuxeo.ecm.core.api.DocumentModel; 029import org.nuxeo.ecm.core.api.DocumentRef; 030import org.nuxeo.ecm.core.api.impl.blob.StringBlob; 031import org.nuxeo.ecm.core.transientstore.api.TransientStore; 032import org.nuxeo.ecm.core.transientstore.api.TransientStoreService; 033import org.nuxeo.ecm.core.transientstore.work.TransientStoreWork; 034import org.nuxeo.ecm.platform.rendition.extension.RenditionProvider; 035import org.nuxeo.ecm.platform.rendition.impl.LazyRendition; 036import org.nuxeo.ecm.platform.rendition.service.RenditionDefinition; 037import org.nuxeo.ecm.platform.rendition.service.RenditionService; 038import org.nuxeo.runtime.api.Framework; 039 040/** 041 * @author <a href="mailto:[email protected]">Tiry</a> 042 * @since 7.2 043 */ 044public abstract class AbstractRenditionBuilderWork extends TransientStoreWork { 045 046 private static final long serialVersionUID = 1L; 047 048 protected final String key; 049 050 protected final DocumentRef docRef; 051 052 protected final String renditionName; 053 054 protected static Log log = LogFactory.getLog(AbstractRenditionBuilderWork.class); 055 056 public static final String CATEGORY = "renditionBuilder"; 057 058 public AbstractRenditionBuilderWork(String key, DocumentModel doc, RenditionDefinition def) { 059 super(); 060 this.key = key; 061 docRef = doc.getRef(); 062 repositoryName = doc.getRepositoryName(); 063 renditionName = def.getName(); 064 setOriginatingUsername(doc.getCoreSession().getPrincipal().getName()); 065 this.id = buildId(doc, def); 066 } 067 068 protected String buildId(DocumentModel doc, RenditionDefinition def) { 069 StringBuilder sb = new StringBuilder("rendition:"); 070 sb.append(doc.getId()); 071 String variant = def.getProvider().getVariant(doc, def); 072 if (variant != null) { 073 sb.append("::"); 074 sb.append(variant); 075 } 076 sb.append("::"); 077 sb.append(def.getName()); 078 return sb.toString(); 079 } 080 081 @Override 082 public String getTitle() { 083 return "Lazy Rendition for " + renditionName + " on " + docRef.toString() + " on behalf of " 084 + originatingUsername; 085 } 086 087 @Override 088 public String getCategory() { 089 return CATEGORY; 090 } 091 092 protected String getTransientStoreName() { 093 return AbstractLazyCachableRenditionProvider.CACHE_NAME; 094 } 095 096 @Override 097 public boolean isIdempotent() { 098 // The same rendering can be executed multiple times because the result is transient. 099 return false; 100 } 101 102 @Override 103 public boolean isCoalescing() { 104 // The same rendering has no reason to be executed more than once if schedulled multiple times 105 // The last schedulled work will build the wanted rendition 106 return true; 107 } 108 109 @Override 110 public void work() { 111 if (log.isDebugEnabled()) { 112 log.debug(String.format("Starting %s work with id %s for transient store key %s and document %s.", 113 getClass().getSimpleName(), id, key, docRef)); 114 } 115 openUserSession(); 116 DocumentModel doc = session.getDocument(docRef); 117 118 RenditionService rs = Framework.getService(RenditionService.class); 119 RenditionDefinition def = rs.getAvailableRenditionDefinition(doc, renditionName); 120 121 log.debug("Starting rendition computation."); 122 List<Blob> blobs = doComputeRendition(session, doc, def); 123 updateAndCompleteStoreEntry(getSourceDocumentModificationDate(doc), blobs); 124 } 125 126 @Override 127 public void cleanUp(boolean ok, Exception e) { 128 if (ok) { 129 super.cleanUp(ok, e); 130 return; 131 } 132 133 // Fetch document and compute its modification date before cleaning up which closes the session 134 DocumentModel doc = session.getDocument(docRef); 135 String sourceDocumentModificationDate = getSourceDocumentModificationDate(doc); 136 137 super.cleanUp(ok, e); 138 139 List<Blob> blobs = new ArrayList<>(); 140 StringBlob emptyBlob = new StringBlob(""); 141 emptyBlob.setFilename("error"); 142 emptyBlob.setMimeType("text/plain;" + LazyRendition.ERROR_MARKER); 143 blobs.add(emptyBlob); 144 updateAndCompleteStoreEntry(sourceDocumentModificationDate, blobs); 145 } 146 147 void updateAndCompleteStoreEntry(String sourceDocumentModificationDate, List<Blob> blobs) { 148 if (log.isDebugEnabled()) { 149 log.debug( 150 String.format("Updating and completing transient store entry with key %s (workId=%s, document=%s).", 151 key, id, docRef)); 152 } 153 TransientStoreService tss = Framework.getService(TransientStoreService.class); 154 TransientStore ts = tss.getStore(getTransientStoreName()); 155 156 if (sourceDocumentModificationDate != null) { 157 ts.putParameter(key, AbstractLazyCachableRenditionProvider.SOURCE_DOCUMENT_MODIFICATION_DATE_KEY, 158 sourceDocumentModificationDate); 159 } 160 ts.putBlobs(key, blobs); 161 ts.setCompleted(key, true); 162 } 163 164 protected String getSourceDocumentModificationDate(DocumentModel doc) { 165 RenditionService rs = Framework.getService(RenditionService.class); 166 RenditionDefinition definition = rs.getAvailableRenditionDefinition(doc, renditionName); 167 RenditionProvider provider = definition.getProvider(); 168 if (provider instanceof AbstractLazyCachableRenditionProvider) { 169 return ((AbstractLazyCachableRenditionProvider) provider).getSourceDocumentModificationDate(doc, 170 definition); 171 } 172 return null; 173 } 174 175 /** 176 * Does the actual Rendition Computation : this code will be called from inside an Asynchronous Work 177 * 178 * @param session 179 * @param doc 180 * @param def 181 * @return 182 */ 183 protected abstract List<Blob> doComputeRendition(CoreSession session, DocumentModel doc, RenditionDefinition def); 184 185}