001/* 002 * (C) Copyright 2010-2018 Nuxeo (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 * Nuxeo - initial API and implementation 018 */ 019package org.nuxeo.ecm.platform.rendition.service; 020 021import static org.nuxeo.ecm.core.api.security.SecurityConstants.ADD_CHILDREN; 022import static org.nuxeo.ecm.platform.rendition.Constants.RENDITION_SOURCE_ID_PROPERTY; 023import static org.nuxeo.ecm.platform.rendition.Constants.RENDITION_SOURCE_VERSIONABLE_ID_PROPERTY; 024 025import java.io.Serializable; 026import java.util.ArrayList; 027import java.util.Collections; 028import java.util.Deque; 029import java.util.HashMap; 030import java.util.LinkedList; 031import java.util.List; 032import java.util.Map; 033import java.util.Optional; 034 035import javax.script.Invocable; 036import javax.script.ScriptContext; 037import javax.script.ScriptEngine; 038import javax.script.ScriptEngineManager; 039import javax.script.ScriptException; 040 041import org.apache.commons.collections4.CollectionUtils; 042import org.apache.commons.lang3.StringUtils; 043import org.apache.logging.log4j.LogManager; 044import org.apache.logging.log4j.Logger; 045import org.nuxeo.ecm.core.api.Blob; 046import org.nuxeo.ecm.core.api.CoreInstance; 047import org.nuxeo.ecm.core.api.CoreSession; 048import org.nuxeo.ecm.core.api.DocumentModel; 049import org.nuxeo.ecm.core.api.DocumentRef; 050import org.nuxeo.ecm.core.api.DocumentSecurityException; 051import org.nuxeo.ecm.core.api.IdRef; 052import org.nuxeo.ecm.core.api.IterableQueryResult; 053import org.nuxeo.ecm.core.api.NuxeoException; 054import org.nuxeo.ecm.core.api.NuxeoPrincipal; 055import org.nuxeo.ecm.core.api.UnrestrictedSessionRunner; 056import org.nuxeo.ecm.core.api.VersioningOption; 057import org.nuxeo.ecm.core.query.sql.NXQL; 058import org.nuxeo.ecm.platform.query.nxql.NXQLQueryBuilder; 059import org.nuxeo.ecm.platform.rendition.Constants; 060import org.nuxeo.ecm.platform.rendition.Rendition; 061import org.nuxeo.ecm.platform.rendition.impl.LazyRendition; 062import org.nuxeo.ecm.platform.rendition.impl.LiveRendition; 063import org.nuxeo.ecm.platform.rendition.impl.StoredRendition; 064import org.nuxeo.runtime.model.ComponentContext; 065import org.nuxeo.runtime.model.ComponentInstance; 066import org.nuxeo.runtime.model.DefaultComponent; 067import org.nuxeo.runtime.transaction.TransactionHelper; 068 069/** 070 * Default implementation of {@link RenditionService}. 071 * 072 * @author <a href="mailto:troger@nuxeo.com">Thomas Roger</a> 073 * @since 5.4.1 074 */ 075public class RenditionServiceImpl extends DefaultComponent implements RenditionService { 076 077 public static final String RENDITION_DEFINITIONS_EP = "renditionDefinitions"; 078 079 public static final String RENDITON_DEFINION_PROVIDERS_EP = "renditionDefinitionProviders"; 080 081 public static final String DEFAULT_RENDITION_EP = "defaultRendition"; 082 083 /** 084 * @since 8.1 085 */ 086 public static final String STORED_RENDITION_MANAGERS_EP = "storedRenditionManagers"; 087 088 private static final Logger log = LogManager.getLogger(RenditionServiceImpl.class); 089 090 /** 091 * @since 7.3. 092 */ 093 protected RenditionDefinitionRegistry renditionDefinitionRegistry; 094 095 protected RenditionDefinitionProviderRegistry renditionDefinitionProviderRegistry; 096 097 protected List<DefaultRenditionDescriptor> defaultRenditionDescriptors = new ArrayList<>(); 098 099 protected static final StoredRenditionManager DEFAULT_STORED_RENDITION_MANAGER = new DefaultStoredRenditionManager(); 100 101 /** 102 * @since 8.1 103 */ 104 protected Deque<StoredRenditionManagerDescriptor> storedRenditionManagerDescriptors = new LinkedList<>(); 105 106 protected final ScriptEngineManager scriptEngineManager; 107 108 public RenditionServiceImpl() { 109 scriptEngineManager = new ScriptEngineManager(); 110 } 111 112 /** 113 * @since 8.1 114 */ 115 public StoredRenditionManager getStoredRenditionManager() { 116 StoredRenditionManagerDescriptor descr = storedRenditionManagerDescriptors.peekLast(); 117 return descr == null ? DEFAULT_STORED_RENDITION_MANAGER : descr.getStoredRenditionManager(); 118 } 119 120 @Override 121 public void activate(ComponentContext context) { 122 renditionDefinitionRegistry = new RenditionDefinitionRegistry(); 123 renditionDefinitionProviderRegistry = new RenditionDefinitionProviderRegistry(); 124 super.activate(context); 125 } 126 127 @Override 128 public void deactivate(ComponentContext context) { 129 renditionDefinitionRegistry = null; 130 renditionDefinitionProviderRegistry = null; 131 super.deactivate(context); 132 } 133 134 /** 135 * Shoudn't be used since it doesn't take into account the rendition definitions made available for a given document 136 * by the contributed {@link RenditionDefinitionProvider}s. 137 * 138 * @deprecated since 10.10, use {@link #getAvailableRenditionDefinition(DocumentModel, String)} instead 139 */ 140 @Deprecated(since = "10.10") 141 public RenditionDefinition getRenditionDefinition(String name) { 142 return renditionDefinitionRegistry.getRenditionDefinition(name); 143 } 144 145 @Override 146 public List<RenditionDefinition> getDeclaredRenditionDefinitions() { 147 return new ArrayList<>(renditionDefinitionRegistry.descriptors.values()); 148 } 149 150 /** 151 * @deprecated since 7.2 because unused 152 */ 153 @Override 154 @Deprecated(since = "7.2") 155 public List<RenditionDefinition> getDeclaredRenditionDefinitionsForProviderType(String providerType) { 156 List<RenditionDefinition> defs = new ArrayList<>(); 157 for (RenditionDefinition def : getDeclaredRenditionDefinitions()) { 158 if (def.getProviderType().equals(providerType)) { 159 defs.add(def); 160 } 161 } 162 return defs; 163 } 164 165 @Override 166 public List<RenditionDefinition> getAvailableRenditionDefinitions(DocumentModel doc) { 167 168 List<RenditionDefinition> defs = new ArrayList<>(); 169 defs.addAll(renditionDefinitionRegistry.getRenditionDefinitions(doc)); 170 defs.addAll(renditionDefinitionProviderRegistry.getRenditionDefinitions(doc)); 171 172 // XXX what about "lost renditions" ? 173 return defs; 174 } 175 176 @Override 177 public DocumentRef storeRendition(DocumentModel source, String renditionDefinitionName) { 178 Rendition rendition = getRendition(source, renditionDefinitionName, true); 179 return rendition == null ? null : rendition.getHostDocument().getRef(); 180 } 181 182 /** 183 * @deprecated since 8.1 184 */ 185 @Deprecated(since = "8.1") 186 protected DocumentModel storeRendition(DocumentModel sourceDocument, Rendition rendition, String name) { 187 StoredRendition storedRendition = storeRendition(sourceDocument, rendition); 188 return storedRendition == null ? null : storedRendition.getHostDocument(); 189 } 190 191 /** 192 * @since 8.1 193 * @deprecated since 10.10, use {@link #storeRendition(DocumentModel, Rendition, RenditionDefinition)} instead 194 */ 195 @Deprecated(since = "10.10") 196 protected StoredRendition storeRendition(DocumentModel sourceDocument, Rendition rendition) { 197 RenditionDefinition renditionDefinition = getAvailableRenditionDefinition(sourceDocument, rendition.getName()); 198 return storeRendition(sourceDocument, rendition, renditionDefinition); 199 } 200 201 /** 202 * @since 10.10 203 */ 204 protected StoredRendition storeRendition(DocumentModel sourceDocument, Rendition rendition, 205 RenditionDefinition renditionDefinition) { 206 if (!rendition.isCompleted()) { 207 log.debug("Incomplete rendition for source document {}.", sourceDocument); 208 return null; 209 } 210 List<Blob> renderedBlobs = rendition.getBlobs(); 211 if (CollectionUtils.isEmpty(renderedBlobs)) { 212 log.debug("No rendition blobs for source document {}.", sourceDocument); 213 return null; 214 } 215 Blob renderedBlob = renderedBlobs.get(0); 216 String mimeType = renderedBlob.getMimeType(); 217 if (mimeType != null 218 && (mimeType.contains(LazyRendition.ERROR_MARKER) || mimeType.contains(LazyRendition.STALE_MARKER))) { 219 log.debug("Rendition has MIME type {} for source document {}.", mimeType, sourceDocument); 220 return null; 221 } 222 223 CoreSession session = sourceDocument.getCoreSession(); 224 DocumentModel version = null; 225 boolean isVersionable = sourceDocument.isVersionable(); 226 if (sourceDocument.isVersion()) { 227 version = sourceDocument; 228 sourceDocument = session.getDocument(new IdRef(version.getSourceId())); 229 } else if (isVersionable) { 230 DocumentRef versionRef = createVersionIfNeeded(sourceDocument, session); 231 version = session.getDocument(versionRef); 232 } 233 234 log.debug("Creating stored rendition for source document {}.", sourceDocument); 235 return getStoredRenditionManager().createStoredRendition(sourceDocument, version, renderedBlob, 236 renditionDefinition); 237 } 238 239 protected DocumentRef createVersionIfNeeded(DocumentModel source, CoreSession session) { 240 if (source.isVersionable()) { 241 if (source.isVersion()) { 242 return source.getRef(); 243 } else if (source.isCheckedOut()) { 244 DocumentRef versionRef = session.checkIn(source.getRef(), VersioningOption.MINOR, null); 245 source.refresh(DocumentModel.REFRESH_STATE, null); 246 return versionRef; 247 } else { 248 return session.getLastDocumentVersionRef(source.getRef()); 249 } 250 } 251 return null; 252 } 253 254 @Override 255 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 256 if (RENDITION_DEFINITIONS_EP.equals(extensionPoint)) { 257 RenditionDefinition renditionDefinition = (RenditionDefinition) contribution; 258 renditionDefinitionRegistry.addContribution(renditionDefinition); 259 } else if (RENDITON_DEFINION_PROVIDERS_EP.equals(extensionPoint)) { 260 renditionDefinitionProviderRegistry.addContribution((RenditionDefinitionProviderDescriptor) contribution); 261 } else if (STORED_RENDITION_MANAGERS_EP.equals(extensionPoint)) { 262 storedRenditionManagerDescriptors.add(((StoredRenditionManagerDescriptor) contribution)); 263 } else if (DEFAULT_RENDITION_EP.equals(extensionPoint)) { 264 // Save contribution 265 defaultRenditionDescriptors.add((DefaultRenditionDescriptor) contribution); 266 } 267 } 268 269 protected RenditionDefinition mergeRenditions(RenditionDefinition oldRenditionDefinition, 270 RenditionDefinition newRenditionDefinition) { 271 String label = newRenditionDefinition.getLabel(); 272 if (label != null) { 273 oldRenditionDefinition.label = label; 274 } 275 276 String operationChain = newRenditionDefinition.getOperationChain(); 277 if (operationChain != null) { 278 oldRenditionDefinition.operationChain = operationChain; 279 } 280 281 return oldRenditionDefinition; 282 } 283 284 @Override 285 public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 286 if (RENDITION_DEFINITIONS_EP.equals(extensionPoint)) { 287 renditionDefinitionRegistry.removeContribution((RenditionDefinition) contribution); 288 } else if (RENDITON_DEFINION_PROVIDERS_EP.equals(extensionPoint)) { 289 renditionDefinitionProviderRegistry.removeContribution( 290 (RenditionDefinitionProviderDescriptor) contribution); 291 } else if (STORED_RENDITION_MANAGERS_EP.equals(extensionPoint)) { 292 storedRenditionManagerDescriptors.remove((contribution)); 293 } else if (DEFAULT_RENDITION_EP.equals(extensionPoint)) { 294 defaultRenditionDescriptors.remove(contribution); 295 } 296 } 297 298 @Override 299 public Rendition getRendition(DocumentModel doc, String renditionName) { 300 RenditionDefinition renditionDefinition = getAvailableRenditionDefinition(doc, renditionName); 301 return getRendition(doc, renditionDefinition, renditionDefinition.isStoreByDefault()); 302 } 303 304 @Override 305 public Rendition getRendition(DocumentModel doc, String renditionName, boolean store) { 306 RenditionDefinition renditionDefinition = getAvailableRenditionDefinition(doc, renditionName); 307 return getRendition(doc, renditionDefinition, store); 308 } 309 310 /** 311 * @since 11.1 312 */ 313 protected Optional<Rendition> getRenditionSafe(DocumentModel doc, String defaultRenditionName, boolean store) { 314 try { 315 return Optional.of(getRendition(doc, defaultRenditionName, store)); 316 } catch (NuxeoException e) { 317 log.error("Unable to use default rendition: {}", defaultRenditionName, e); 318 return Optional.empty(); 319 } 320 } 321 322 protected Rendition getRendition(DocumentModel doc, RenditionDefinition renditionDefinition, boolean store) { 323 324 Rendition rendition; 325 boolean isVersionable = doc.isVersionable(); 326 if (!isVersionable || !doc.isCheckedOut()) { 327 log.debug("Document {} is not versionable nor checked out, trying to find a stored rendition.", doc); 328 // stored renditions are only done against a non-versionable doc 329 // or a versionable doc that is not checkedout 330 rendition = getStoredRenditionManager().findStoredRendition(doc, renditionDefinition); 331 if (rendition != null) { 332 log.debug("Found and returning a stored rendition for document {}.", doc); 333 return rendition; 334 } else { 335 log.debug("Found no stored rendition for document {}.", doc); 336 } 337 } else { 338 log.debug("Document {} is versionable and checked out, not trying to find any stored rendition.", doc); 339 } 340 341 rendition = new LiveRendition(doc, renditionDefinition); 342 343 if (store) { 344 StoredRendition storedRendition = storeRendition(doc, rendition, renditionDefinition); 345 if (storedRendition != null) { 346 log.debug("Returning new stored rendition for document {}.", doc); 347 return storedRendition; 348 } else { 349 log.debug("No rendition stored for document {}, returning live rendition.", doc); 350 } 351 } else { 352 log.debug("Returning live rendition for document {}.", doc); 353 } 354 return rendition; 355 } 356 357 @Override 358 public RenditionDefinition getAvailableRenditionDefinition(DocumentModel doc, String renditionName) { 359 RenditionDefinition renditionDefinition = renditionDefinitionRegistry.getRenditionDefinition(renditionName); 360 if (renditionDefinition == null) { 361 renditionDefinition = renditionDefinitionProviderRegistry.getRenditionDefinition(renditionName, doc); 362 if (renditionDefinition == null) { 363 String message = "The rendition definition '%s' is not registered"; 364 throw new NuxeoException(String.format(message, renditionName)); 365 } 366 } else { 367 // we have a rendition definition but we must check that it can be used for this doc 368 if (!renditionDefinitionRegistry.canUseRenditionDefinition(renditionDefinition, doc)) { 369 throw new NuxeoException("Rendition " + renditionName + " cannot be used for this doc " + doc.getId()); 370 } 371 } 372 if (renditionDefinition.getProvider() == null) { 373 throw new NuxeoException( 374 String.format("Rendition definition %s isn't bound to any rendition provider", renditionName)); 375 } 376 if (!renditionDefinition.getProvider().isAvailable(doc, renditionDefinition)) { 377 throw new NuxeoException( 378 String.format("Rendition %s not available for this doc %s", renditionName, doc.getPathAsString())); 379 } 380 return renditionDefinition; 381 } 382 383 @Override 384 public List<Rendition> getAvailableRenditions(DocumentModel doc) { 385 return getAvailableRenditions(doc, false); 386 } 387 388 @Override 389 public List<Rendition> getAvailableRenditions(DocumentModel doc, boolean onlyVisible) { 390 List<Rendition> renditions = new ArrayList<>(); 391 392 List<RenditionDefinition> defs = getAvailableRenditionDefinitions(doc); 393 if (defs != null) { 394 for (RenditionDefinition def : defs) { 395 if (!onlyVisible || def.isVisible()) { 396 Rendition rendition = getRendition(doc, def.getName(), false); 397 if (rendition != null) { 398 renditions.add(rendition); 399 } 400 } 401 } 402 } 403 404 return renditions; 405 } 406 407 @Override 408 public void deleteStoredRenditions(String repositoryName) { 409 StoredRenditionsCleaner cleaner = new StoredRenditionsCleaner(repositoryName); 410 cleaner.runUnrestricted(); 411 } 412 413 private final class StoredRenditionsCleaner extends UnrestrictedSessionRunner { 414 415 private static final int BATCH_SIZE = 100; 416 417 private StoredRenditionsCleaner(String repositoryName) { 418 super(repositoryName); 419 } 420 421 @Override 422 public void run() { 423 Map<String, List<String>> sourceIdToRenditionRefs = computeLiveDocumentRefsToRenditionRefs(); 424 removeStoredRenditions(sourceIdToRenditionRefs); 425 } 426 427 /** 428 * Computes only live documents renditions, the related versions will be deleted by Nuxeo. 429 */ 430 private Map<String, List<String>> computeLiveDocumentRefsToRenditionRefs() { 431 Map<String, List<String>> liveDocumentRefsToRenditionRefs = new HashMap<>(); 432 String query = String.format("SELECT %s, %s, %s FROM Document WHERE %s IS NOT NULL AND ecm:isVersion = 0", 433 NXQL.ECM_UUID, RENDITION_SOURCE_ID_PROPERTY, RENDITION_SOURCE_VERSIONABLE_ID_PROPERTY, 434 RENDITION_SOURCE_ID_PROPERTY); 435 try (IterableQueryResult result = session.queryAndFetch(query, NXQL.NXQL)) { 436 for (Map<String, Serializable> res : result) { 437 String renditionRef = res.get(NXQL.ECM_UUID).toString(); 438 String sourceId = res.get(RENDITION_SOURCE_ID_PROPERTY).toString(); 439 Serializable sourceVersionableId = res.get(RENDITION_SOURCE_VERSIONABLE_ID_PROPERTY); 440 441 String key = sourceVersionableId != null ? sourceVersionableId.toString() : sourceId; 442 liveDocumentRefsToRenditionRefs.computeIfAbsent(key, k -> new ArrayList<>()).add(renditionRef); 443 } 444 } 445 return liveDocumentRefsToRenditionRefs; 446 } 447 448 private void removeStoredRenditions(Map<String, List<String>> liveDocumentRefsToRenditionRefs) { 449 List<String> liveDocumentRefs = new ArrayList<>(liveDocumentRefsToRenditionRefs.keySet()); 450 if (liveDocumentRefs.isEmpty()) { 451 // no more document to check 452 return; 453 } 454 455 int processedSourceIds = 0; 456 while (processedSourceIds < liveDocumentRefs.size()) { 457 // compute the batch of source ids to check for existence 458 int limit = processedSourceIds + BATCH_SIZE > liveDocumentRefs.size() ? liveDocumentRefs.size() 459 : processedSourceIds + BATCH_SIZE; 460 List<String> batchSourceIds = liveDocumentRefs.subList(processedSourceIds, limit); 461 462 // retrieve still existing documents 463 List<String> existingSourceIds = new ArrayList<>(); 464 String query = NXQLQueryBuilder.getQuery("SELECT ecm:uuid FROM Document WHERE ecm:uuid IN ?", 465 new Object[] { batchSourceIds }, true, true, null); 466 try (IterableQueryResult result = session.queryAndFetch(query, NXQL.NXQL)) { 467 result.forEach(res -> existingSourceIds.add(res.get(NXQL.ECM_UUID).toString())); 468 } 469 batchSourceIds.removeAll(existingSourceIds); 470 471 List<String> renditionRefsToDelete = batchSourceIds.stream() 472 .map(liveDocumentRefsToRenditionRefs::get) 473 .reduce(new ArrayList<>(), (allRefs, refs) -> { 474 allRefs.addAll(refs); 475 return allRefs; 476 }); 477 478 if (!renditionRefsToDelete.isEmpty()) { 479 session.removeDocuments(renditionRefsToDelete.stream().map(IdRef::new).toArray(DocumentRef[]::new)); 480 } 481 482 if (TransactionHelper.isTransactionActiveOrMarkedRollback()) { 483 TransactionHelper.commitOrRollbackTransaction(); 484 TransactionHelper.startTransaction(); 485 } 486 487 // next batch 488 processedSourceIds += BATCH_SIZE; 489 } 490 } 491 } 492 493 @Override 494 public Rendition getDefaultRendition(DocumentModel doc, String reason, Map<String, Serializable> extendedInfos) { 495 return getDefaultRendition(doc, reason, false, extendedInfos); 496 } 497 498 @Override 499 public Rendition getDefaultRendition(DocumentModel doc, String reason, boolean store, 500 Map<String, Serializable> extendedInfos) { 501 Map<String, Object> context = new HashMap<>(); 502 Map<String, Serializable> ei = extendedInfos == null ? Collections.emptyMap() : extendedInfos; 503 NuxeoPrincipal currentUser = NuxeoPrincipal.getCurrent(); 504 context.put("Document", doc); 505 context.put("Infos", ei); 506 context.put("CurrentUser", currentUser); 507 ScriptEngine engine = null; 508 for (int i = defaultRenditionDescriptors.size() - 1; i >= 0; i--) { 509 DefaultRenditionDescriptor desc = defaultRenditionDescriptors.get(i); 510 if ((StringUtils.isEmpty(reason) && StringUtils.isEmpty(desc.reason)) 511 || (reason != null && reason.equals(desc.reason))) { 512 String scriptLanguage = desc.getScriptLanguage(); 513 if (engine == null || !engine.getFactory().getNames().contains(scriptLanguage)) { 514 // Instantiating an engine may be costly, let's keep previous one if same language 515 engine = scriptEngineManager.getEngineByName(scriptLanguage); 516 if (engine == null) { 517 throw new NuxeoException("Engine not found for language: " + scriptLanguage); 518 } 519 } 520 if (!(engine instanceof Invocable)) { 521 throw new NuxeoException( 522 "Engine " + engine.getClass().getName() + " not Invocable for language: " + scriptLanguage); 523 } 524 try { 525 engine.eval(desc.getScript()); 526 engine.getBindings(ScriptContext.ENGINE_SCOPE).putAll(context); 527 Object result = ((Invocable) engine).invokeFunction("run"); 528 if (result == null && desc.override) { 529 break; 530 } else { 531 String defaultRenditionName = (String) result; 532 if (!StringUtils.isBlank(defaultRenditionName)) { 533 Optional<Rendition> rendition = getRenditionSafe(doc, defaultRenditionName, store); 534 if (rendition.isPresent()) { 535 return rendition.get(); 536 } 537 } 538 } 539 540 } catch (NoSuchMethodException e) { 541 throw new NuxeoException("Script does not contain function: run() in defaultRendition: ", e); 542 } catch (ScriptException e) { 543 log.error("Failed to evaluate script: ", e); 544 } 545 } 546 } 547 log.warn("Failed to get rendition name for reason {}", reason); 548 return null; 549 } 550 551 @Override 552 public DocumentModel publishRendition(DocumentModel doc, DocumentModel target, String renditionName, 553 boolean override) { 554 CoreSession session = doc.getCoreSession(); 555 if (!session.hasPermission(target.getRef(), ADD_CHILDREN)) { 556 log.error("Permission '{}' is not granted to '{}' on document '{}'", ADD_CHILDREN, 557 session.getPrincipal().getName(), target.getPath()); 558 throw new DocumentSecurityException( 559 "Privilege '" + ADD_CHILDREN + "' is not granted to '" + session.getPrincipal().getName() + "'"); 560 } 561 Rendition rendition = StringUtils.isEmpty(renditionName) 562 ? getDefaultRendition(doc, Constants.DEFAULT_RENDTION_PUBLISH_REASON, true, null) 563 : getRendition(doc, renditionName, true); 564 if (rendition == null) { 565 throw new NuxeoException("Unable to render the document"); 566 } 567 DocumentModel renditionDocument = rendition.getHostDocument(); 568 /* 569 * We've checked above that the current user is allowed to add new documents in the target. We need the 570 * privileged session to publish the rendition which is a placeless document. 571 */ 572 DocumentRef publishedDocumentRef = CoreInstance.doPrivileged(session, 573 (CoreSession s) -> s.publishDocument(renditionDocument, target, override).getRef()); 574 DocumentModel publishedDocument = session.getDocument(publishedDocumentRef); 575 if (override) { 576 RenditionsRemover remover = new RenditionsRemover(publishedDocument); 577 remover.runUnrestricted(); 578 } 579 return publishedDocument; 580 } 581 582}