diff --git a/repository/src/main/java/org/alfresco/repo/rendition2/RenditionService2.java b/repository/src/main/java/org/alfresco/repo/rendition2/RenditionService2.java
index bd7d4c3b7f..e2485fcdbc 100644
--- a/repository/src/main/java/org/alfresco/repo/rendition2/RenditionService2.java
+++ b/repository/src/main/java/org/alfresco/repo/rendition2/RenditionService2.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
- * Copyright (C) 2005 - 2022 Alfresco Software Limited
+ * Copyright (C) 2005 - 2025 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -32,28 +32,20 @@ import org.alfresco.service.cmr.repository.NodeRef;
import java.util.List;
/**
- * The Async Rendition service. Replaces the original rendition services which included synchronous renditions and
- * asynchronous methods with Java call backs.
+ * The Async Rendition service. Replaces the original rendition services which included synchronous renditions and asynchronous methods with Java call backs.
+ *
*
- * Renditions are defined as {@link RenditionDefinition2}s and may be registered and looked by the associated
- * {@link RenditionDefinitionRegistry2}.
+ * Renditions are defined as {@link RenditionDefinition2}s and may be registered and looked by the associated {@link RenditionDefinitionRegistry2}.
+ *
*
* Unlike the original RenditionService this service, it:
*
- * - Performs async renditions without a Java callback, as another node in the cluster may complete the rendition.
- * The current node requests a transform, but another node might take the resulting transform and turn it into a
- * rendition if the external Transform Service is used.
- * - Reduces the configurable options to do with with the associations of rendition nodes, their type. They
- * are identical to 'hidden' (not normally seen as nodes in their own right in a
- * UI) renditions produced by the original service. So, they are always directly under the source node connected by a
- * {@code}rn:rendition{@code} association with the name of the rendition.
- * - The rendition nodes additionally have a {@code}rn:rendition2{@code} aspect and a {@code}contentUrlHashCode{@code}
- * property. This property contains a value that allows the service to work out if it holds a rendition of the
- * source node's current content.
- * - Failures are handled by setting the rendition node's content to null.
- * - When a rendition is requested via the REST API, only the newer service is used.
- * - Where possible old service renditions migrate automatically over to the new service when content on a
- * source node is updated.
+ * * - Performs async renditions without a Java callback, as another node in the cluster may complete the rendition. The current node requests a transform, but another node might take the resulting transform and turn it into a rendition if the external Transform Service is used.
+ * * - Reduces the configurable options to do with with the associations of rendition nodes, their type. They are identical to 'hidden' (not normally seen as nodes in their own right in a UI) renditions produced by the original service. So, they are always directly under the source node connected by a {@code}rn:rendition{@code} association with the name of the rendition.
+ * * - The rendition nodes additionally have a {@code}rn:rendition2{@code} aspect and a {@code}contentUrlHashCode{@code} property. This property contains a value that allows the service to work out if it holds a rendition of the source node's current content.
+ * * - Failures are handled by setting the rendition node's content to null.
+ * * - When a rendition is requested via the REST API, only the newer service is used.
+ * * - Where possible old service renditions migrate automatically over to the new service when content on a source node is updated.
*
*
* @author adavis
@@ -66,29 +58,30 @@ public interface RenditionService2
RenditionDefinitionRegistry2 getRenditionDefinitionRegistry2();
/**
- * This method asynchronously transforms content to a target mimetype with transform options supplied in the
- * {@code transformDefinition}. A response is set on a message queue once the transform is complete or fails,
- * together with some client supplied data. The response queue and client data are also included in the
- * transformDefinition.
+ * This method asynchronously transforms content to a target mimetype with transform options supplied in the {@code transformDefinition}. A response is set on a message queue once the transform is complete or fails, together with some client supplied data. The response queue and client data are also included in the transformDefinition.
+ *
*
- * This method does not create a rendition node, but uses the same code as renditions to perform the transform. The
- * {@code transformDefinition} extends {@link RenditionDefinition2}, but is not stored in a
- * {@link RenditionDefinitionRegistry2}, as it is transient in nature.
+ * This method does not create a rendition node, but uses the same code as renditions to perform the transform. The {@code transformDefinition} extends {@link RenditionDefinition2}, but is not stored in a {@link RenditionDefinitionRegistry2}, as it is transient in nature.
*
- * @param sourceNodeRef the node from which the content is retrieved.
- * @param transformDefinition which defines the transform, where to sent the response and some client specified data.
- * @throws UnsupportedOperationException if the transform is not supported.
+ * @param sourceNodeRef
+ * the node from which the content is retrieved.
+ * @param transformDefinition
+ * which defines the transform, where to sent the response and some client specified data.
+ * @throws UnsupportedOperationException
+ * if the transform is not supported.
*/
@NotAuditable
public void transform(NodeRef sourceNodeRef, TransformDefinition transformDefinition);
/**
- * This method asynchronously renders content as specified by the {@code renditionName}. The content to be
- * rendered is provided by {@code sourceNodeRef}.
+ * This method asynchronously renders content as specified by the {@code renditionName}. The content to be rendered is provided by {@code sourceNodeRef}.
*
- * @param sourceNodeRef the node from which the content is retrieved.
- * @param renditionName the rendition to be performed.
- * @throws UnsupportedOperationException if the transform is not supported AND the rendition has not been created before.
+ * @param sourceNodeRef
+ * the node from which the content is retrieved.
+ * @param renditionName
+ * the rendition to be performed.
+ * @throws UnsupportedOperationException
+ * if the transform is not supported AND the rendition has not been created before.
*/
@NotAuditable
public void render(NodeRef sourceNodeRef, String renditionName);
@@ -104,10 +97,11 @@ public interface RenditionService2
/**
* This method gets the rendition of the {@code sourceNodeRef} identified by its name.
*
- * @param sourceNodeRef the source node for the renditions
- * @param renditionName the renditionName used to identify a rendition.
- * @return the {@link ChildAssociationRef} which links the source node to the
- * rendition or null
if there is no rendition or it is not up to date.
+ * @param sourceNodeRef
+ * the source node for the renditions
+ * @param renditionName
+ * the renditionName used to identify a rendition.
+ * @return the {@link ChildAssociationRef} which links the source node to the rendition or null
if there is no rendition or it is not up to date.
*/
@NotAuditable
ChildAssociationRef getRenditionByName(NodeRef sourceNodeRef, String renditionName);
@@ -115,7 +109,8 @@ public interface RenditionService2
/**
* This method clears source nodeRef rendition content and content hash code using supplied rendition name.
*
- * @param renditionNode the rendition node
+ * @param renditionNode
+ * the rendition node
*/
@NotAuditable
void clearRenditionContentDataInTransaction(NodeRef renditionNode);
@@ -124,4 +119,14 @@ public interface RenditionService2
* Indicates if renditions are enabled. Set using the {@code system.thumbnail.generate} value.
*/
boolean isEnabled();
+
+ /**
+ * This method forces the content hash code for every {@code sourceNodeRef} renditions.
+ *
+ * @param sourceNodeRef
+ * the source node to update renditions hash code
+ */
+ @NotAuditable
+ void forceRenditionsContentHashCode(NodeRef sourceNodeRef);
+
}
\ No newline at end of file
diff --git a/repository/src/main/java/org/alfresco/repo/rendition2/RenditionService2Impl.java b/repository/src/main/java/org/alfresco/repo/rendition2/RenditionService2Impl.java
index 4de95b295c..c0605c0b9f 100644
--- a/repository/src/main/java/org/alfresco/repo/rendition2/RenditionService2Impl.java
+++ b/repository/src/main/java/org/alfresco/repo/rendition2/RenditionService2Impl.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
- * Copyright (C) 2005 - 2022 Alfresco Software Limited
+ * Copyright (C) 2005 - 2025 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -217,8 +217,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
@Override
public void transform(NodeRef sourceNodeRef, TransformDefinition transformDefinition)
{
- requestAsyncTransformOrRendition(sourceNodeRef, new RenderOrTransformCallBack()
- {
+ requestAsyncTransformOrRendition(sourceNodeRef, new RenderOrTransformCallBack() {
@Override
public String getName()
{
@@ -237,8 +236,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
@Override
public void render(NodeRef sourceNodeRef, String renditionName)
{
- requestAsyncTransformOrRendition(sourceNodeRef, new RenderOrTransformCallBack()
- {
+ requestAsyncTransformOrRendition(sourceNodeRef, new RenderOrTransformCallBack() {
@Override
public String getName()
{
@@ -277,7 +275,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
int renditionContentHashCode = getRenditionContentHashCode(renditionNode);
if (logger.isDebugEnabled())
{
- logger.debug(getName() + ": Source " + sourceContentHashCode + " rendition " + renditionContentHashCode+ " hashCodes");
+ logger.debug(getName() + ": Source " + sourceContentHashCode + " rendition " + renditionContentHashCode + " hashCodes");
}
if (renditionContentHashCode == sourceContentHashCode)
{
@@ -299,14 +297,14 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
if (!nodeService.exists(sourceNodeRef))
{
- throw new IllegalArgumentException(renderOrTransform.getName()+ ": The supplied sourceNodeRef "+sourceNodeRef+" does not exist.");
+ throw new IllegalArgumentException(renderOrTransform.getName() + ": The supplied sourceNodeRef " + sourceNodeRef + " does not exist.");
}
RenditionDefinition2 renditionDefinition = renderOrTransform.getRenditionDefinition();
if (logger.isDebugEnabled())
{
- logger.debug(renderOrTransform.getName()+ ": transform " +sourceNodeRef);
+ logger.debug(renderOrTransform.getName() + ": transform " + sourceNodeRef);
}
AtomicBoolean supported = new AtomicBoolean(true);
@@ -328,14 +326,13 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
}
String user = AuthenticationUtil.getRunAsUser();
- RetryingTransactionHelper.RetryingTransactionCallback callback = () ->
- {
+ RetryingTransactionHelper.RetryingTransactionCallback callback = () -> {
int sourceContentHashCode = getSourceContentHashCode(sourceNodeRef);
if (!supported.get())
{
if (logger.isDebugEnabled())
{
- logger.debug(renderOrTransform.getName() +" is not supported. " +
+ logger.debug(renderOrTransform.getName() + " is not supported. " +
"The content might be too big or the source mimetype cannot be converted.");
}
failure(sourceNodeRef, renditionDefinition, sourceContentHashCode);
@@ -372,12 +369,10 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
public void failure(NodeRef sourceNodeRef, RenditionDefinition2 renditionDefinition, int transformContentHashCode)
{
// The original transaction may have already have failed
- AuthenticationUtil.runAsSystem((AuthenticationUtil.RunAsWork) () ->
- transactionService.getRetryingTransactionHelper().doInTransaction(() ->
- {
- consume(sourceNodeRef, null, renditionDefinition, transformContentHashCode);
- return null;
- }, false, true));
+ AuthenticationUtil.runAsSystem((AuthenticationUtil.RunAsWork) () -> transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
+ consume(sourceNodeRef, null, renditionDefinition, transformContentHashCode);
+ return null;
+ }, false, true));
}
public void consume(NodeRef sourceNodeRef, InputStream transformInputStream, RenditionDefinition2 renditionDefinition,
@@ -391,7 +386,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
if (renditionDefinition instanceof TransformDefinition)
{
- TransformDefinition transformDefinition = (TransformDefinition)renditionDefinition;
+ TransformDefinition transformDefinition = (TransformDefinition) renditionDefinition;
String targetMimetype = transformDefinition.getTargetMimetype();
if (AsynchronousExtractor.isMetadataExtractMimetype(targetMimetype))
{
@@ -484,9 +479,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
}
/**
- * Takes a transformation (InputStream) and attaches it as a rendition to the source node.
- * Does nothing if there is already a newer rendition.
- * If the transformInputStream is null, this is taken to be a transform failure.
+ * Takes a transformation (InputStream) and attaches it as a rendition to the source node. Does nothing if there is already a newer rendition. If the transformInputStream is null, this is taken to be a transform failure.
*/
private void consumeRendition(NodeRef sourceNodeRef, int sourceContentHashCode, InputStream transformInputStream,
RenditionDefinition2 renditionDefinition, int transformContentHashCode)
@@ -507,93 +500,92 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
(transformInputStream == null ? " to null as the transform failed" : " to the transform result"));
}
- AuthenticationUtil.runAsSystem((AuthenticationUtil.RunAsWork) () ->
- transactionService.getRetryingTransactionHelper().doInTransaction(() ->
+ AuthenticationUtil.runAsSystem((AuthenticationUtil.RunAsWork) () -> transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
+ // Ensure that the creation of a rendition does not cause updates to the modified, modifier properties on the source node
+ NodeRef renditionNode = getRenditionNode(sourceNodeRef, renditionName);
+ boolean createRenditionNode = renditionNode == null;
+ boolean sourceHasAspectRenditioned = nodeService.hasAspect(sourceNodeRef, RenditionModel.ASPECT_RENDITIONED);
+ try
+ {
+ ruleService.disableRuleType(RuleType.UPDATE);
+ behaviourFilter.disableBehaviour(sourceNodeRef, ContentModel.ASPECT_AUDITABLE);
+ behaviourFilter.disableBehaviour(sourceNodeRef, ContentModel.ASPECT_VERSIONABLE);
+
+ // If they do not exist create the rendition association and the rendition node.
+ if (createRenditionNode)
+ {
+ renditionNode = createRenditionNode(sourceNodeRef, renditionDefinition);
+ }
+ else if (!nodeService.hasAspect(renditionNode, RenditionModel.ASPECT_RENDITION2))
+ {
+ nodeService.addAspect(renditionNode, RenditionModel.ASPECT_RENDITION2, null);
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Added rendition2 aspect to rendition " + renditionName + " on " + sourceNodeRef);
+ }
+ }
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Set ThumbnailLastModified for " + renditionName);
+ }
+ setThumbnailLastModified(sourceNodeRef, renditionName);
+
+ if (transformInputStream != null)
{
- // Ensure that the creation of a rendition does not cause updates to the modified, modifier properties on the source node
- NodeRef renditionNode = getRenditionNode(sourceNodeRef, renditionName);
- boolean createRenditionNode = renditionNode == null;
- boolean sourceHasAspectRenditioned = nodeService.hasAspect(sourceNodeRef, RenditionModel.ASPECT_RENDITIONED);
try
{
- ruleService.disableRuleType(RuleType.UPDATE);
- behaviourFilter.disableBehaviour(sourceNodeRef, ContentModel.ASPECT_AUDITABLE);
- behaviourFilter.disableBehaviour(sourceNodeRef, ContentModel.ASPECT_VERSIONABLE);
+ // Set or replace rendition content
+ ContentWriter contentWriter = contentService.getWriter(renditionNode, DEFAULT_RENDITION_CONTENT_PROP, true);
+ String targetMimetype = renditionDefinition.getTargetMimetype();
+ contentWriter.setMimetype(targetMimetype);
+ contentWriter.setEncoding(DEFAULT_ENCODING);
+ ContentWriter renditionWriter = contentWriter;
+ renditionWriter.putContent(transformInputStream);
- // If they do not exist create the rendition association and the rendition node.
- if (createRenditionNode)
+ ContentReader contentReader = renditionWriter.getReader();
+ long sizeOfRendition = contentReader.getSize();
+ if (sizeOfRendition > 0L)
{
- renditionNode = createRenditionNode(sourceNodeRef, renditionDefinition);
- }
- else if (!nodeService.hasAspect(renditionNode, RenditionModel.ASPECT_RENDITION2))
- {
- nodeService.addAspect(renditionNode, RenditionModel.ASPECT_RENDITION2, null);
if (logger.isDebugEnabled())
{
- logger.debug("Added rendition2 aspect to rendition " + renditionName + " on " + sourceNodeRef);
- }
- }
- if (logger.isDebugEnabled())
- {
- logger.debug("Set ThumbnailLastModified for " + renditionName);
- }
- setThumbnailLastModified(sourceNodeRef, renditionName);
-
- if (transformInputStream != null)
- {
- try
- {
- // Set or replace rendition content
- ContentWriter contentWriter = contentService.getWriter(renditionNode, DEFAULT_RENDITION_CONTENT_PROP, true);
- String targetMimetype = renditionDefinition.getTargetMimetype();
- contentWriter.setMimetype(targetMimetype);
- contentWriter.setEncoding(DEFAULT_ENCODING);
- ContentWriter renditionWriter = contentWriter;
- renditionWriter.putContent(transformInputStream);
-
- ContentReader contentReader = renditionWriter.getReader();
- long sizeOfRendition = contentReader.getSize();
- if (sizeOfRendition > 0L)
- {
- if (logger.isDebugEnabled()) {
- logger.debug("Set rendition hashcode for " + renditionName);
- }
- nodeService.setProperty(renditionNode, RenditionModel.PROP_RENDITION_CONTENT_HASH_CODE, transformContentHashCode);
- }
- else
- {
- logger.error("Transform was zero bytes for " + renditionName + " on " + sourceNodeRef);
- clearRenditionContentData(renditionNode);
- }
- }
- catch (Exception e)
- {
- logger.error("Failed to copy transform InputStream into rendition " + renditionName + " on " + sourceNodeRef);
- throw e;
+ logger.debug("Set rendition hashcode for " + renditionName);
}
+ nodeService.setProperty(renditionNode, RenditionModel.PROP_RENDITION_CONTENT_HASH_CODE, transformContentHashCode);
}
else
{
+ logger.error("Transform was zero bytes for " + renditionName + " on " + sourceNodeRef);
clearRenditionContentData(renditionNode);
}
-
- if (!sourceHasAspectRenditioned)
- {
- nodeService.addAspect(sourceNodeRef, RenditionModel.ASPECT_RENDITIONED, null);
- }
}
catch (Exception e)
{
- throw new RenditionService2Exception(TRANSFORMING_ERROR_MESSAGE + e.getMessage(), e);
+ logger.error("Failed to copy transform InputStream into rendition " + renditionName + " on " + sourceNodeRef);
+ throw e;
}
- finally
- {
- behaviourFilter.enableBehaviour(sourceNodeRef, ContentModel.ASPECT_AUDITABLE);
- behaviourFilter.enableBehaviour(sourceNodeRef, ContentModel.ASPECT_VERSIONABLE);
- ruleService.enableRuleType(RuleType.UPDATE);
- }
- return null;
- }, false, true));
+ }
+ else
+ {
+ clearRenditionContentData(renditionNode);
+ }
+
+ if (!sourceHasAspectRenditioned)
+ {
+ nodeService.addAspect(sourceNodeRef, RenditionModel.ASPECT_RENDITIONED, null);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new RenditionService2Exception(TRANSFORMING_ERROR_MESSAGE + e.getMessage(), e);
+ }
+ finally
+ {
+ behaviourFilter.enableBehaviour(sourceNodeRef, ContentModel.ASPECT_AUDITABLE);
+ behaviourFilter.enableBehaviour(sourceNodeRef, ContentModel.ASPECT_VERSIONABLE);
+ ruleService.enableRuleType(RuleType.UPDATE);
+ }
+ return null;
+ }, false, true));
}
}
@@ -634,14 +626,14 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
if (logger.isTraceEnabled())
{
- logger.trace("Setting thumbnail last modified date to " + lastModifiedValue +" on source node: " + sourceNodeRef);
+ logger.trace("Setting thumbnail last modified date to " + lastModifiedValue + " on source node: " + sourceNodeRef);
}
if (nodeService.hasAspect(sourceNodeRef, ContentModel.ASPECT_THUMBNAIL_MODIFICATION))
{
List thumbnailMods = (List) nodeService.getProperty(sourceNodeRef, ContentModel.PROP_LAST_THUMBNAIL_MODIFICATION_DATA);
String target = null;
- for (String currThumbnailMod: thumbnailMods)
+ for (String currThumbnailMod : thumbnailMods)
{
if (currThumbnailMod.startsWith(prefix))
{
@@ -665,8 +657,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
}
/**
- * Returns the hash code of the source node's content url. As transformations may be returned in a different
- * sequences to which they were requested, this is used work out if a rendition should be replaced.
+ * Returns the hash code of the source node's content url. As transformations may be returned in a different sequences to which they were requested, this is used work out if a rendition should be replaced.
*/
private int getSourceContentHashCode(NodeRef sourceNodeRef)
{
@@ -675,7 +666,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
if (contentData != null)
{
// Originally we used the contentData URL, but that is not enough if the mimetype changes.
- String contentString = contentData.getContentUrl()+contentData.getMimetype();
+ String contentString = contentData.getContentUrl() + contentData.getMimetype();
if (contentString != null)
{
hashCode = contentString.hashCode();
@@ -685,13 +676,11 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
}
/**
- * Returns the hash code of source node's content url on the rendition node (node may be null) if it does not exist.
- * Used work out if a rendition should be replaced. {@code -2} is returned if the rendition does not exist or was
- * not created by RenditionService2. {@code -1} is returned if there was no source content or the rendition failed.
+ * Returns the hash code of source node's content url on the rendition node (node may be null) if it does not exist. Used work out if a rendition should be replaced. {@code -2} is returned if the rendition does not exist or was not created by RenditionService2. {@code -1} is returned if there was no source content or the rendition failed.
*/
private int getRenditionContentHashCode(NodeRef renditionNode)
{
- if ( renditionNode == null || !nodeService.hasAspect(renditionNode, RenditionModel.ASPECT_RENDITION2))
+ if (renditionNode == null || !nodeService.hasAspect(renditionNode, RenditionModel.ASPECT_RENDITION2))
{
return RENDITION2_DOES_NOT_EXIST;
}
@@ -699,7 +688,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
Serializable hashCode = nodeService.getProperty(renditionNode, PROP_RENDITION_CONTENT_HASH_CODE);
return hashCode == null
? SOURCE_HAS_NO_CONTENT
- : (int)hashCode;
+ : (int) hashCode;
}
private NodeRef getRenditionNode(NodeRef sourceNodeRef, String renditionName)
@@ -773,11 +762,12 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
}
/**
- * This method checks whether the specified source node is of a content class which has been registered for
- * rendition prevention.
+ * This method checks whether the specified source node is of a content class which has been registered for rendition prevention.
*
- * @param sourceNode the node to check.
- * @throws RenditionService2PreventedException if the source node is configured for rendition prevention.
+ * @param sourceNode
+ * the node to check.
+ * @throws RenditionService2PreventedException
+ * if the source node is configured for rendition prevention.
*/
// This code is based on the old RenditionServiceImpl.checkSourceNodeForPreventionClass(...)
private void checkSourceNodeForPreventionClass(NodeRef sourceNode)
@@ -833,8 +823,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
}
/**
- * Indicates if the rendition is available. Failed renditions (there was an error) don't have a contentUrl
- * and out of date renditions or those still being created don't have a matching contentHashCode.
+ * Indicates if the rendition is available. Failed renditions (there was an error) don't have a contentUrl and out of date renditions or those still being created don't have a matching contentHashCode.
*/
public boolean isRenditionAvailable(NodeRef sourceNodeRef, NodeRef renditionNode)
{
@@ -852,7 +841,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
int renditionContentHashCode = getRenditionContentHashCode(renditionNode);
if (logger.isDebugEnabled())
{
- logger.debug("isRenditionAvailable source " + sourceContentHashCode + " and rendition " + renditionContentHashCode+" hashcodes");
+ logger.debug("isRenditionAvailable source " + sourceContentHashCode + " and rendition " + renditionContentHashCode + " hashcodes");
}
if (sourceContentHashCode != renditionContentHashCode)
{
@@ -892,19 +881,17 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
}
ChildAssociationRef childAssoc = renditions.get(0);
NodeRef renditionNode = childAssoc.getChildRef();
- return !isRenditionAvailable(sourceNodeRef, renditionNode) ? null: childAssoc;
+ return !isRenditionAvailable(sourceNodeRef, renditionNode) ? null : childAssoc;
}
}
@Override
public void clearRenditionContentDataInTransaction(NodeRef renditionNode)
{
- AuthenticationUtil.runAsSystem((AuthenticationUtil.RunAsWork) () ->
- transactionService.getRetryingTransactionHelper().doInTransaction(() ->
- {
- clearRenditionContentData(renditionNode);
- return null;
- }, false, true));
+ AuthenticationUtil.runAsSystem((AuthenticationUtil.RunAsWork) () -> transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
+ clearRenditionContentData(renditionNode);
+ return null;
+ }, false, true));
}
@Override
@@ -913,6 +900,38 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
return enabled && thumbnailsEnabled;
}
+ @Override
+ public void forceRenditionsContentHashCode(NodeRef sourceNodeRef)
+ {
+ if (sourceNodeRef != null && nodeService.exists(sourceNodeRef))
+ {
+ List renditions = getRenditionChildAssociations(sourceNodeRef);
+ if (renditions != null)
+ {
+ int sourceContentHashCode = getSourceContentHashCode(sourceNodeRef);
+ for (ChildAssociationRef rendition : renditions)
+ {
+ NodeRef renditionNode = rendition.getChildRef();
+ if (nodeService.hasAspect(renditionNode, RenditionModel.ASPECT_RENDITION2))
+ {
+ int renditionContentHashCode = getRenditionContentHashCode(renditionNode);
+ String renditionName = rendition.getQName().getLocalName();
+ if (sourceContentHashCode != renditionContentHashCode)
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Update content hash code for rendition " + renditionName + " of node "
+ + sourceNodeRef);
+ }
+ nodeService.setProperty(renditionNode, PROP_RENDITION_CONTENT_HASH_CODE,
+ sourceContentHashCode);
+ }
+ }
+ }
+ }
+ }
+ }
+
@Override
public void onContentUpdate(NodeRef sourceNodeRef, boolean newContent)
{
@@ -950,4 +969,6 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
}
}
+
+
}
diff --git a/repository/src/test/java/org/alfresco/repo/rendition2/AbstractRenditionIntegrationTest.java b/repository/src/test/java/org/alfresco/repo/rendition2/AbstractRenditionIntegrationTest.java
index 2b8826659c..02be165a76 100644
--- a/repository/src/test/java/org/alfresco/repo/rendition2/AbstractRenditionIntegrationTest.java
+++ b/repository/src/test/java/org/alfresco/repo/rendition2/AbstractRenditionIntegrationTest.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
- * Copyright (C) 2005 - 2022 Alfresco Software Limited
+ * Copyright (C) 2005 - 2025 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -44,14 +44,8 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.thumbnail.ThumbnailRegistry;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.service.cmr.rendition.RenditionService;
-import org.alfresco.service.cmr.repository.ChildAssociationRef;
-import org.alfresco.service.cmr.repository.ContentReader;
-import org.alfresco.service.cmr.repository.ContentService;
-import org.alfresco.service.cmr.repository.ContentWriter;
-import org.alfresco.service.cmr.repository.MimetypeService;
-import org.alfresco.service.cmr.repository.NodeRef;
-import org.alfresco.service.cmr.repository.NodeService;
-import org.alfresco.service.cmr.repository.StoreRef;
+import org.alfresco.service.cmr.repository.*;
+import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.security.MutableAuthenticationService;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.security.PersonService;
@@ -128,6 +122,7 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
protected static final String ADMIN = "admin";
protected static final String DOC_LIB = "doclib";
+ protected static final String PDF = "pdf";
private CronExpression origLocalTransCron;
private CronExpression origRenditionCron;
@@ -152,7 +147,7 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
// Strict MimetypeCheck
System.setProperty("transformer.strict.mimetype.check", "true");
- // Retry on DifferentMimetype
+ // Retry on DifferentMimetype
System.setProperty("content.transformer.retryOn.different.mimetype", "true");
}
@@ -181,7 +176,7 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
if (transformServiceRegistry instanceof LocalTransformServiceRegistry)
{
- ((LocalTransformServiceRegistry)transformServiceRegistry).setEnabled(localTransformServiceEnabled);
+ ((LocalTransformServiceRegistry) transformServiceRegistry).setEnabled(localTransformServiceEnabled);
}
thumbnailRegistry.setTransformServiceRegistry(transformServiceRegistry);
@@ -257,9 +252,7 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
// Creates a new source node as the given user in its own transaction.
protected NodeRef createSource(String user, String testFileName)
{
- return AuthenticationUtil.runAs(() ->
- transactionService.getRetryingTransactionHelper().doInTransaction(() ->
- createSource(testFileName)), user);
+ return AuthenticationUtil.runAs(() -> transactionService.getRetryingTransactionHelper().doInTransaction(() -> createSource(testFileName)), user);
}
// Creates a new source node as the current user in the current transaction.
@@ -271,12 +264,10 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
// Changes the content of a source node as the given user in its own transaction.
protected void updateContent(String user, NodeRef sourceNodeRef, String testFileName)
{
- AuthenticationUtil.runAs((AuthenticationUtil.RunAsWork) () ->
- transactionService.getRetryingTransactionHelper().doInTransaction(() ->
- {
- updateContent(sourceNodeRef, testFileName);
- return null;
- }), user);
+ AuthenticationUtil.runAs((AuthenticationUtil.RunAsWork) () -> transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
+ updateContent(sourceNodeRef, testFileName);
+ return null;
+ }), user);
}
// Changes the content of a source node as the current user in the current transaction.
@@ -295,12 +286,10 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
// Clears the content of a source node as the given user in its own transaction.
protected void clearContent(String user, NodeRef sourceNodeRef)
{
- AuthenticationUtil.runAs((AuthenticationUtil.RunAsWork) () ->
- transactionService.getRetryingTransactionHelper().doInTransaction(() ->
- {
- clearContent(sourceNodeRef);
- return null;
- }), user);
+ AuthenticationUtil.runAs((AuthenticationUtil.RunAsWork) () -> transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
+ clearContent(sourceNodeRef);
+ return null;
+ }), user);
}
// Clears the content of a source node as the current user in the current transaction.
@@ -312,23 +301,19 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
// Requests a new rendition as the given user in its own transaction.
protected void render(String user, NodeRef sourceNode, String renditionName)
{
- AuthenticationUtil.runAs((AuthenticationUtil.RunAsWork) () ->
- transactionService.getRetryingTransactionHelper().doInTransaction(() ->
- {
- render(sourceNode, renditionName);
- return null;
- }), user);
+ AuthenticationUtil.runAs((AuthenticationUtil.RunAsWork) () -> transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
+ render(sourceNode, renditionName);
+ return null;
+ }), user);
}
// Requests a new metadata extract as the given user in its own transaction.
protected void extract(String user, NodeRef sourceNode)
{
- AuthenticationUtil.runAs((AuthenticationUtil.RunAsWork) () ->
- transactionService.getRetryingTransactionHelper().doInTransaction(() ->
- {
- extract(sourceNode);
- return null;
- }), user);
+ AuthenticationUtil.runAs((AuthenticationUtil.RunAsWork) () -> transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
+ extract(sourceNode);
+ return null;
+ }), user);
}
// Requests a new rendition as the current user in the current transaction.
@@ -357,7 +342,7 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
Throwable cause = e.getCause();
if (cause instanceof AssertionFailedError)
{
- throw (AssertionFailedError)cause;
+ throw (AssertionFailedError) cause;
}
throw e;
}
@@ -375,7 +360,7 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
Throwable cause = e.getCause();
if (cause instanceof AssertionFailedError)
{
- throw (AssertionFailedError)cause;
+ throw (AssertionFailedError) cause;
}
throw e;
}
@@ -386,16 +371,15 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
{
long maxMillis = 10000;
ChildAssociationRef assoc = null;
- for (int i = (int)(maxMillis / 1000); i >= 0; i--)
+ for (int i = (int) (maxMillis / 1000); i >= 0; i--)
{
// Must create a new transaction in order to see changes that take place after this method started.
- assoc = transactionService.getRetryingTransactionHelper().doInTransaction(() ->
- renditionService2.getRenditionByName(sourceNodeRef, renditionName), true, true);
+ assoc = transactionService.getRetryingTransactionHelper().doInTransaction(() -> renditionService2.getRenditionByName(sourceNodeRef, renditionName), true, true);
if (assoc != null)
{
break;
}
- logger.debug("RenditionService2.getRenditionByName(...) sleep "+i);
+ logger.debug("RenditionService2.getRenditionByName(...) sleep " + i);
sleep(1000);
}
if (shouldExist)
@@ -415,11 +399,10 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
{
long maxMillis = 5000;
boolean nodeModified = true;
- for (int i = (int)(maxMillis / 1000); i >= 0; i--)
+ for (int i = (int) (maxMillis / 1000); i >= 0; i--)
{
// Must create a new transaction in order to see changes that take place after this method started.
- nodeModified = transactionService.getRetryingTransactionHelper().doInTransaction(() ->
- {
+ nodeModified = transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
Serializable created = nodeService.getProperty(sourceNodeRef, ContentModel.PROP_CREATED);
Serializable modified = nodeService.getProperty(sourceNodeRef, ContentModel.PROP_MODIFIED);
return !created.equals(modified);
@@ -428,7 +411,7 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
{
break;
}
- logger.debug("waitForExtract sleep "+i);
+ logger.debug("waitForExtract sleep " + i);
sleep(1000);
}
if (nodePropsShouldChange)
@@ -445,7 +428,7 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
protected String getTestFileName(String sourceMimetype) throws FileNotFoundException
{
String extension = mimetypeMap.getExtension(sourceMimetype);
- String testFileName = extension.equals(EXTENSION_BINARY) ? null : "quick."+extension;
+ String testFileName = extension.equals(EXTENSION_BINARY) ? null : "quick." + extension;
if (testFileName != null)
{
try
@@ -491,8 +474,7 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
String createRandomUser()
{
- return AuthenticationUtil.runAs(() ->
- {
+ return AuthenticationUtil.runAs(() -> {
String username = generateNewUsernameString();
createUser(username);
return username;
@@ -505,13 +487,12 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
}
void createUser(final String username,
- final String firstName,
- final String lastName,
- final String jobTitle,
- final long quota)
+ final String firstName,
+ final String lastName,
+ final String jobTitle,
+ final long quota)
{
- RetryingTransactionHelper.RetryingTransactionCallback createUserCallback = () ->
- {
+ RetryingTransactionHelper.RetryingTransactionCallback createUserCallback = () -> {
authenticationService.createAuthentication(username, PASSWORD.toCharArray());
PropertyMap personProperties = new PropertyMap();
@@ -519,7 +500,7 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
personProperties.put(ContentModel.PROP_AUTHORITY_DISPLAY_NAME, "title" + username);
personProperties.put(ContentModel.PROP_FIRSTNAME, firstName);
personProperties.put(ContentModel.PROP_LASTNAME, lastName);
- personProperties.put(ContentModel.PROP_EMAIL, username+"@example.com");
+ personProperties.put(ContentModel.PROP_EMAIL, username + "@example.com");
personProperties.put(ContentModel.PROP_JOBTITLE, jobTitle);
if (quota > 0)
{
@@ -548,14 +529,12 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
}
/**
- * Helper method which gets the content hash code from the supplied rendition node without specific validations (the
- * equivalent method from {@link RenditionService2Impl} is not exposed)
+ * Helper method which gets the content hash code from the supplied rendition node without specific validations (the equivalent method from {@link RenditionService2Impl} is not exposed)
*
* @param renditionNodeRef
* the rendition node
*
- * @return -1 in case of there is no content, -2 in case rendition doesn't exist, the actual content hash code
- * otherwise
+ @return -1 in case of there is no content, -2 in case rendition doesn't exist, the actual content hash code otherwise
*/
protected int getRenditionContentHashCode(NodeRef renditionNodeRef)
{
@@ -569,4 +548,27 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
return renditionContentHashCode;
}
+ /**
+ * Helper method which gets the content hash code from the supplied source node (the equivalent method from {@link RenditionService2Impl} is not public)
+ *
+ * @param sourceNodeRef
+ * the source node
+ *
+ * @return -1 in case of there is no content, otherwise, the actual content hash code otherwise
+ */
+ protected int getSourceContentHashCode(NodeRef sourceNodeRef)
+ {
+ int hashCode = -1;
+ ContentData contentData = DefaultTypeConverter.INSTANCE.convert(ContentData.class, nodeService.getProperty(sourceNodeRef, PROP_CONTENT));
+ if (contentData != null)
+ {
+ // Originally we used the contentData URL, but that is not enough if the mimetype changes.
+ String contentString = contentData.getContentUrl() + contentData.getMimetype();
+ if (contentString != null)
+ {
+ hashCode = contentString.hashCode();
+ }
+ }
+ return hashCode;
+ }
}
diff --git a/repository/src/test/java/org/alfresco/repo/rendition2/RenditionService2IntegrationTest.java b/repository/src/test/java/org/alfresco/repo/rendition2/RenditionService2IntegrationTest.java
index ec72dcfbb5..8dad21d3dd 100644
--- a/repository/src/test/java/org/alfresco/repo/rendition2/RenditionService2IntegrationTest.java
+++ b/repository/src/test/java/org/alfresco/repo/rendition2/RenditionService2IntegrationTest.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
- * Copyright (C) 2005 - 2022 Alfresco Software Limited
+ * Copyright (C) 2005 - 2025 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -158,10 +158,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
clearContent(ADMIN, sourceNodeRef);
render(ADMIN, sourceNodeRef, DOC_LIB);
- ChildAssociationRef assoc = AuthenticationUtil.runAs(() ->
- renditionService2.getRenditionByName(sourceNodeRef, DOC_LIB), ADMIN);
- waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, false);
- assertNull("There should be no rendition as there was no content", assoc);
+ ChildAssociationRef assoc = AuthenticationUtil.runAs(() -> renditionService2.getRenditionByName(sourceNodeRef, DOC_LIB), ADMIN);
}
@Test
@@ -190,8 +187,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
clearContent(ADMIN, sourceNodeRef);
render(ADMIN, sourceNodeRef, DOC_LIB);
- ChildAssociationRef assoc = AuthenticationUtil.runAs(() ->
- renditionService2.getRenditionByName(sourceNodeRef, DOC_LIB), ADMIN);
+ ChildAssociationRef assoc = AuthenticationUtil.runAs(() -> renditionService2.getRenditionByName(sourceNodeRef, DOC_LIB), ADMIN);
waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, false);
assertNull("There should be no rendition as there was no content", assoc);
@@ -216,8 +212,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
String ownerUserName = createRandomUser();
NodeRef sourceNodeRef = createSource(ownerUserName, "quick.jpg");
String otherUserName = createRandomUser();
- transactionService.getRetryingTransactionHelper().doInTransaction(() ->
- {
+ transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
permissionService.setPermission(sourceNodeRef, otherUserName, PermissionService.READ, true);
return null;
});
@@ -236,8 +231,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
String ownerUserName = createRandomUser();
NodeRef sourceNodeRef = createSource(ownerUserName, "quick.jpg");
String otherUserName = createRandomUser();
- transactionService.getRetryingTransactionHelper().doInTransaction(() ->
- {
+ transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
permissionService.setPermission(sourceNodeRef, otherUserName, PermissionService.READ, true);
return null;
});
@@ -257,8 +251,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
NodeRef sourceNodeRef = createSource(ownerUserName, "quick.jpg");
render(ownerUserName, sourceNodeRef, DOC_LIB);
String noPermissionsUser = createRandomUser();
- transactionService.getRetryingTransactionHelper().doInTransaction(() ->
- {
+ transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
permissionService.setPermission(sourceNodeRef, noPermissionsUser, PermissionService.ALL_PERMISSIONS, false);
return null;
});
@@ -280,12 +273,9 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
NodeRef sourceNodeRef = createSource(ownerUserName, "quick.jpg");
final QName doclibRendDefQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "doclib");
transactionService.getRetryingTransactionHelper()
- .doInTransaction(() ->
- AuthenticationUtil.runAs(() ->
- renditionService.render(sourceNodeRef, doclibRendDefQName), ownerUserName));
+ .doInTransaction(() -> AuthenticationUtil.runAs(() -> renditionService.render(sourceNodeRef, doclibRendDefQName), ownerUserName));
- NodeRef oldRendition = AuthenticationUtil.runAs(() ->
- renditionService.getRenditionByName(sourceNodeRef, doclibRendDefQName).getChildRef(), ownerUserName);
+ NodeRef oldRendition = AuthenticationUtil.runAs(() -> renditionService.getRenditionByName(sourceNodeRef, doclibRendDefQName).getChildRef(), ownerUserName);
assertFalse("The rendition should be generated by old Rendition Service",
AuthenticationUtil.runAs(() -> nodeService.hasAspect(oldRendition, RenditionModel.ASPECT_RENDITION2), ownerUserName));
@@ -335,12 +325,10 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
renditionService2.setEnabled(false);
// Call 'clearRenditionContentData' method directly to prove rendition content will be cleaned
- AuthenticationUtil.runAs((AuthenticationUtil.RunAsWork) () ->
- transactionService.getRetryingTransactionHelper().doInTransaction(() ->
- {
- renditionService2.clearRenditionContentData(sourceNodeRef, DOC_LIB);
- return null;
- }), ADMIN);
+ AuthenticationUtil.runAs((AuthenticationUtil.RunAsWork) () -> transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
+ renditionService2.clearRenditionContentData(sourceNodeRef, DOC_LIB);
+ return null;
+ }), ADMIN);
// The rendition should not have content by now
assertNull("Rendition has content", nodeService.getProperty(renditionNodeRef, ContentModel.PROP_CONTENT));
@@ -356,8 +344,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
/**
* Tests if a rendition without content (but with contentHashCode) can be generated again.
*
- * If the rendition consumption receives a null InputStream, the contentHashCode should be cleaned from the
- * rendition node, allowing new requests to generate the rendition.
+ * If the rendition consumption receives a null InputStream, the contentHashCode should be cleaned from the rendition node, allowing new requests to generate the rendition.
*
*/
@Test
@@ -369,8 +356,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
/**
* Tests if a rendition without content (but with contentHashCode) can be generated again.
*
- * If the rendition consumption receives a zero length InputStream, the contentHashCode should be cleaned from the
- * rendition node, allowing new requests to generate the rendition.
+ * If the rendition consumption receives a zero length InputStream, the contentHashCode should be cleaned from the rendition node, allowing new requests to generate the rendition.
*
*/
@Test
@@ -492,22 +478,16 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
NodeRef sourceNodeRef = createSource(ADMIN, "quick.jpg");
final QName doclibRendDefQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "doclib");
transactionService.getRetryingTransactionHelper()
- .doInTransaction(() ->
- AuthenticationUtil.runAs(() ->
- renditionService.render(sourceNodeRef, doclibRendDefQName), ADMIN));
+ .doInTransaction(() -> AuthenticationUtil.runAs(() -> renditionService.render(sourceNodeRef, doclibRendDefQName), ADMIN));
assertNotNull("The old renditions service did not render", waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true));
List lastThumbnailModification = transactionService.getRetryingTransactionHelper()
- .doInTransaction(() ->
- AuthenticationUtil.runAs(() ->
- (List) nodeService.getProperty(sourceNodeRef, ContentModel.PROP_LAST_THUMBNAIL_MODIFICATION_DATA), ADMIN));
+ .doInTransaction(() -> AuthenticationUtil.runAs(() -> (List) nodeService.getProperty(sourceNodeRef, ContentModel.PROP_LAST_THUMBNAIL_MODIFICATION_DATA), ADMIN));
updateContent(ADMIN, sourceNodeRef, "quick.png");
List newThumbnailModification = null;
for (int i = 0; i < 5; i++)
{
newThumbnailModification = transactionService.getRetryingTransactionHelper()
- .doInTransaction(() ->
- AuthenticationUtil.runAs(() ->
- (List) nodeService.getProperty(sourceNodeRef, ContentModel.PROP_LAST_THUMBNAIL_MODIFICATION_DATA), ADMIN));
+ .doInTransaction(() -> AuthenticationUtil.runAs(() -> (List) nodeService.getProperty(sourceNodeRef, ContentModel.PROP_LAST_THUMBNAIL_MODIFICATION_DATA), ADMIN));
if (!newThumbnailModification.equals(lastThumbnailModification))
{
break;
@@ -579,9 +559,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
NodeRef sourceNodeRef = createSource(ADMIN, "quick.jpg");
final QName doclibRendDefQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "doclib");
transactionService.getRetryingTransactionHelper()
- .doInTransaction(() ->
- AuthenticationUtil.runAs(() ->
- renditionService.render(sourceNodeRef, doclibRendDefQName), ADMIN));
+ .doInTransaction(() -> AuthenticationUtil.runAs(() -> renditionService.render(sourceNodeRef, doclibRendDefQName), ADMIN));
waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true);
renditionService2.setEnabled(true);
@@ -652,9 +630,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
NodeRef sourceNodeRef = createSource(ADMIN, "quick.jpg");
final QName doclibRendDefQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "doclib");
transactionService.getRetryingTransactionHelper()
- .doInTransaction(() ->
- AuthenticationUtil.runAs(() ->
- renditionService.render(sourceNodeRef, doclibRendDefQName), ADMIN));
+ .doInTransaction(() -> AuthenticationUtil.runAs(() -> renditionService.render(sourceNodeRef, doclibRendDefQName), ADMIN));
waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true);
renditionService2.setEnabled(true);
@@ -682,4 +658,60 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
renditionService2.setEnabled(true);
}
}
+
+ @Test
+ public void testForceRenditionsContentHashCode()
+ {
+
+ // Create a node
+ NodeRef sourceNodeRef = createSource(ADMIN, "quick.docx");
+ assertNotNull("Node not generated", sourceNodeRef);
+
+ // Get content hash code for the source node
+ int sourceNodeContentHashCode = getSourceContentHashCode(sourceNodeRef);
+
+ // Trigger the pdf rendition
+ render(ADMIN, sourceNodeRef, PDF);
+ NodeRef pdfRenditionNodeRef = waitForRendition(ADMIN, sourceNodeRef, PDF, true);
+ assertNotNull("pdf rendition was not generated", pdfRenditionNodeRef);
+ assertNotNull("pdf rendition was not generated", nodeService.getProperty(pdfRenditionNodeRef, PROP_CONTENT));
+
+ // Check the pdf rendition content hash code is valid
+ int pdfRenditionContentHashCode = getRenditionContentHashCode(pdfRenditionNodeRef);
+ assertEquals("pdf rendition content hash code is different from source node content hash code", sourceNodeContentHashCode, pdfRenditionContentHashCode);
+
+ // Trigger the doc lib rendition
+ render(ADMIN, sourceNodeRef, DOC_LIB);
+ NodeRef docLibRenditionNodeRef = waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true);
+ assertNotNull("doc lib rendition was not generated", docLibRenditionNodeRef);
+ assertNotNull("doc lib rendition was not generated", nodeService.getProperty(docLibRenditionNodeRef, PROP_CONTENT));
+
+ // Check the doc lib rendition content hash code is valid
+ int docLibenditionContentHashCode = getRenditionContentHashCode(docLibRenditionNodeRef);
+ assertEquals("doc lib rendition content hash code is different from source node content hash code", sourceNodeContentHashCode, docLibenditionContentHashCode);
+
+ // Update the source node content
+ updateContent(ADMIN, sourceNodeRef, "quick.docx");
+
+ // Get source node content hash code after update
+ int sourceNodeContentHashCode2 = getSourceContentHashCode(sourceNodeRef);
+
+ // Check content hash code are different after content update
+ assertNotEquals("Source node content hash code is the same after content update", sourceNodeContentHashCode, sourceNodeContentHashCode2);
+ assertNotEquals("pdf rendition content hash code is the same after content update", sourceNodeContentHashCode2, pdfRenditionContentHashCode);
+ assertNotEquals("doc lib rendition content hash code is the same after content update", sourceNodeContentHashCode2, docLibenditionContentHashCode);
+
+ // Forces the content hash code for every source node renditions
+ AuthenticationUtil.runAs(() -> {
+ renditionService2.forceRenditionsContentHashCode(sourceNodeRef);
+ return null;
+ }, ADMIN);
+
+ // Check the renditions content hash code are now the same as the latest source node content hash code
+ int pdfRenditionContentHashCode2 = getRenditionContentHashCode(pdfRenditionNodeRef);
+ int docLibenditionContentHashCode2 = getRenditionContentHashCode(docLibRenditionNodeRef);
+ assertEquals("pdf rendition content hash code is different from latest source node content hash code", sourceNodeContentHashCode2, pdfRenditionContentHashCode2);
+ assertEquals("doc lib rendition content hash code is different from latest source node content hash code", sourceNodeContentHashCode2, docLibenditionContentHashCode2);
+ }
+
}