From aa24cc2333dbecbd96359bb2322f78ca1bc898a4 Mon Sep 17 00:00:00 2001 From: alandavis Date: Tue, 25 Sep 2018 18:31:32 +0100 Subject: [PATCH] REPO-3766 Renditions: Simplified Async Rendition Service REPO-3688 Renditions: Deprecation of public Java APIs --- pom.xml | 2 +- .../rest/api/impl/RenditionsImpl.java | 118 ++++++++---------- .../alfresco/public-rest-context.xml | 2 +- .../rest/api/tests/RenditionsTest.java | 7 +- .../alfresco/rest/api/tests/TestPeople.java | 8 +- .../api/tests/client/PublicApiClient.java | 29 ++++- 6 files changed, 89 insertions(+), 77 deletions(-) diff --git a/pom.xml b/pom.xml index b47f85d2e0..baf771f023 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ 7.10 7.4 - 8.10 + 8.15 1.1 2.9.5 diff --git a/src/main/java/org/alfresco/rest/api/impl/RenditionsImpl.java b/src/main/java/org/alfresco/rest/api/impl/RenditionsImpl.java index ac4036121c..c92df940dc 100644 --- a/src/main/java/org/alfresco/rest/api/impl/RenditionsImpl.java +++ b/src/main/java/org/alfresco/rest/api/impl/RenditionsImpl.java @@ -29,10 +29,10 @@ package org.alfresco.rest.api.impl; import org.alfresco.heartbeat.RenditionsDataCollector; import org.alfresco.model.ContentModel; import org.alfresco.query.PagingResults; +import org.alfresco.repo.rendition2.RenditionDefinition2; +import org.alfresco.repo.rendition2.RenditionDefinitionRegistry2; +import org.alfresco.repo.rendition2.RenditionService2; import org.alfresco.repo.tenant.TenantService; -import org.alfresco.repo.thumbnail.ThumbnailDefinition; -import org.alfresco.repo.thumbnail.ThumbnailHelper; -import org.alfresco.repo.thumbnail.ThumbnailRegistry; import org.alfresco.repo.thumbnail.script.ScriptThumbnailService; import org.alfresco.rest.antlr.WhereClauseParser; import org.alfresco.rest.api.Nodes; @@ -45,6 +45,7 @@ import org.alfresco.rest.framework.core.exceptions.ConstraintViolatedException; import org.alfresco.rest.framework.core.exceptions.DisabledServiceException; import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; import org.alfresco.rest.framework.core.exceptions.NotFoundException; +import org.alfresco.rest.framework.core.exceptions.StaleEntityException; import org.alfresco.rest.framework.resource.content.BinaryResource; import org.alfresco.rest.framework.resource.content.CacheDirective; import org.alfresco.rest.framework.resource.content.ContentInfoImpl; @@ -57,17 +58,12 @@ import org.alfresco.rest.framework.resource.parameters.where.Query; import org.alfresco.rest.framework.resource.parameters.where.QueryHelper; import org.alfresco.rest.workflow.api.impl.MapBasedQueryWalker; import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.action.Action; -import org.alfresco.service.cmr.action.ActionService; -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.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.thumbnail.ThumbnailService; -import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.util.PropertyCheck; import org.alfresco.util.TempFileProvider; @@ -98,15 +94,12 @@ public class RenditionsImpl implements Renditions, ResourceLoaderAware private Nodes nodes; private NodeService nodeService; - private ThumbnailService thumbnailService; private ScriptThumbnailService scriptThumbnailService; - private RenditionService renditionService; private MimetypeService mimetypeService; - private ActionService actionService; - private NamespaceService namespaceService; private ServiceRegistry serviceRegistry; private ResourceLoader resourceLoader; private TenantService tenantService; + private RenditionService2 renditionService2; private RenditionsDataCollector renditionsDataCollector; public void setNodes(Nodes nodes) @@ -114,11 +107,6 @@ public class RenditionsImpl implements Renditions, ResourceLoaderAware this.nodes = nodes; } - public void setThumbnailService(ThumbnailService thumbnailService) - { - this.thumbnailService = thumbnailService; - } - public void setScriptThumbnailService(ScriptThumbnailService scriptThumbnailService) { this.scriptThumbnailService = scriptThumbnailService; @@ -140,6 +128,11 @@ public class RenditionsImpl implements Renditions, ResourceLoaderAware this.tenantService = tenantService; } + public void setRenditionService2(RenditionService2 renditionService2) + { + this.renditionService2 = renditionService2; + } + public void setRenditionsDataCollector(RenditionsDataCollector renditionsDataCollector) { this.renditionsDataCollector = renditionsDataCollector; @@ -148,26 +141,23 @@ public class RenditionsImpl implements Renditions, ResourceLoaderAware public void init() { PropertyCheck.mandatory(this, "nodes", nodes); - PropertyCheck.mandatory(this, "thumbnailService", thumbnailService); PropertyCheck.mandatory(this, "scriptThumbnailService", scriptThumbnailService); PropertyCheck.mandatory(this, "serviceRegistry", serviceRegistry); PropertyCheck.mandatory(this, "tenantService", tenantService); + PropertyCheck.mandatory(this, "renditionService2", renditionService2); PropertyCheck.mandatory(this, "renditionsDataCollector", renditionsDataCollector); this.nodeService = serviceRegistry.getNodeService(); - this.actionService = serviceRegistry.getActionService(); - this.renditionService = serviceRegistry.getRenditionService(); this.mimetypeService = serviceRegistry.getMimetypeService(); - this.namespaceService = serviceRegistry.getNamespaceService(); } @Override public CollectionWithPagingInfo getRenditions(NodeRef nodeRef, Parameters parameters) { final NodeRef validatedNodeRef = validateNode(nodeRef.getStoreRef(), nodeRef.getId()); - String contentMimeType = getMimeType(validatedNodeRef); + ContentData contentData = getContentData(nodeRef, true); + String sourceMimetype = contentData.getMimetype(); - Query query = parameters.getQuery(); boolean includeCreated = true; boolean includeNotCreated = true; String status = getStatus(parameters); @@ -180,15 +170,17 @@ public class RenditionsImpl implements Renditions, ResourceLoaderAware Map apiRenditions = new TreeMap<>(); if (includeNotCreated) { - // List all available thumbnail definitions - List thumbnailDefinitions = thumbnailService.getThumbnailRegistry().getThumbnailDefinitions(contentMimeType, -1); - for (ThumbnailDefinition thumbnailDefinition : thumbnailDefinitions) + // List all available rendition definitions + long size = contentData.getSize(); + RenditionDefinitionRegistry2 renditionDefinitionRegistry2 = renditionService2.getRenditionDefinitionRegistry2(); + Set renditionNames = renditionDefinitionRegistry2.getRenditionNamesFrom(sourceMimetype, size); + for (String renditionName : renditionNames) { - apiRenditions.put(thumbnailDefinition.getName(), toApiRendition(thumbnailDefinition)); + apiRenditions.put(renditionName, toApiRendition(renditionName)); } } - List nodeRefRenditions = renditionService.getRenditions(validatedNodeRef); + List nodeRefRenditions = renditionService2.getRenditions(validatedNodeRef); if (!nodeRefRenditions.isEmpty()) { for (ChildAssociationRef childAssociationRef : nodeRefRenditions) @@ -231,21 +223,23 @@ public class RenditionsImpl implements Renditions, ResourceLoaderAware // if there is no rendition, then try to find the available/registered rendition (yet to be created). if (renditionNodeRef == null && includeNotCreated) { - ThumbnailDefinition thumbnailDefinition = thumbnailService.getThumbnailRegistry().getThumbnailDefinition(renditionId); - if (thumbnailDefinition == null) + ContentData contentData = getContentData(validatedNodeRef, true); + String sourceMimetype = contentData.getMimetype(); + long size = contentData.getSize(); + RenditionDefinitionRegistry2 renditionDefinitionRegistry2 = renditionService2.getRenditionDefinitionRegistry2(); + RenditionDefinition2 renditionDefinition = renditionDefinitionRegistry2.getRenditionDefinition(renditionId); + if (renditionDefinition == null) { throw new NotFoundException(renditionId + " is not registered."); } else { - String contentMimeType = getMimeType(validatedNodeRef); - // List all available thumbnail definitions for the source node - List thumbnailDefinitions = thumbnailService.getThumbnailRegistry().getThumbnailDefinitions(contentMimeType, -1); + Set renditionNames = renditionDefinitionRegistry2.getRenditionNamesFrom(sourceMimetype, size); boolean found = false; - for (ThumbnailDefinition td : thumbnailDefinitions) + for (String renditionName : renditionNames) { // Check the registered renditionId is applicable for the node's mimeType - if (renditionId.equals(td.getName())) + if (renditionId.equals(renditionName)) { found = true; break; @@ -253,10 +247,10 @@ public class RenditionsImpl implements Renditions, ResourceLoaderAware } if (!found) { - throw new NotFoundException(renditionId + " is not applicable for the node's mimeType " + contentMimeType); + throw new NotFoundException(renditionId + " is not applicable for the node's mimeType " + sourceMimetype); } } - return toApiRendition(thumbnailDefinition); + return toApiRendition(renditionId); } if (renditionNodeRef == null) @@ -277,9 +271,9 @@ public class RenditionsImpl implements Renditions, ResourceLoaderAware public void createRendition(NodeRef nodeRef, Rendition rendition, boolean executeAsync, Parameters parameters) { // If thumbnail generation has been configured off, then don't bother. - if (!thumbnailService.getThumbnailsEnabled()) + if (!renditionService2.isEnabled()) { - throw new DisabledServiceException("Thumbnail generation has been disabled."); + throw new DisabledServiceException("Rendition generation has been disabled."); } final NodeRef sourceNodeRef = validateNode(nodeRef.getStoreRef(), nodeRef.getId()); @@ -289,29 +283,22 @@ public class RenditionsImpl implements Renditions, ResourceLoaderAware throw new ConstraintViolatedException(rendition.getId() + " rendition already exists."); } - // Use the thumbnail registry to get the details of the thumbnail - ThumbnailRegistry registry = thumbnailService.getThumbnailRegistry(); - ThumbnailDefinition thumbnailDefinition = registry.getThumbnailDefinition(rendition.getId()); - if (thumbnailDefinition == null) + try { - throw new NotFoundException(rendition.getId() + " is not registered."); + renditionService2.render(sourceNodeRef, rendition.getId()); } - - ContentData contentData = getContentData(sourceNodeRef, true); - // Check if anything is currently available to generate thumbnails for the specified mimeType - String sourceMimetype = contentData.getMimetype(); - if (!registry.isThumbnailDefinitionAvailable(contentData.getContentUrl(), sourceMimetype, contentData.getSize(), sourceNodeRef, - thumbnailDefinition)) + catch (IllegalArgumentException e) { - throw new InvalidArgumentException("Unable to create thumbnail '" + thumbnailDefinition.getName() + "' for " + - sourceMimetype + " as no transformer is currently available."); + throw new NotFoundException(rendition.getId() + " is not registered."); // 404 + } + catch (UnsupportedOperationException e) + { + throw new IllegalArgumentException((e.getMessage())); // 400 + } + catch (IllegalStateException e) + { + throw new StaleEntityException(e.getMessage()); // 409 } - - Action action = ThumbnailHelper.createCreateThumbnailAction(thumbnailDefinition, serviceRegistry); - renditionsDataCollector.recordRenditionRequest(thumbnailDefinition, sourceMimetype); - - // Create thumbnail - or else queue for async creation - actionService.executeAction(action, sourceNodeRef, true, executeAsync); } @Override @@ -423,10 +410,8 @@ public class RenditionsImpl implements Renditions, ResourceLoaderAware { throw new InvalidArgumentException("renditionId can't be null or empty."); } - // Thumbnails have a cm: prefix. - QName renditionQName = QName.resolveToQName(namespaceService, renditionId); - ChildAssociationRef nodeRefRendition = renditionService.getRenditionByName(nodeRef, renditionQName); + ChildAssociationRef nodeRefRendition = renditionService2.getRenditionByName(nodeRef, renditionId); if (nodeRefRendition == null) { return null; @@ -457,12 +442,15 @@ public class RenditionsImpl implements Renditions, ResourceLoaderAware return apiRendition; } - protected Rendition toApiRendition(ThumbnailDefinition thumbnailDefinition) + protected Rendition toApiRendition(String renditionName) { - ContentInfo contentInfo = new ContentInfo(thumbnailDefinition.getMimetype(), - getMimeTypeDisplayName(thumbnailDefinition.getMimetype()), null, null); + RenditionDefinitionRegistry2 renditionDefinitionRegistry2 = renditionService2.getRenditionDefinitionRegistry2(); + RenditionDefinition2 renditionDefinition = renditionDefinitionRegistry2.getRenditionDefinition(renditionName); + ContentInfo contentInfo = new ContentInfo(renditionDefinition.getTargetMimetype(), + getMimeTypeDisplayName(renditionDefinition.getTargetMimetype()), null, null); + Rendition apiRendition = new Rendition(); - apiRendition.setId(thumbnailDefinition.getName()); + apiRendition.setId(renditionName); apiRendition.setContent(contentInfo); apiRendition.setStatus(RenditionStatus.NOT_CREATED); diff --git a/src/main/resources/alfresco/public-rest-context.xml b/src/main/resources/alfresco/public-rest-context.xml index cff19bf0b8..73295d7f32 100644 --- a/src/main/resources/alfresco/public-rest-context.xml +++ b/src/main/resources/alfresco/public-rest-context.xml @@ -1359,10 +1359,10 @@ - + diff --git a/src/test/java/org/alfresco/rest/api/tests/RenditionsTest.java b/src/test/java/org/alfresco/rest/api/tests/RenditionsTest.java index feca5ee960..fb43dc6cf7 100644 --- a/src/test/java/org/alfresco/rest/api/tests/RenditionsTest.java +++ b/src/test/java/org/alfresco/rest/api/tests/RenditionsTest.java @@ -36,6 +36,7 @@ import static org.junit.Assert.assertTrue; import com.google.common.collect.Ordering; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.transform.ContentTransformer; +import org.alfresco.repo.rendition2.RenditionService2Impl; import org.alfresco.rest.api.model.Site; import org.alfresco.rest.api.nodes.NodesEntityResource; import org.alfresco.rest.api.tests.RepoService.TestNetwork; @@ -469,9 +470,9 @@ public class RenditionsTest extends AbstractBaseApiTest multipleRenditionRequest.add(new Rendition().setId("imgpreview")); post(getNodeRenditionsUrl(contentNodeId), toJsonAsString(multipleRenditionRequest), 400); - ThumbnailService thumbnailService = applicationContext.getBean("thumbnailService", ThumbnailService.class); + RenditionService2Impl renditionService2 = applicationContext.getBean("renditionService2", RenditionService2Impl.class); // Disable thumbnail generation - thumbnailService.setThumbnailsEnabled(false); + renditionService2.setThumbnailsEnabled(false); try { // Create multipart request @@ -494,7 +495,7 @@ public class RenditionsTest extends AbstractBaseApiTest } finally { - thumbnailService.setThumbnailsEnabled(true); + renditionService2.setThumbnailsEnabled(true); } } diff --git a/src/test/java/org/alfresco/rest/api/tests/TestPeople.java b/src/test/java/org/alfresco/rest/api/tests/TestPeople.java index bd54305fd0..acda284400 100644 --- a/src/test/java/org/alfresco/rest/api/tests/TestPeople.java +++ b/src/test/java/org/alfresco/rest/api/tests/TestPeople.java @@ -85,6 +85,7 @@ import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; +import static java.lang.Thread.sleep; import static org.alfresco.repo.security.authentication.ResetPasswordServiceImplTest.getWorkflowIdAndKeyFromUrl; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -2059,7 +2060,7 @@ public class TestPeople extends AbstractBaseApiTest { try { - Thread.sleep(requiredDelay); + sleep(requiredDelay); } catch (InterruptedException e) { @@ -2129,7 +2130,7 @@ public class TestPeople extends AbstractBaseApiTest } @Test - public void updateAvatar() throws PublicApiException, IOException + public void updateAvatar() throws PublicApiException, IOException, InterruptedException { final String person1 = account1PersonIt.next(); final String person2 = account1PersonIt.next(); @@ -2243,7 +2244,8 @@ public class TestPeople extends AbstractBaseApiTest @Test - public void removeAvatar() throws IOException, PublicApiException{ + public void removeAvatar() throws IOException, PublicApiException, InterruptedException + { final String person1 = account1PersonIt.next(); final String person2 = account1PersonIt.next(); diff --git a/src/test/java/org/alfresco/rest/api/tests/client/PublicApiClient.java b/src/test/java/org/alfresco/rest/api/tests/client/PublicApiClient.java index ce4fe2d593..aeb5315610 100644 --- a/src/test/java/org/alfresco/rest/api/tests/client/PublicApiClient.java +++ b/src/test/java/org/alfresco/rest/api/tests/client/PublicApiClient.java @@ -1411,17 +1411,17 @@ public class PublicApiClient remove("people", personId, "activities", String.valueOf(activity.getId()), "Failed to remove activity"); } - public HttpResponse getAvatar(String personId, boolean placeholder, int expectedStatus) throws PublicApiException + public HttpResponse getAvatar(String personId, boolean placeholder, int expectedStatus) throws PublicApiException, InterruptedException { return getAvatar(personId, null, placeholder, null, expectedStatus); } - public HttpResponse getAvatar(String personId, String ifModifiedSince, int expectedStatus) throws PublicApiException + public HttpResponse getAvatar(String personId, String ifModifiedSince, int expectedStatus) throws PublicApiException, InterruptedException { return getAvatar(personId, null, false, ifModifiedSince, expectedStatus); } - public HttpResponse getAvatar(String personId, Boolean attachment, boolean placeholder, String ifModifiedSince, int expectedStatus) throws PublicApiException + public HttpResponse getAvatar(String personId, Boolean attachment, boolean placeholder, String ifModifiedSince, int expectedStatus) throws PublicApiException, InterruptedException { // Binary response expected Map params = new HashMap<>(); @@ -1438,12 +1438,33 @@ public class PublicApiClient headers.put("If-Modified-Since", ifModifiedSince); } - HttpResponse response = getSingle("people", personId, "avatar", null, params, headers, "Failed to get avatar", expectedStatus); + // As renditions are now done async, we generally need to wait. + HttpResponse response = getSingleWithDelayRetry("people", personId, "avatar", + null, params, headers, "Failed to get avatar", 40, 2500, expectedStatus); checkStatus("Unexpected response", expectedStatus, response); return response; } + private HttpResponse getSingleWithDelayRetry(String entityCollectionName, String entityId, String relationCollectionName, String relationId, Map params, + Map headers, String errorMessage, int repeat, long pauseInMillisecond, int expectedStatus) throws PublicApiException, InterruptedException + { + int retryCount = 0; + while (retryCount < repeat) + { + try + { + return getSingle(entityCollectionName, entityId, relationCollectionName, relationId, params, headers, errorMessage, expectedStatus); + } + catch (PublicApiException ex) + { + retryCount++; + Thread.sleep(pauseInMillisecond); + } + } + return null; + } + public HttpResponse updateAvatar(String personId, File avatar, int expectedStatus) throws PublicApiException { try