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:tdelprat@nuxeo.com">Tiry</a> 018 */ 019package org.nuxeo.ecm.platform.rendition.lazy; 020 021import java.util.ArrayList; 022import java.util.List; 023 024import org.apache.logging.log4j.LogManager; 025import org.apache.logging.log4j.Logger; 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:tdelprat@nuxeo.com">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 private static final Logger log = LogManager.getLogger(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.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 log.debug("Starting work: {} with id: {} for transient store key: {} and document: {}", 112 getClass().getSimpleName(), id, key, docRef); 113 openUserSession(); 114 DocumentModel doc = session.getDocument(docRef); 115 116 RenditionService rs = Framework.getService(RenditionService.class); 117 RenditionDefinition def = rs.getAvailableRenditionDefinition(doc, renditionName); 118 119 log.debug("Starting rendition computation."); 120 List<Blob> blobs = doComputeRendition(session, doc, def); 121 updateAndCompleteStoreEntry(getSourceDocumentModificationDate(doc), blobs); 122 } 123 124 @Override 125 public void cleanUp(boolean ok, Exception e) { 126 try { 127 if (!ok) { 128 storeAnErrorRendition(); 129 } 130 } finally { 131 super.cleanUp(ok, e); 132 } 133 } 134 135 protected void storeAnErrorRendition() { 136 if (session == null) { 137 log.info("No session, unable to clean work: {} with id: {} for transient store key: {} and document: {}", 138 getClass().getSimpleName(), id, key, docRef); 139 return; 140 } 141 DocumentModel doc = session.getDocument(docRef); 142 String sourceDocumentModificationDate = getSourceDocumentModificationDate(doc); 143 List<Blob> blobs = new ArrayList<>(); 144 StringBlob emptyBlob = new StringBlob(""); 145 emptyBlob.setFilename("error"); 146 emptyBlob.setMimeType("text/plain;" + LazyRendition.ERROR_MARKER); 147 blobs.add(emptyBlob); 148 updateAndCompleteStoreEntry(sourceDocumentModificationDate, blobs); 149 } 150 151 void updateAndCompleteStoreEntry(String sourceDocumentModificationDate, List<Blob> blobs) { 152 log.debug("Updating and completing transient store entry with key: {} (workId: {}, document: {})", key, id, 153 docRef); 154 TransientStoreService tss = Framework.getService(TransientStoreService.class); 155 TransientStore ts = tss.getStore(getTransientStoreName()); 156 157 log.debug("BEGIN - Putting blobs for transient store entry with key: {}", key); 158 ts.putBlobs(key, blobs); 159 log.debug("END - Putting blobs for transient store entry with key: {}", key); 160 161 if (sourceDocumentModificationDate != null) { 162 log.debug( 163 "Updating source document modification date parameter to: {} for transient store entry with key: {}", 164 sourceDocumentModificationDate, key); 165 ts.putParameter(key, AbstractLazyCachableRenditionProvider.SOURCE_DOCUMENT_MODIFICATION_DATE_KEY, 166 sourceDocumentModificationDate); 167 } 168 ts.setCompleted(key, true); 169 } 170 171 protected String getSourceDocumentModificationDate(DocumentModel doc) { 172 RenditionService rs = Framework.getService(RenditionService.class); 173 RenditionDefinition definition = rs.getAvailableRenditionDefinition(doc, renditionName); 174 RenditionProvider provider = definition.getProvider(); 175 if (provider instanceof AbstractLazyCachableRenditionProvider) { 176 return ((AbstractLazyCachableRenditionProvider) provider).getSourceDocumentModificationDate(doc, 177 definition); 178 } 179 return null; 180 } 181 182 /** 183 * Does the actual Rendition Computation: this code will be called from inside an Asynchronous Work 184 */ 185 protected abstract List<Blob> doComputeRendition(CoreSession session, DocumentModel doc, RenditionDefinition def); 186 187}