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