001/* 002 * (C) Copyright 2010 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 * Contributors: 016 * Nuxeo - initial API and implementation 017 */ 018 019package org.nuxeo.ecm.platform.rendition.service; 020 021import java.util.ArrayList; 022import java.util.Deque; 023import java.util.HashMap; 024import java.util.LinkedList; 025import java.util.List; 026import java.util.Map; 027 028import org.apache.commons.logging.Log; 029import org.apache.commons.logging.LogFactory; 030import org.nuxeo.ecm.automation.AutomationService; 031import org.nuxeo.ecm.core.api.Blob; 032import org.nuxeo.ecm.core.api.CoreSession; 033import org.nuxeo.ecm.core.api.DocumentModel; 034import org.nuxeo.ecm.core.api.DocumentRef; 035import org.nuxeo.ecm.core.api.NuxeoException; 036import org.nuxeo.ecm.core.api.VersioningOption; 037import org.nuxeo.ecm.platform.rendition.Rendition; 038import org.nuxeo.ecm.platform.rendition.extension.DefaultAutomationRenditionProvider; 039import org.nuxeo.ecm.platform.rendition.extension.RenditionProvider; 040import org.nuxeo.ecm.platform.rendition.impl.LiveRendition; 041import org.nuxeo.ecm.platform.rendition.impl.StoredRendition; 042import org.nuxeo.runtime.api.Framework; 043import org.nuxeo.runtime.model.ComponentContext; 044import org.nuxeo.runtime.model.ComponentInstance; 045import org.nuxeo.runtime.model.DefaultComponent; 046 047/** 048 * Default implementation of {@link RenditionService}. 049 * 050 * @author <a href="mailto:troger@nuxeo.com">Thomas Roger</a> 051 * @since 5.4.1 052 */ 053public class RenditionServiceImpl extends DefaultComponent implements RenditionService { 054 055 public static final String RENDITION_DEFINITIONS_EP = "renditionDefinitions"; 056 057 public static final String RENDITON_DEFINION_PROVIDERS_EP = "renditionDefinitionProviders"; 058 059 /** 060 * @since 8.1 061 */ 062 public static final String STORED_RENDITION_MANAGERS_EP = "storedRenditionManagers"; 063 064 private static final Log log = LogFactory.getLog(RenditionServiceImpl.class); 065 066 /** 067 * @deprecated since 7.2. Not used. 068 */ 069 @Deprecated 070 protected AutomationService automationService; 071 072 /** 073 * @deprecated since 7.3. 074 */ 075 @Deprecated 076 protected Map<String, RenditionDefinition> renditionDefinitions; 077 078 /** 079 * @since 7.3. RenditionDefinitions are store in {@link #renditionDefinitionRegistry}. 080 */ 081 protected RenditionDefinitionRegistry renditionDefinitionRegistry; 082 083 protected RenditionDefinitionProviderRegistry renditionDefinitionProviderRegistry; 084 085 protected static final StoredRenditionManager DEFAULT_STORED_RENDITION_MANAGER = new DefaultStoredRenditionManager(); 086 087 /** 088 * @since 8.1 089 */ 090 protected Deque<StoredRenditionManagerDescriptor> storedRenditionManagerDescriptors = new LinkedList<>(); 091 092 /** 093 * @since 8.1 094 */ 095 public StoredRenditionManager getStoredRenditionManager() { 096 StoredRenditionManagerDescriptor descr = storedRenditionManagerDescriptors.peekLast(); 097 return descr == null ? DEFAULT_STORED_RENDITION_MANAGER : descr.getStoredRenditionManager(); 098 } 099 100 @Override 101 public void activate(ComponentContext context) { 102 renditionDefinitions = new HashMap<>(); 103 renditionDefinitionRegistry = new RenditionDefinitionRegistry(); 104 renditionDefinitionProviderRegistry = new RenditionDefinitionProviderRegistry(); 105 super.activate(context); 106 } 107 108 @Override 109 public void deactivate(ComponentContext context) { 110 renditionDefinitions = null; 111 renditionDefinitionRegistry = null; 112 renditionDefinitionProviderRegistry = null; 113 super.deactivate(context); 114 } 115 116 public RenditionDefinition getRenditionDefinition(String name) { 117 return renditionDefinitionRegistry.getRenditionDefinition(name); 118 } 119 120 @Override 121 @Deprecated 122 public List<RenditionDefinition> getDeclaredRenditionDefinitions() { 123 return new ArrayList<>(renditionDefinitionRegistry.descriptors.values()); 124 } 125 126 @Override 127 @Deprecated 128 public List<RenditionDefinition> getDeclaredRenditionDefinitionsForProviderType(String providerType) { 129 List<RenditionDefinition> defs = new ArrayList<>(); 130 for (RenditionDefinition def : getDeclaredRenditionDefinitions()) { 131 if (def.getProviderType().equals(providerType)) { 132 defs.add(def); 133 } 134 } 135 return defs; 136 } 137 138 @Override 139 public List<RenditionDefinition> getAvailableRenditionDefinitions(DocumentModel doc) { 140 141 List<RenditionDefinition> defs = new ArrayList<>(); 142 defs.addAll(renditionDefinitionRegistry.getRenditionDefinitions(doc)); 143 defs.addAll(renditionDefinitionProviderRegistry.getRenditionDefinitions(doc)); 144 145 // XXX what about "lost renditions" ? 146 return defs; 147 } 148 149 @Override 150 public DocumentRef storeRendition(DocumentModel source, String renditionDefinitionName) { 151 Rendition rendition = getRendition(source, renditionDefinitionName, true); 152 return rendition == null ? null : rendition.getHostDocument().getRef(); 153 } 154 155 /** 156 * @deprecated since 8.1 157 */ 158 @Deprecated 159 protected DocumentModel storeRendition(DocumentModel sourceDocument, Rendition rendition, String name) { 160 StoredRendition storedRendition = storeRendition(sourceDocument, rendition); 161 return storedRendition == null ? null : storedRendition.getHostDocument(); 162 } 163 164 /** 165 * @since 8.1 166 */ 167 protected StoredRendition storeRendition(DocumentModel sourceDocument, Rendition rendition) { 168 if (!rendition.isCompleted()) { 169 return null; 170 } 171 List<Blob> renderedBlobs = rendition.getBlobs(); 172 Blob renderedBlob = renderedBlobs.get(0); 173 CoreSession session = sourceDocument.getCoreSession(); 174 DocumentModel version = null; 175 boolean isVersionable = sourceDocument.isVersionable(); 176 if (isVersionable) { 177 DocumentRef versionRef = createVersionIfNeeded(sourceDocument, session); 178 version = session.getDocument(versionRef); 179 } 180 181 RenditionDefinition renditionDefinition = getRenditionDefinition(rendition.getName()); 182 StoredRendition storedRendition = getStoredRenditionManager().createStoredRendition(sourceDocument, version, 183 renderedBlob, renditionDefinition); 184 return storedRendition; 185 } 186 187 protected DocumentRef createVersionIfNeeded(DocumentModel source, CoreSession session) { 188 if (source.isVersionable()) { 189 if (source.isVersion()) { 190 return source.getRef(); 191 } else if (source.isCheckedOut()) { 192 DocumentRef versionRef = session.checkIn(source.getRef(), VersioningOption.MINOR, null); 193 source.refresh(DocumentModel.REFRESH_STATE, null); 194 return versionRef; 195 } else { 196 return session.getLastDocumentVersionRef(source.getRef()); 197 } 198 } 199 return null; 200 } 201 202 /** 203 * @deprecated since 7.2. Not used. 204 */ 205 @Deprecated 206 protected AutomationService getAutomationService() { 207 return Framework.getService(AutomationService.class); 208 } 209 210 @Override 211 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 212 if (RENDITION_DEFINITIONS_EP.equals(extensionPoint)) { 213 RenditionDefinition renditionDefinition = (RenditionDefinition) contribution; 214 renditionDefinitionRegistry.addContribution(renditionDefinition); 215 } else if (RENDITON_DEFINION_PROVIDERS_EP.equals(extensionPoint)) { 216 renditionDefinitionProviderRegistry.addContribution((RenditionDefinitionProviderDescriptor) contribution); 217 } else if (STORED_RENDITION_MANAGERS_EP.equals(extensionPoint)) { 218 storedRenditionManagerDescriptors.add(((StoredRenditionManagerDescriptor) contribution)); 219 } 220 } 221 222 /** 223 * @deprecated since 7.3. RenditionDefinitions are store in {@link #renditionDefinitionRegistry}. 224 */ 225 @Deprecated 226 protected void registerRendition(RenditionDefinition renditionDefinition) { 227 String name = renditionDefinition.getName(); 228 if (name == null) { 229 log.error("Cannot register rendition without a name"); 230 return; 231 } 232 boolean enabled = renditionDefinition.isEnabled(); 233 if (renditionDefinitions.containsKey(name)) { 234 log.info("Overriding rendition with name: " + name); 235 if (enabled) { 236 renditionDefinition = mergeRenditions(renditionDefinitions.get(name), renditionDefinition); 237 } else { 238 log.info("Disabled rendition with name " + name); 239 renditionDefinitions.remove(name); 240 } 241 } 242 if (enabled) { 243 log.info("Registering rendition with name: " + name); 244 renditionDefinitions.put(name, renditionDefinition); 245 } 246 247 // setup the Provider 248 setupProvider(renditionDefinition); 249 } 250 251 /** 252 * @deprecated since 7.3. RenditionDefinitions are store in {@link #renditionDefinitionRegistry}. 253 */ 254 @Deprecated 255 protected void setupProvider(RenditionDefinition definition) { 256 if (definition.getProviderClass() == null) { 257 definition.setProvider(new DefaultAutomationRenditionProvider()); 258 } else { 259 try { 260 RenditionProvider provider = definition.getProviderClass().newInstance(); 261 definition.setProvider(provider); 262 } catch (Exception e) { 263 log.error("Unable to create RenditionProvider", e); 264 } 265 } 266 } 267 268 protected RenditionDefinition mergeRenditions(RenditionDefinition oldRenditionDefinition, 269 RenditionDefinition newRenditionDefinition) { 270 String label = newRenditionDefinition.getLabel(); 271 if (label != null) { 272 oldRenditionDefinition.label = label; 273 } 274 275 String operationChain = newRenditionDefinition.getOperationChain(); 276 if (operationChain != null) { 277 oldRenditionDefinition.operationChain = operationChain; 278 } 279 280 return oldRenditionDefinition; 281 } 282 283 @Override 284 public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 285 if (RENDITION_DEFINITIONS_EP.equals(extensionPoint)) { 286 renditionDefinitionRegistry.removeContribution((RenditionDefinition) contribution); 287 } else if (RENDITON_DEFINION_PROVIDERS_EP.equals(extensionPoint)) { 288 renditionDefinitionProviderRegistry.removeContribution((RenditionDefinitionProviderDescriptor) contribution); 289 } else if (STORED_RENDITION_MANAGERS_EP.equals(extensionPoint)) { 290 storedRenditionManagerDescriptors.remove(((StoredRenditionManagerDescriptor) contribution)); 291 } 292 } 293 294 /** 295 * @deprecated since 7.3. RenditionDefinitions are store in {@link #renditionDefinitionRegistry}. 296 */ 297 @Deprecated 298 protected void unregisterRendition(RenditionDefinition renditionDefinition) { 299 String name = renditionDefinition.getName(); 300 renditionDefinitions.remove(name); 301 log.info("Unregistering rendition with name: " + name); 302 } 303 304 @Override 305 public Rendition getRendition(DocumentModel doc, String renditionName) { 306 RenditionDefinition renditionDefinition = getAvailableRenditionDefinition(doc, renditionName); 307 return getRendition(doc, renditionDefinition, renditionDefinition.isStoreByDefault()); 308 } 309 310 @Override 311 public Rendition getRendition(DocumentModel doc, String renditionName, boolean store) { 312 RenditionDefinition renditionDefinition = getAvailableRenditionDefinition(doc, renditionName); 313 return getRendition(doc, renditionDefinition, store); 314 } 315 316 protected Rendition getRendition(DocumentModel doc, RenditionDefinition renditionDefinition, boolean store) { 317 318 Rendition rendition = null; 319 boolean isVersionable = doc.isVersionable(); 320 if (!isVersionable || !doc.isCheckedOut()) { 321 // stored renditions are only done against a non-versionable doc 322 // or a versionable doc that is not checkedout 323 rendition = getStoredRenditionManager().findStoredRendition(doc, renditionDefinition); 324 if (rendition != null) { 325 return rendition; 326 } 327 } 328 329 rendition = new LiveRendition(doc, renditionDefinition); 330 331 if (store) { 332 StoredRendition storedRendition = storeRendition(doc, rendition); 333 if (storedRendition != null) { 334 return storedRendition; 335 } 336 } 337 return rendition; 338 } 339 340 protected RenditionDefinition getAvailableRenditionDefinition(DocumentModel doc, String renditionName) { 341 RenditionDefinition renditionDefinition = renditionDefinitionRegistry.getRenditionDefinition(renditionName); 342 if (renditionDefinition == null) { 343 renditionDefinition = renditionDefinitionProviderRegistry.getRenditionDefinition(renditionName, doc); 344 if (renditionDefinition == null) { 345 String message = "The rendition definition '%s' is not registered"; 346 throw new NuxeoException(String.format(message, renditionName)); 347 } 348 } else { 349 // we have a rendition definition but we must check that it can be used for this doc 350 if (!renditionDefinitionRegistry.canUseRenditionDefinition(renditionDefinition, doc)) { 351 throw new NuxeoException("Rendition " + renditionName + " cannot be used for this doc " + doc.getId()); 352 } 353 } 354 if (!renditionDefinition.getProvider().isAvailable(doc, renditionDefinition)) { 355 throw new NuxeoException("Rendition " + renditionName + " not available for this doc " + doc.getId()); 356 } 357 return renditionDefinition; 358 } 359 360 @Override 361 public List<Rendition> getAvailableRenditions(DocumentModel doc) { 362 return getAvailableRenditions(doc, false); 363 } 364 365 @Override 366 public List<Rendition> getAvailableRenditions(DocumentModel doc, boolean onlyVisible) { 367 List<Rendition> renditions = new ArrayList<>(); 368 369 if (doc.isProxy()) { 370 return renditions; 371 } 372 373 List<RenditionDefinition> defs = getAvailableRenditionDefinitions(doc); 374 if (defs != null) { 375 for (RenditionDefinition def : defs) { 376 if (!onlyVisible || onlyVisible && def.isVisible()) { 377 Rendition rendition = getRendition(doc, def.getName(), false); 378 if (rendition != null) { 379 renditions.add(rendition); 380 } 381 } 382 } 383 } 384 385 return renditions; 386 } 387}