001/* 002 * (C) Copyright 2010 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * Contributors: 014 * Nuxeo - initial API and implementation 015 */ 016 017package org.nuxeo.ecm.platform.rendition.service; 018 019import java.util.ArrayList; 020import java.util.HashMap; 021import java.util.List; 022import java.util.Map; 023 024import org.apache.commons.logging.Log; 025import org.apache.commons.logging.LogFactory; 026import org.jboss.el.ExpressionFactoryImpl; 027import org.nuxeo.ecm.automation.AutomationService; 028import org.nuxeo.ecm.core.api.Blob; 029import org.nuxeo.ecm.core.api.CoreSession; 030import org.nuxeo.ecm.core.api.DocumentModel; 031import org.nuxeo.ecm.core.api.DocumentNotFoundException; 032import org.nuxeo.ecm.core.api.DocumentRef; 033import org.nuxeo.ecm.core.api.NuxeoException; 034import org.nuxeo.ecm.core.api.VersioningOption; 035import org.nuxeo.ecm.platform.actions.ActionContext; 036import org.nuxeo.ecm.platform.actions.ELActionContext; 037import org.nuxeo.ecm.platform.actions.ejb.ActionManager; 038import org.nuxeo.ecm.platform.el.ExpressionContext; 039import org.nuxeo.ecm.platform.rendition.Rendition; 040import org.nuxeo.ecm.platform.rendition.extension.DefaultAutomationRenditionProvider; 041import org.nuxeo.ecm.platform.rendition.extension.RenditionProvider; 042import org.nuxeo.ecm.platform.rendition.impl.LiveRendition; 043import org.nuxeo.ecm.platform.rendition.impl.StoredRendition; 044import org.nuxeo.runtime.api.Framework; 045import org.nuxeo.runtime.model.ComponentContext; 046import org.nuxeo.runtime.model.ComponentInstance; 047import org.nuxeo.runtime.model.DefaultComponent; 048 049/** 050 * Default implementation of {@link RenditionService}. 051 * 052 * @author <a href="mailto:troger@nuxeo.com">Thomas Roger</a> 053 * @since 5.4.1 054 */ 055public class RenditionServiceImpl extends DefaultComponent implements RenditionService { 056 057 public static final String RENDITION_DEFINITIONS_EP = "renditionDefinitions"; 058 059 public static final String RENDITON_DEFINION_PROVIDERS_EP = "renditionDefinitionProviders"; 060 061 private static final Log log = LogFactory.getLog(RenditionServiceImpl.class); 062 063 /** 064 * @deprecated since 7.2. Not used. 065 */ 066 @Deprecated 067 protected AutomationService automationService; 068 069 /** 070 * @deprecated since 7.3. 071 */ 072 @Deprecated 073 protected Map<String, RenditionDefinition> renditionDefinitions; 074 075 /** 076 * @since 7.3. RenditionDefinitions are store in {@link #renditionDefinitionRegistry}. 077 */ 078 protected RenditionDefinitionRegistry renditionDefinitionRegistry; 079 080 protected RenditionDefinitionProviderRegistry renditionDefinitionProviderRegistry; 081 082 @Override 083 public void activate(ComponentContext context) { 084 renditionDefinitions = new HashMap<>(); 085 renditionDefinitionRegistry = new RenditionDefinitionRegistry(); 086 renditionDefinitionProviderRegistry = new RenditionDefinitionProviderRegistry(); 087 super.activate(context); 088 } 089 090 @Override 091 public void deactivate(ComponentContext context) { 092 renditionDefinitions = null; 093 renditionDefinitionRegistry = null; 094 renditionDefinitionProviderRegistry = null; 095 super.deactivate(context); 096 } 097 098 public RenditionDefinition getRenditionDefinition(String name) { 099 return renditionDefinitionRegistry.getRenditionDefinition(name); 100 } 101 102 @Override 103 @Deprecated 104 public List<RenditionDefinition> getDeclaredRenditionDefinitions() { 105 return new ArrayList<>(renditionDefinitionRegistry.descriptors.values()); 106 } 107 108 @Override 109 @Deprecated 110 public List<RenditionDefinition> getDeclaredRenditionDefinitionsForProviderType(String providerType) { 111 List<RenditionDefinition> defs = new ArrayList<>(); 112 for (RenditionDefinition def : getDeclaredRenditionDefinitions()) { 113 if (def.getProviderType().equals(providerType)) { 114 defs.add(def); 115 } 116 } 117 return defs; 118 } 119 120 @Override 121 public List<RenditionDefinition> getAvailableRenditionDefinitions(DocumentModel doc) { 122 123 List<RenditionDefinition> defs = new ArrayList<>(); 124 defs.addAll(renditionDefinitionRegistry.getRenditionDefinitions(doc)); 125 defs.addAll(renditionDefinitionProviderRegistry.getRenditionDefinitions(doc)); 126 127 // XXX what about "lost renditions" ? 128 return defs; 129 } 130 131 protected boolean canUseRenditionDefinition(RenditionDefinition renditionDefinition, DocumentModel doc) { 132 ActionManager actionService = Framework.getService(ActionManager.class); 133 return actionService.checkFilters(renditionDefinition.getFilterIds(), createActionContext(doc)); 134 } 135 136 protected ActionContext createActionContext(DocumentModel doc) { 137 ActionContext actionContext = new ELActionContext(new ExpressionContext(), new ExpressionFactoryImpl()); 138 actionContext.setCurrentDocument(doc); 139 return actionContext; 140 } 141 142 @Override 143 public DocumentRef storeRendition(DocumentModel source, String renditionDefinitionName) { 144 145 Rendition rendition = getRendition(source, renditionDefinitionName, true); 146 147 return rendition.getHostDocument().getRef(); 148 } 149 150 protected DocumentModel storeRendition(DocumentModel sourceDocument, List<Blob> renderedBlobs, String name) { 151 CoreSession session = sourceDocument.getCoreSession(); 152 DocumentRef versionRef = createVersionIfNeeded(sourceDocument, session); 153 154 DocumentModel version = session.getDocument(versionRef); 155 RenditionCreator rc = new RenditionCreator(session, sourceDocument, version, renderedBlobs.get(0), name); 156 rc.runUnrestricted(); 157 158 DocumentModel detachedRendition = rc.getDetachedDendition(); 159 160 detachedRendition.attach(sourceDocument.getSessionId()); 161 return detachedRendition; 162 } 163 164 protected DocumentRef createVersionIfNeeded(DocumentModel source, CoreSession session) { 165 DocumentRef versionRef; 166 if (source.isVersion()) { 167 versionRef = source.getRef(); 168 } else if (source.isCheckedOut()) { 169 versionRef = session.checkIn(source.getRef(), VersioningOption.MINOR, null); 170 source.refresh(DocumentModel.REFRESH_STATE, null); 171 } else { 172 versionRef = session.getLastDocumentVersionRef(source.getRef()); 173 } 174 return versionRef; 175 } 176 177 /** 178 * @deprecated since 7.2. Not used. 179 */ 180 @Deprecated 181 protected AutomationService getAutomationService() { 182 return Framework.getService(AutomationService.class); 183 } 184 185 @Override 186 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 187 if (RENDITION_DEFINITIONS_EP.equals(extensionPoint)) { 188 RenditionDefinition renditionDefinition = (RenditionDefinition) contribution; 189 renditionDefinitionRegistry.addContribution(renditionDefinition); 190 } else if (RENDITON_DEFINION_PROVIDERS_EP.equals(extensionPoint)) { 191 renditionDefinitionProviderRegistry.addContribution((RenditionDefinitionProviderDescriptor) contribution); 192 } 193 } 194 195 /** 196 * @deprecated since 7.3. RenditionDefinitions are store in {@link #renditionDefinitionRegistry}. 197 */ 198 @Deprecated 199 protected void registerRendition(RenditionDefinition renditionDefinition) { 200 String name = renditionDefinition.getName(); 201 if (name == null) { 202 log.error("Cannot register rendition without a name"); 203 return; 204 } 205 boolean enabled = renditionDefinition.isEnabled(); 206 if (renditionDefinitions.containsKey(name)) { 207 log.info("Overriding rendition with name: " + name); 208 if (enabled) { 209 renditionDefinition = mergeRenditions(renditionDefinitions.get(name), renditionDefinition); 210 } else { 211 log.info("Disabled rendition with name " + name); 212 renditionDefinitions.remove(name); 213 } 214 } 215 if (enabled) { 216 log.info("Registering rendition with name: " + name); 217 renditionDefinitions.put(name, renditionDefinition); 218 } 219 220 // setup the Provider 221 setupProvider(renditionDefinition); 222 } 223 224 /** 225 * @deprecated since 7.3. RenditionDefinitions are store in {@link #renditionDefinitionRegistry}. 226 */ 227 @Deprecated 228 protected void setupProvider(RenditionDefinition definition) { 229 if (definition.getProviderClass() == null) { 230 definition.setProvider(new DefaultAutomationRenditionProvider()); 231 } else { 232 try { 233 RenditionProvider provider = definition.getProviderClass().newInstance(); 234 definition.setProvider(provider); 235 } catch (Exception e) { 236 log.error("Unable to create RenditionProvider", e); 237 } 238 } 239 } 240 241 protected RenditionDefinition mergeRenditions(RenditionDefinition oldRenditionDefinition, 242 RenditionDefinition newRenditionDefinition) { 243 String label = newRenditionDefinition.getLabel(); 244 if (label != null) { 245 oldRenditionDefinition.label = label; 246 } 247 248 String operationChain = newRenditionDefinition.getOperationChain(); 249 if (operationChain != null) { 250 oldRenditionDefinition.operationChain = operationChain; 251 } 252 253 return oldRenditionDefinition; 254 } 255 256 @Override 257 public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 258 if (RENDITION_DEFINITIONS_EP.equals(extensionPoint)) { 259 renditionDefinitionRegistry.removeContribution((RenditionDefinition) contribution); 260 } else if (RENDITON_DEFINION_PROVIDERS_EP.equals(extensionPoint)) { 261 renditionDefinitionProviderRegistry.removeContribution((RenditionDefinitionProviderDescriptor) contribution); 262 } 263 } 264 265 /** 266 * @deprecated since 7.3. RenditionDefinitions are store in {@link #renditionDefinitionRegistry}. 267 */ 268 @Deprecated 269 protected void unregisterRendition(RenditionDefinition renditionDefinition) { 270 String name = renditionDefinition.getName(); 271 renditionDefinitions.remove(name); 272 log.info("Unregistering rendition with name: " + name); 273 } 274 275 @Override 276 public Rendition getRendition(DocumentModel doc, String renditionName) { 277 return getRendition(doc, renditionName, false); 278 } 279 280 @Override 281 public Rendition getRendition(DocumentModel doc, String renditionName, boolean store) { 282 283 RenditionDefinition renditionDefinition = renditionDefinitionRegistry.getRenditionDefinition(renditionName); 284 if (renditionDefinition == null) { 285 renditionDefinition = renditionDefinitionProviderRegistry.getRenditionDefinition(renditionName, doc); 286 if (renditionDefinition == null) { 287 String message = "The rendition definition '%s' is not registered"; 288 throw new NuxeoException(String.format(message, renditionName)); 289 } 290 } 291 292 if (!renditionDefinition.getProvider().isAvailable(doc, renditionDefinition)) { 293 throw new NuxeoException("Rendition " + renditionName + " not available for this doc " + doc.getId()); 294 } 295 296 DocumentModel stored = null; 297 if (!doc.isCheckedOut()) { 298 // since stored renditions are done against a version 299 // checkedout Documents can not have a stored rendition 300 RenditionFinder finder = new RenditionFinder(doc, renditionName); 301 finder.runUnrestricted(); 302 // retrieve the Detached stored rendition doc 303 stored = finder.getStoredRendition(); 304 // re-attach the detached doc 305 if (stored != null) { 306 stored.attach(doc.getCoreSession().getSessionId()); 307 } 308 } 309 310 if (stored != null) { 311 return new StoredRendition(stored, renditionDefinition); 312 } 313 314 LiveRendition rendition = new LiveRendition(doc, renditionDefinition); 315 316 if (store) { 317 DocumentModel storedRenditionDoc = storeRendition(doc, rendition.getBlobs(), renditionDefinition.getName()); 318 return new StoredRendition(storedRenditionDoc, renditionDefinition); 319 320 } else { 321 return rendition; 322 } 323 } 324 325 @Override 326 public List<Rendition> getAvailableRenditions(DocumentModel doc) { 327 return getAvailableRenditions(doc, false); 328 } 329 330 @Override 331 public List<Rendition> getAvailableRenditions(DocumentModel doc, boolean onlyVisible) { 332 List<Rendition> renditions = new ArrayList<>(); 333 334 if (doc.isProxy()) { 335 return renditions; 336 } 337 338 List<RenditionDefinition> defs = getAvailableRenditionDefinitions(doc); 339 if (defs != null) { 340 for (RenditionDefinition def : defs) { 341 if (!onlyVisible || onlyVisible && def.isVisible()) { 342 Rendition rendition = getRendition(doc, def.getName()); 343 if (rendition != null) { 344 renditions.add(rendition); 345 } 346 } 347 } 348 } 349 350 return renditions; 351 } 352}