diff --git a/.secrets.baseline b/.secrets.baseline index 4a3a67cf6e..a0bd0a92bf 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -1519,7 +1519,7 @@ "filename": "repository/src/test/java/org/alfresco/repo/rendition2/AbstractRenditionIntegrationTest.java", "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", "is_verified": false, - "line_number": 128, + "line_number": 130, "is_secret": false } ], @@ -1868,5 +1868,5 @@ } ] }, - "generated_at": "2025-03-21T13:01:19Z" + "generated_at": "2025-03-27T23:45:41Z" } 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 7fc02b0bf6..0d71273188 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 @@ -119,4 +119,13 @@ 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); } 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 c351f59030..730b476b03 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 @@ -936,4 +936,35 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea } } + @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); + } + } + } + } + } + } } 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 2673e3ada7..65e1cec8ea 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 @@ -55,6 +55,7 @@ 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.ContentData; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentWriter; @@ -62,6 +63,7 @@ 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.datatype.DefaultTypeConverter; import org.alfresco.service.cmr.security.MutableAuthenticationService; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.security.PersonService; @@ -129,6 +131,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; @@ -554,4 +557,28 @@ 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 6216f1dbac..8807d6724e 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 @@ -445,6 +445,61 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati assertEquals(TOTAL_NODES, countModifier(nodes, user1)); } + @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); + } + private int countModifier(List nodes, String user) { int count = 0;