From 2af1a9621126056c9ae14db4bcecbce40dbf7ff0 Mon Sep 17 00:00:00 2001 From: alandavis Date: Fri, 29 Nov 2019 22:13:43 +0000 Subject: [PATCH] REPO-4791 If possible, use Local transforms where Legacy transforms are called. The objective of this PR is to replace all the ContentService calls related to transforms with calls to the SynchronousTransformClient that switches between Local and Legacy transforms. This will allow us to remove the Legacy transforms at some point and also take advantage of the T-Engines which can be scaled. - Introduced a ContentTransformService interface to hold all the ContentService calls related to transforms and an implementation class ContentTransformServiceAdaptor as a super class of ContentServiceImpl. The Adaptor provides code to allow the SynchronousTransformClient to be called. As a result any external custom code that uses these methods will be able to take advantage of Local or Legacy transforms. - The transform code originally in ContentServiceImpl has been moved to ContentTransformServiceImpl, which is a super class of the LegacySynchronousTransformClient. - All calls in the repository have been replaced with calls to SynchronousTransformClient. There are still a few calls to these methods in ContentServiceImplTest and ArchiveContentTransformerTest that have not been changes as they test Legacy transform code. - The asynchronous LocalTransformClient now uses the SynchronousTransformClient. - Bug fix to the TransformationOptionsConverter as it was found that the page number being used for images was wrong. Legacy transformers start at page 1, where as Local and ImageMagick itself start at 0. - Added a conversion to the new transform option (Map options = converter.getOptions(transformationOptions); + if (!synchronousTransformClient.isSupported(sourceMimetype, sourceSizeInBytes, + contentUrl, mimeType, options, null, actionedUponNodeRef)) { - throw new RuleServiceException(String.format(TRANSFORMER_NOT_EXISTS_MESSAGE_PATTERN, contentReader.getMimetype(), mimeType)); + throw new RuleServiceException(String.format(TRANSFORMER_NOT_EXISTS_MESSAGE_PATTERN, sourceMimetype, mimeType)); } // Get the details of the copy destination @@ -325,9 +346,10 @@ public class TransformActionExecuter extends ActionExecuterAbstractBase { // transform - will throw NoTransformerException if there are no transformers - TransformationOptions options = newTransformationOptions(ruleAction, sourceNodeRef); - options.setTargetNodeRef(destinationNodeRef); - this.contentService.transform(contentReader, contentWriter, options); + TransformationOptions transformationOptions = newTransformationOptions(ruleAction, sourceNodeRef); + transformationOptions.setTargetNodeRef(destinationNodeRef); + Map options = converter.getOptions(transformationOptions); + synchronousTransformClient.transform(contentReader, contentWriter, options, null, sourceNodeRef); } /** diff --git a/src/main/java/org/alfresco/repo/content/ContentServiceImpl.java b/src/main/java/org/alfresco/repo/content/ContentServiceImpl.java index d740654cf4..2d6d2e09df 100644 --- a/src/main/java/org/alfresco/repo/content/ContentServiceImpl.java +++ b/src/main/java/org/alfresco/repo/content/ContentServiceImpl.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2019 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -25,15 +25,6 @@ */ package org.alfresco.repo.content; -import java.io.File; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.repo.content.ContentServicePolicies.OnContentPropertyUpdatePolicy; @@ -41,12 +32,6 @@ import org.alfresco.repo.content.ContentServicePolicies.OnContentReadPolicy; import org.alfresco.repo.content.ContentServicePolicies.OnContentUpdatePolicy; import org.alfresco.repo.content.cleanup.EagerContentStoreCleaner; import org.alfresco.repo.content.filestore.FileContentStore; -import org.alfresco.repo.content.filestore.FileContentWriter; -import org.alfresco.repo.content.transform.ContentTransformer; -import org.alfresco.repo.content.transform.ContentTransformerRegistry; -import org.alfresco.repo.content.transform.TransformerDebug; -import org.alfresco.repo.content.transform.UnimportantTransformException; -import org.alfresco.repo.content.transform.UnsupportedTransformationException; import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.policy.ClassPolicyDelegate; import org.alfresco.repo.policy.JavaBehaviour; @@ -63,10 +48,8 @@ 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.MimetypeServiceAware; -import org.alfresco.service.cmr.repository.NoTransformerException; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.TransformationOptions; import org.alfresco.service.cmr.usage.ContentQuotaException; import org.alfresco.service.namespace.QName; import org.alfresco.util.EqualsHelper; @@ -78,6 +61,12 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.extensions.surf.util.I18NUtil; +import java.io.Serializable; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + /** * Service implementation acting as a level of indirection between the client * and the underlying content store. @@ -89,7 +78,7 @@ import org.springframework.extensions.surf.util.I18NUtil; * @author Derek Hulley * @since 3.2 */ -public class ContentServiceImpl implements ContentService, ApplicationContextAware +public class ContentServiceImpl extends ContentTransformServiceAdaptor implements ContentService, ApplicationContextAware { private static Log logger = LogFactory.getLog(ContentServiceImpl.class); @@ -98,25 +87,16 @@ public class ContentServiceImpl implements ContentService, ApplicationContextAwa private MimetypeService mimetypeService; private RetryingTransactionHelper transactionHelper; private ApplicationContext applicationContext; - @Deprecated - protected TransformerDebug transformerDebug; - - /** a registry of all available content transformers */ - @Deprecated - private ContentTransformerRegistry transformerRegistry; /** The cleaner that will ensure that rollbacks clean up after themselves */ private EagerContentStoreCleaner eagerContentStoreCleaner; /** the store to use. Any multi-store support is provided by the store implementation. */ private ContentStore store; /** the store for all temporarily created content */ private ContentStore tempStore; - @Deprecated - private ContentTransformer imageMagickContentTransformer; /** Should we consider zero byte content to be the same as no content? */ private boolean ignoreEmptyContent; - private boolean transformerFailover = true; - + /** * The policy component */ @@ -149,15 +129,6 @@ public class ContentServiceImpl implements ContentService, ApplicationContextAwa this.mimetypeService = mimetypeService; } - /** - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. - */ - @Deprecated - public void setTransformerRegistry(ContentTransformerRegistry transformerRegistry) - { - this.transformerRegistry = transformerRegistry; - } - public void setEagerContentStoreCleaner(EagerContentStoreCleaner eagerContentStoreCleaner) { this.eagerContentStoreCleaner = eagerContentStoreCleaner; @@ -174,35 +145,13 @@ public class ContentServiceImpl implements ContentService, ApplicationContextAwa } /** - * * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. */ - @Deprecated - public void setImageMagickContentTransformer(ContentTransformer imageMagickContentTransformer) - { - this.imageMagickContentTransformer = imageMagickContentTransformer; - } - public void setIgnoreEmptyContent(boolean ignoreEmptyContent) { this.ignoreEmptyContent = ignoreEmptyContent; } - /** - * Allows fail over form one transformer to another when there is - * more than one transformer available. The cost is that the output - * of the transformer must go to a temporary file in case it fails. - * @param transformerFailover {@code true} (the default) indicate - * that fail over should take place. - * @deprecated The transformations code is being moved out of the codebase and replaced by the - * new async RenditionService2 or other external libraries. - */ - @Deprecated - public void setTransformerFailover(boolean transformerFailover) - { - this.transformerFailover = transformerFailover; - } - /* (non-Javadoc) * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) */ @@ -211,17 +160,6 @@ public class ContentServiceImpl implements ContentService, ApplicationContextAwa this.applicationContext = applicationContext; } - /** - * Helper setter of the transformer debug. - * @param transformerDebug TransformerDebug - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. - */ - @Deprecated - public void setTransformerDebug(TransformerDebug transformerDebug) - { - this.transformerDebug = transformerDebug; - } - /** * Service initialise */ @@ -564,407 +502,6 @@ public class ContentServiceImpl implements ContentService, ApplicationContextAwa return tempStore.getWriter(ContentContext.NULL_CONTEXT); } - /** - * @see org.alfresco.repo.content.transform.ContentTransformerRegistry - * @see org.alfresco.repo.content.transform.ContentTransformer - * @see org.alfresco.service.cmr.repository.ContentService#transform(org.alfresco.service.cmr.repository.ContentReader, org.alfresco.service.cmr.repository.ContentWriter) - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. - */ - @Deprecated - public void transform(ContentReader reader, ContentWriter writer) - { - // Call transform with no options - TransformationOptions options = new TransformationOptions(); - this.transform(reader, writer, options); - } - - /** - * @see org.alfresco.repo.content.transform.ContentTransformerRegistry - * @see org.alfresco.repo.content.transform.ContentTransformer - * @deprecated - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. - */ - @Deprecated - public void transform(ContentReader reader, ContentWriter writer, Map options) - throws NoTransformerException, ContentIOException - { - transform(reader, writer, new TransformationOptions(options)); - } - - /** - * @see org.alfresco.repo.content.transform.ContentTransformerRegistry - * @see org.alfresco.repo.content.transform.ContentTransformer - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. - */ - @Deprecated - public void transform(ContentReader reader, ContentWriter writer, TransformationOptions options) - throws NoTransformerException, ContentIOException - { - // check that source and target mimetypes are available - if (reader == null) - { - throw new AlfrescoRuntimeException("The content reader must be set"); - } - String sourceMimetype = reader.getMimetype(); - if (sourceMimetype == null) - { - throw new AlfrescoRuntimeException("The content reader mimetype must be set: " + reader); - } - String targetMimetype = writer.getMimetype(); - if (targetMimetype == null) - { - throw new AlfrescoRuntimeException("The content writer mimetype must be set: " + writer); - } - - long sourceSize = reader.getSize(); - try - { - // look for a transformer - transformerDebug.pushAvailable(reader.getContentUrl(), sourceMimetype, targetMimetype, options); - List transformers = getActiveTransformers(sourceMimetype, sourceSize, targetMimetype, options); - transformerDebug.availableTransformers(transformers, sourceSize, options, "ContentService.transform(...)"); - - int count = transformers.size(); - if (count == 0) - { - throw new NoTransformerException(sourceMimetype, targetMimetype); - } - - if (count == 1 || !transformerFailover) - { - ContentTransformer transformer = transformers.size() == 0 ? null : transformers.get(0); - transformer.transform(reader, writer, options); - } - else - { - failoverTransformers(reader, writer, options, targetMimetype, transformers); - } - } - finally - { - if (transformerDebug.isEnabled()) - { - transformerDebug.popAvailable(); - debugTransformations(sourceMimetype, targetMimetype, sourceSize, options); - } - } - } - - /** - * - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. - */ - @Deprecated - private void failoverTransformers(ContentReader reader, ContentWriter writer, - TransformationOptions options, String targetMimetype, - List transformers) - { - List exceptions = null; - boolean done = false; - try - { - // Try the best transformer and then the next if it fails - // and so on down the list - char c = 'a'; - String outputFileExt = mimetypeService.getExtension(targetMimetype); - for (ContentTransformer transformer : transformers) - { - ContentWriter currentWriter = writer; - File tempFile = null; - try - { - // We can't know in advance which of the - // available transformer will work - if any. - // We can't write into the ContentWriter stream. - // So make a temporary file writer with the - // current transformer name. - tempFile = TempFileProvider.createTempFile( - "FailoverTransformer_intermediate_" - + transformer.getClass().getSimpleName() + "_", "." - + outputFileExt); - currentWriter = new FileContentWriter(tempFile); - currentWriter.setMimetype(targetMimetype); - currentWriter.setEncoding(writer.getEncoding()); - - if (c != 'a' && transformerDebug.isEnabled()) - { - transformerDebug.debug(""); - transformerDebug.debug("Try " + c + ")"); - } - c++; - - transformer.transform(reader, currentWriter, options); - - if (tempFile != null) - { - writer.putContent(tempFile); - } - - // No need to close input or output streams - // (according - // to comment in FailoverContentTransformer) - done = true; - return; - } - catch (Exception e) - { - if (exceptions == null) - { - exceptions = new ArrayList(); - } - if (!(e instanceof AlfrescoRuntimeException)) - { - e = new AlfrescoRuntimeException(e.getMessage(), e); - } - exceptions.add((AlfrescoRuntimeException)e); - - // Set a new reader to refresh the input stream. - reader = reader.getReader(); - } - } - // Throw the exception from the first transformer. The - // others are consumed. - if (exceptions != null) - { - throw exceptions.get(0); - } - } - finally - { - // Log exceptions that we have consumed. We may have thrown the first one if - // none of the transformers worked. - if (exceptions != null) - { - boolean first = true; - for (Exception e : exceptions) - { - Throwable rootCause = (e instanceof AlfrescoRuntimeException) ? ((AlfrescoRuntimeException)e).getRootCause() : null; - String message = (rootCause == null ? null : rootCause.getMessage()); - if (done) - { - message = "Transformer succeeded after previous transformer failed"+ (message == null ? "" : ": "+message); - if (rootCause instanceof UnsupportedTransformationException || - rootCause instanceof UnimportantTransformException) - { - logger.debug(message); - } - else - { - logger.warn(message, e); - } - } - else if (!first) // The first exception is logged later - { - message = "Transformer exception"+ (message == null ? "" : ": "+message); - if (rootCause instanceof UnsupportedTransformationException || - rootCause instanceof UnimportantTransformException) - { - logger.debug(message); - } - else - { - logger.error(message, e); - } - first = false; - } - } - } - } - } - - /** - * @see org.alfresco.repo.content.transform.ContentTransformerRegistry - * @see org.alfresco.repo.content.transform.ContentTransformer - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. - */ - @Deprecated - public ContentTransformer getTransformer(String sourceMimetype, String targetMimetype) - { - return getTransformer(null, sourceMimetype, -1, targetMimetype, new TransformationOptions()); - } - - /** - * - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. - */ - @Deprecated - public ContentTransformer getTransformer(String sourceMimetype, String targetMimetype, TransformationOptions options) - { - return getTransformer(null, sourceMimetype, -1, targetMimetype, options); - } - - /** - * @see org.alfresco.service.cmr.repository.ContentService#getTransformer(String, java.lang.String, long, java.lang.String, org.alfresco.service.cmr.repository.TransformationOptions) - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. - */ - @Deprecated - public ContentTransformer getTransformer(String sourceUrl, String sourceMimetype, long sourceSize, String targetMimetype, TransformationOptions options) - { - List transformers = getTransformers(sourceUrl, sourceMimetype, sourceSize, targetMimetype, options); - return (transformers == null) ? null : transformers.get(0); - } - - /** - * @see org.alfresco.service.cmr.repository.ContentService#getTransformers(String, java.lang.String, long, java.lang.String, org.alfresco.service.cmr.repository.TransformationOptions) - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. - */ - @Deprecated - public List getTransformers(String sourceUrl, String sourceMimetype, long sourceSize, String targetMimetype, TransformationOptions options) - { - try - { - // look for a transformer - transformerDebug.pushAvailable(sourceUrl, sourceMimetype, targetMimetype, options); - List transformers = getActiveTransformers(sourceMimetype, sourceSize, targetMimetype, options); - transformerDebug.availableTransformers(transformers, sourceSize, options, "ContentService.getTransformer(...)"); - return transformers.isEmpty() ? null : transformers; - } - finally - { - transformerDebug.popAvailable(); - } - } - - /** - * Checks if the file just uploaded into Share is a special "debugTransformers.txt" file and - * if it is creates TransformerDebug that lists all the supported mimetype transformation for - * each transformer. - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. - */ - @Deprecated - private void debugTransformations(String sourceMimetype, String targetMimetype, - long sourceSize, TransformationOptions transformOptions) - { - // check the file name - if (MimetypeMap.MIMETYPE_TEXT_PLAIN.equals(sourceMimetype) && - MimetypeMap.MIMETYPE_IMAGE_PNG.equals(targetMimetype)) - { - String fileName = transformerDebug.getFileName(transformOptions, true, 0); - if (fileName != null && fileName.contains("debugTransformers.txt")) - { - transformerDebug.transformationsByTransformer(null, false, false, null); - transformerDebug.transformationsByExtension(null, null, false, false, false, null); - } - } - } - - /** - * {@inheritDoc} - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. - */ - @Deprecated - public long getMaxSourceSizeBytes(String sourceMimetype, String targetMimetype, TransformationOptions options) - { - try - { - long maxSourceSize = 0; - transformerDebug.pushAvailable(null, sourceMimetype, targetMimetype, options); - List transformers = getActiveTransformers(sourceMimetype, -1, targetMimetype, options); - for (ContentTransformer transformer: transformers) - { - long maxSourceSizeKBytes = transformer.getMaxSourceSizeKBytes(sourceMimetype, targetMimetype, options); - if (maxSourceSize >= 0) - { - if (maxSourceSizeKBytes < 0) - { - maxSourceSize = -1; - } - else if (maxSourceSizeKBytes > 0 && maxSourceSize < maxSourceSizeKBytes) - { - maxSourceSize = maxSourceSizeKBytes; - } - } - // if maxSourceSizeKBytes == 0 this implies the transformation is disabled - } - if (transformerDebug.isEnabled()) - { - transformerDebug.availableTransformers(transformers, -1, options, - "ContentService.getMaxSourceSizeBytes() = "+transformerDebug.fileSize(maxSourceSize*1024)); - } - return (maxSourceSize > 0) ? maxSourceSize * 1024 : maxSourceSize; - } - finally - { - transformerDebug.popAvailable(); - } - } - - /** - * - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. - */ - @Deprecated - public List getActiveTransformers(String sourceMimetype, String targetMimetype, TransformationOptions options) - { - return getActiveTransformers(sourceMimetype, -1, targetMimetype, options); - } - - /** - * - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. - */ - @Deprecated - public List getActiveTransformers(String sourceMimetype, long sourceSize, String targetMimetype, TransformationOptions options) - { - return transformerRegistry.getActiveTransformers(sourceMimetype, sourceSize, targetMimetype, options); - } - - /** - * @see org.alfresco.service.cmr.repository.ContentService#getImageTransformer() - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. - */ - @Deprecated - public ContentTransformer getImageTransformer() - { - return imageMagickContentTransformer; - } - - /** - * @see org.alfresco.repo.content.transform.ContentTransformerRegistry - * @see org.alfresco.repo.content.transform.ContentTransformer - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. - */ - @Deprecated - public boolean isTransformable(ContentReader reader, ContentWriter writer) - { - return isTransformable(reader, writer, new TransformationOptions()); - } - - /** - * @see org.alfresco.service.cmr.repository.ContentService#isTransformable(org.alfresco.service.cmr.repository.ContentReader, org.alfresco.service.cmr.repository.ContentWriter, org.alfresco.service.cmr.repository.TransformationOptions) - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. - */ - @Deprecated - public boolean isTransformable(ContentReader reader, ContentWriter writer, TransformationOptions options) - { - // check that source and target mimetypes are available - String sourceMimetype = reader.getMimetype(); - if (sourceMimetype == null) - { - throw new AlfrescoRuntimeException("The content reader mimetype must be set: " + reader); - } - String targetMimetype = writer.getMimetype(); - if (targetMimetype == null) - { - throw new AlfrescoRuntimeException("The content writer mimetype must be set: " + writer); - } - - long sourceSize = reader.getSize(); - try - { - // look for a transformer - transformerDebug.pushAvailable(reader.getContentUrl(), sourceMimetype, targetMimetype, options); - List transformers = getActiveTransformers(sourceMimetype, sourceSize, targetMimetype, options); - transformerDebug.availableTransformers(transformers, sourceSize, options, "ContentService.isTransformable(...)"); - - return transformers.size() > 0; - } - finally - { - transformerDebug.popAvailable(); - } - } - /** * Ensures that, upon closure of the output stream, the node is updated with * the latest URL of the content to which it refers. diff --git a/src/main/java/org/alfresco/repo/content/ContentTransformServiceAdaptor.java b/src/main/java/org/alfresco/repo/content/ContentTransformServiceAdaptor.java new file mode 100644 index 0000000000..874bcad9a6 --- /dev/null +++ b/src/main/java/org/alfresco/repo/content/ContentTransformServiceAdaptor.java @@ -0,0 +1,311 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2019 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.content; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.content.transform.AbstractContentTransformer2; +import org.alfresco.repo.content.transform.ContentTransformer; +import org.alfresco.repo.content.transform.LocalTransform; +import org.alfresco.repo.content.transform.LocalTransformServiceRegistry; +import org.alfresco.repo.content.transform.UnsupportedTransformationException; +import org.alfresco.repo.rendition2.LegacySynchronousTransformClient; +import org.alfresco.repo.rendition2.SynchronousTransformClient; +import org.alfresco.repo.rendition2.TransformationOptionsConverter; +import org.alfresco.service.cmr.repository.ContentIOException; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentTransformService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NoTransformerException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.TransformationOptions; +import org.alfresco.transform.client.registry.TransformServiceRegistry; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Contains deprecated methods originally from {@link org.alfresco.repo.content.ContentServiceImpl} that is used to + * perform Legacy transforms. + * + * @author adavis + */ +@Deprecated +public class ContentTransformServiceAdaptor implements ContentTransformService +{ + private ContentTransformer imageMagickContentTransformer; + private LegacySynchronousTransformClient legacySynchronousTransformClient; + private LocalTransformServiceRegistry localTransformServiceRegistryImpl; + private SynchronousTransformClient synchronousTransformClient; + private TransformServiceRegistry localTransformServiceRegistry; + private TransformationOptionsConverter converter; + + @Deprecated + public void setImageMagickContentTransformer(ContentTransformer imageMagickContentTransformer) + { + this.imageMagickContentTransformer = imageMagickContentTransformer; + } + + public void setLegacySynchronousTransformClient(LegacySynchronousTransformClient legacySynchronousTransformClient) + { + this.legacySynchronousTransformClient = legacySynchronousTransformClient; + } + + public void setLocalTransformServiceRegistryImpl(LocalTransformServiceRegistry localTransformServiceRegistryImpl) + { + this.localTransformServiceRegistryImpl = localTransformServiceRegistryImpl; + } + + public void setSynchronousTransformClient(SynchronousTransformClient synchronousTransformClient) + { + this.synchronousTransformClient = synchronousTransformClient; + } + + public void setLocalTransformServiceRegistry(TransformServiceRegistry localTransformServiceRegistry) + { + this.localTransformServiceRegistry = localTransformServiceRegistry; + } + + public void setConverter(TransformationOptionsConverter converter) + { + this.converter = converter; + } + + @Deprecated + @Override + public void transform(ContentReader reader, ContentWriter writer) + { + synchronousTransformClient.transform(reader, writer, Collections.emptyMap(), null, null); + } + + @Deprecated + @Override + public void transform(ContentReader reader, ContentWriter writer, Map legacyOptionsMap) + throws NoTransformerException, ContentIOException + { + TransformationOptions transformationOptions = new TransformationOptions(legacyOptionsMap); + Map options = converter.getOptions(transformationOptions); + synchronousTransformClient.transform(reader, writer, options, null, null); + } + + @Deprecated + @Override + public void transform(ContentReader reader, ContentWriter writer, TransformationOptions transformationOptions) + throws NoTransformerException, ContentIOException + { + try + { + Map options = converter.getOptions(transformationOptions); + synchronousTransformClient.transform(reader, writer, options, null, null); + } + catch (UnsupportedTransformationException ute) + { + throw newNoTransformerException(reader, writer); + } + catch (IllegalArgumentException iae) + { + if (iae.getMessage().contains("sourceNodeRef null has no content")) + { + throw newNoTransformerException(reader, writer); + } + throw new AlfrescoRuntimeException(iae.getMessage(), iae); + } + } + + private NoTransformerException newNoTransformerException(ContentReader reader, ContentWriter writer) + { + String sourceMimetype = reader == null ? "null" : reader.getMimetype(); + String targetMimetype = writer == null ? "null" : writer.getMimetype(); + return new NoTransformerException(sourceMimetype, targetMimetype); + } + + @Deprecated + @Override + public ContentTransformer getTransformer(String sourceMimetype, String targetMimetype) + { + return getTransformer(sourceMimetype, targetMimetype, new TransformationOptions()); + } + + @Deprecated + @Override + public ContentTransformer getTransformer(String sourceMimetype, String targetMimetype, TransformationOptions options) + { + return getTransformer(null, sourceMimetype, -1, targetMimetype, new TransformationOptions()); + } + + @Deprecated + @Override + public ContentTransformer getTransformer(String sourceUrl, String sourceMimetype, long sourceSize, + String targetMimetype, TransformationOptions options) + { + ContentTransformer localTransformer = wrapLocalTransformer(sourceUrl, sourceMimetype, sourceSize, targetMimetype, options); + return localTransformer == null + ? legacySynchronousTransformClient.getTransformer(sourceUrl, sourceMimetype, sourceSize, targetMimetype, options) + : localTransformer; + } + + @Deprecated + @Override + // Same as getActiveTransformers, but with debug + public List getTransformers(String sourceUrl, String sourceMimetype, long sourceSize, + String targetMimetype, TransformationOptions options) + { + ContentTransformer localTransformer = wrapLocalTransformer(sourceUrl, sourceMimetype, sourceSize, targetMimetype, options); + return localTransformer == null + ? legacySynchronousTransformClient.getTransformers(sourceUrl, sourceMimetype, sourceSize, targetMimetype, options) + : Collections.singletonList(localTransformer); + } + + @Deprecated + @Override + // Same as getTransformers, but without debug + public List getActiveTransformers(String sourceMimetype, + String targetMimetype, TransformationOptions options) + { + return getActiveTransformers(sourceMimetype, -1, targetMimetype, options); + } + + @Deprecated + @Override + // Same as getTransformers, but without debug + public List getActiveTransformers(String sourceMimetype, long sourceSize, + String targetMimetype, TransformationOptions options) + { + ContentTransformer localTransformer = wrapLocalTransformer(null, sourceMimetype, sourceSize, targetMimetype, options); + return localTransformer == null + ? legacySynchronousTransformClient.getActiveTransformers(sourceMimetype, sourceSize, targetMimetype, options) + : Collections.singletonList(localTransformer); + } + + private ContentTransformer wrapLocalTransformer(String sourceUrl, String sourceMimetype, long sourceSize, + String targetMimetype, TransformationOptions transformationOptions) + { + AbstractContentTransformer2 transformer = null; + Map options = converter.getOptions(transformationOptions); + LocalTransform localTransform = localTransformServiceRegistryImpl.getLocalTransform(sourceMimetype, + sourceSize, targetMimetype, options, null); + if (localTransform != null) + { + transformer = new AbstractContentTransformer2() { + + @Override + public void transform(ContentReader reader, ContentWriter writer, TransformationOptions options) + throws ContentIOException + { + try + { + transformInternal(reader, writer, transformationOptions); + } + catch (Exception e) + { + throw new ContentIOException(e.getMessage(), e); + } + } + + + @Override + protected void transformInternal(ContentReader reader, ContentWriter writer, TransformationOptions transformationOptions) throws Exception + { + localTransform.transform(reader, writer, options, null, null); + } + + @Override + public void register() + { + } + + @Override + public boolean isSupportedTransformation(String sourceMimetype, String targetMimetype, TransformationOptions options) + { + return true; + } + + @Override + public boolean isTransformable(String sourceMimetype, long sourceSize, String targetMimetype, TransformationOptions options) + { + return true; + } + + @Override + public boolean isTransformableMimetype(String sourceMimetype, String targetMimetype, TransformationOptions options) + { + return true; + } + + @Override + public boolean isTransformableSize(String sourceMimetype, long sourceSize, String targetMimetype, TransformationOptions options) + { + return true; + } + + @Override + public String getName() + { + return "Wrapped<"+localTransformServiceRegistryImpl.findTransformerName(sourceMimetype, sourceSize, + targetMimetype, options, null)+">"; + } + }; + } + return transformer; + } + + @Deprecated + @Override + public long getMaxSourceSizeBytes(String sourceMimetype, + String targetMimetype, TransformationOptions transformationOptions) + { + Map options = converter.getOptions(transformationOptions); + return localTransformServiceRegistry.findMaxSize(sourceMimetype, targetMimetype, options, null); + } + + @Deprecated + @Override + public ContentTransformer getImageTransformer() + { + return imageMagickContentTransformer; + } + + @Deprecated + @Override + public boolean isTransformable(ContentReader reader, ContentWriter writer) + { + return isTransformable(reader, writer, null); + } + + @Deprecated + @Override + public boolean isTransformable(ContentReader reader, ContentWriter writer, TransformationOptions transformationOptions) + { + String sourceMimetype = reader.getMimetype(); + long sourceSizeInBytes = reader.getSize(); + String contentUrl = reader.getContentUrl(); + String targetMimetype = writer.getMimetype(); + NodeRef sourceNodeRef = transformationOptions.getSourceNodeRef(); + Map options = converter.getOptions(transformationOptions); + return synchronousTransformClient.isSupported(sourceMimetype, sourceSizeInBytes, contentUrl, targetMimetype, + options, null, sourceNodeRef); + } +} diff --git a/src/main/java/org/alfresco/repo/content/transform/AbstractContentTransformer2.java b/src/main/java/org/alfresco/repo/content/transform/AbstractContentTransformer2.java index 0b6a8f5089..b6fd82aff7 100644 --- a/src/main/java/org/alfresco/repo/content/transform/AbstractContentTransformer2.java +++ b/src/main/java/org/alfresco/repo/content/transform/AbstractContentTransformer2.java @@ -216,7 +216,7 @@ public abstract class AbstractContentTransformer2 extends AbstractContentTransfo /** * @see org.alfresco.repo.content.transform.ContentTransformer#transform(org.alfresco.service.cmr.repository.ContentReader, org.alfresco.service.cmr.repository.ContentWriter, org.alfresco.service.cmr.repository.TransformationOptions) */ - public final void transform(ContentReader reader, ContentWriter writer, TransformationOptions options) + public void transform(ContentReader reader, ContentWriter writer, TransformationOptions options) throws ContentIOException { try diff --git a/src/main/java/org/alfresco/repo/content/transform/AbstractLocalTransform.java b/src/main/java/org/alfresco/repo/content/transform/AbstractLocalTransform.java index 7dff7edb69..96064404ec 100644 --- a/src/main/java/org/alfresco/repo/content/transform/AbstractLocalTransform.java +++ b/src/main/java/org/alfresco/repo/content/transform/AbstractLocalTransform.java @@ -84,7 +84,7 @@ public abstract class AbstractLocalTransform implements LocalTransform String sourceMimetype, String targetMimetype, String sourceExtension, String targetExtension, String renditionName, NodeRef sourceNodeRef) - throws Exception; + throws UnsupportedTransformationException, ContentIOException; public String getName() { @@ -99,7 +99,6 @@ public abstract class AbstractLocalTransform implements LocalTransform @Override public void transform(ContentReader reader, ContentWriter writer, Map transformOptions, String renditionName, NodeRef sourceNodeRef) - throws Exception { if (isAvailable()) { @@ -144,16 +143,15 @@ public abstract class AbstractLocalTransform implements LocalTransform private void transformWithDebug(ContentReader reader, ContentWriter writer, Map transformOptions, String renditionName, NodeRef sourceNodeRef, String sourceMimetype, String targetMimetype, - String sourceExtension, String targetExtension) throws Exception + String sourceExtension, String targetExtension) { - try { depth.set(depth.get()+1); if (transformerDebug.isEnabled()) { - transformerDebug.pushTransform(name, reader.getContentUrl(), sourceMimetype, + transformerDebug.pushTransform("Local:"+name, reader.getContentUrl(), sourceMimetype, targetMimetype, reader.getSize(), renditionName, sourceNodeRef); } @@ -173,7 +171,6 @@ public abstract class AbstractLocalTransform implements LocalTransform } private void strictMimetypeCheck(ContentReader reader, NodeRef sourceNodeRef, String declaredMimetype) - throws UnsupportedTransformationException { if (mimetypeService != null && strictMimeTypeCheck && depth.get() == 1) { @@ -222,7 +219,7 @@ public abstract class AbstractLocalTransform implements LocalTransform private void retryWithDifferentMimetype(ContentReader reader, ContentWriter writer, String targetMimetype, Map transformOptions, String renditionName, - NodeRef sourceNodeRef, Throwable e) throws Exception + NodeRef sourceNodeRef, Throwable e) { if (mimetypeService != null && localTransformServiceRegistry != null) { @@ -238,7 +235,7 @@ public abstract class AbstractLocalTransform implements LocalTransform } else { - transformerDebug.debug(" Failed: Mime type was '" + differentType + "'", e); + transformerDebug.debug(" Failed: Mimetype was '" + differentType + "'", e); String claimedMimetype = reader.getMimetype(); if (retryTransformOnDifferentMimeType) diff --git a/src/main/java/org/alfresco/repo/content/transform/ComplexContentTransformer.java b/src/main/java/org/alfresco/repo/content/transform/ComplexContentTransformer.java index 5bf9538de4..189e8eb03c 100644 --- a/src/main/java/org/alfresco/repo/content/transform/ComplexContentTransformer.java +++ b/src/main/java/org/alfresco/repo/content/transform/ComplexContentTransformer.java @@ -39,8 +39,8 @@ import java.util.Map; import org.alfresco.api.AlfrescoPublicApi; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.repo.content.filestore.FileContentWriter; +import org.alfresco.repo.rendition2.LegacySynchronousTransformClient; 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.NodeRef; import org.alfresco.service.cmr.repository.TransformationOptionLimits; @@ -84,7 +84,7 @@ public class ComplexContentTransformer extends AbstractContentTransformer2 imple private List transformers; private List intermediateMimetypes; private Map transformationOptionOverrides; - private ContentService contentService; + private LegacySynchronousTransformClient legacySynchronousTransformClient; public ComplexContentTransformer() { @@ -93,13 +93,11 @@ public class ComplexContentTransformer extends AbstractContentTransformer2 imple /** * The list of transformers to use. If any element is null * all possible transformers will be considered. If any element - * is null, the contentService property must be set. + * is null, the legacySynchronousTransformClient property must be set. *

* If a single transformer is supplied, then it will still be used. * * @param transformers list of at least one transformer - * - * @see #setContentService(ContentService) */ public void setTransformers(List transformers) { @@ -135,14 +133,9 @@ public class ComplexContentTransformer extends AbstractContentTransformer2 imple this.transformationOptionOverrides = transformationOptionOverrides; } - /** - * Sets the ContentService. Only required if {@code null} transformers - * are provided to {@link #setTransformers(List)}. - * @param contentService - */ - public void setContentService(ContentService contentService) + public void setLegacySynchronousTransformClient(LegacySynchronousTransformClient legacySynchronousTransformClient) { - this.contentService = contentService; + this.legacySynchronousTransformClient = legacySynchronousTransformClient; } /** @@ -167,9 +160,9 @@ public class ComplexContentTransformer extends AbstractContentTransformer2 imple { if (transformer == null) { - if (contentService == null) + if (legacySynchronousTransformClient == null) { - throw new AlfrescoRuntimeException("'contentService' is a required property if " + + throw new AlfrescoRuntimeException("'legacySynchronousTransformClient' is a required property if " + "there are any null (dynamic) transformers"); } break; @@ -297,7 +290,7 @@ public class ComplexContentTransformer extends AbstractContentTransformer2 imple { parentTransformers.get().push(this); @SuppressWarnings("deprecation") - List firstTansformers = contentService.getActiveTransformers( + List firstTansformers = legacySynchronousTransformClient.getActiveTransformers( currentSourceMimetype, -1, currentTargetMimetype, options); if (firstTansformers.isEmpty()) { @@ -344,7 +337,7 @@ public class ComplexContentTransformer extends AbstractContentTransformer2 imple { parentTransformers.get().push(this); @SuppressWarnings("deprecation") - List firstTansformers = contentService.getActiveTransformers( + List firstTansformers = legacySynchronousTransformClient.getActiveTransformers( sourceMimetype, -1, firstTargetMimetype, options); pageLimitSupported = !firstTansformers.isEmpty(); if (pageLimitSupported) @@ -397,7 +390,7 @@ public class ComplexContentTransformer extends AbstractContentTransformer2 imple { parentTransformers.get().push(this); @SuppressWarnings("deprecation") - List firstTansformers = contentService.getActiveTransformers( + List firstTansformers = legacySynchronousTransformClient.getActiveTransformers( sourceMimetype, -1, firstTargetMimetype, options); if (!firstTansformers.isEmpty()) { @@ -480,7 +473,7 @@ public class ComplexContentTransformer extends AbstractContentTransformer2 imple try { parentTransformers.get().push(this); - contentService.transform(currentReader, currentWriter, options); + legacySynchronousTransformClient.transform(currentReader, currentWriter, options); } finally { diff --git a/src/main/java/org/alfresco/repo/content/transform/LocalFailoverTransform.java b/src/main/java/org/alfresco/repo/content/transform/LocalFailoverTransform.java index 9fafefc060..a78648d3ac 100644 --- a/src/main/java/org/alfresco/repo/content/transform/LocalFailoverTransform.java +++ b/src/main/java/org/alfresco/repo/content/transform/LocalFailoverTransform.java @@ -26,6 +26,7 @@ package org.alfresco.repo.content.transform; import org.alfresco.repo.content.filestore.FileContentWriter; +import org.alfresco.service.cmr.repository.ContentIOException; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.MimetypeService; @@ -77,14 +78,14 @@ public class LocalFailoverTransform extends AbstractLocalTransform ContentWriter writer, Map transformOptions, String sourceMimetype, String targetMimetype, String sourceExtension, String targetExtension, - String renditionName, NodeRef sourceNodeRef) throws Exception + String renditionName, NodeRef sourceNodeRef) { final String targetExt = mimetypeService.getExtension(targetMimetype); // We need to keep a reference to thrown exceptions as we're going to catch them and // then move on to the next transformer. In the event that they all fail, we will throw // the first exception. - Exception transformationException = null; + RuntimeException transformationException = null; for (int i = 0; i < transformers.size(); i++) { @@ -111,7 +112,7 @@ public class LocalFailoverTransform extends AbstractLocalTransform // attempt to transform stepTransformer.transform(reader, currentWriter, transformOptions, renditionName, sourceNodeRef); } - catch (Exception are) + catch (UnsupportedTransformationException | ContentIOException are) { if (transformationException == null) { diff --git a/src/main/java/org/alfresco/repo/content/transform/LocalPipelineTransform.java b/src/main/java/org/alfresco/repo/content/transform/LocalPipelineTransform.java index d31dc4d921..2250818b1d 100644 --- a/src/main/java/org/alfresco/repo/content/transform/LocalPipelineTransform.java +++ b/src/main/java/org/alfresco/repo/content/transform/LocalPipelineTransform.java @@ -91,7 +91,7 @@ public class LocalPipelineTransform extends AbstractLocalTransform ContentWriter writer, Map transformOptions, String sourceMimetype, String targetMimetype, String sourceExtension, String targetExtension, - String renditionName, NodeRef sourceNodeRef) throws Exception + String renditionName, NodeRef sourceNodeRef) { ContentReader currentReader = reader; int lastI = transformers.size() - 1; diff --git a/src/main/java/org/alfresco/repo/content/transform/LocalTransform.java b/src/main/java/org/alfresco/repo/content/transform/LocalTransform.java index 2a7bc61110..7b989796e3 100644 --- a/src/main/java/org/alfresco/repo/content/transform/LocalTransform.java +++ b/src/main/java/org/alfresco/repo/content/transform/LocalTransform.java @@ -25,6 +25,7 @@ */ package org.alfresco.repo.content.transform; +import org.alfresco.service.cmr.repository.ContentIOException; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; @@ -36,7 +37,21 @@ import java.util.Map; */ public interface LocalTransform { + /** + * Requests a synchronous transform. + * @param reader of the source content + * @param writer to the target node's content + * @param transformOptions the actual name value pairs available that could be passed to the Transform Service. + * @param renditionName (optional) name for the set of options and target mimetype. If supplied is used to cache + * results to avoid having to work out if a given transformation is supported a second time. The sourceMimetype + * and sourceSizeInBytes may still change. In the case of ACS this is the rendition name. + * @param sourceNodeRef the source node + * @throws UnsupportedTransformationException if there is an unexpected failure to transform, normally in a + * pipeline, where an intermediate transform may not be performed after all because an intermediate + * converion is too big. + * @throws ContentIOException there is an unexpected communication or transformation failure. + */ void transform(ContentReader reader, ContentWriter writer, Map transformOptions, String renditionName, NodeRef sourceNodeRef) - throws Exception; + throws UnsupportedTransformationException, ContentIOException; } diff --git a/src/main/java/org/alfresco/repo/content/transform/LocalTransformImpl.java b/src/main/java/org/alfresco/repo/content/transform/LocalTransformImpl.java index 2eb2b474ce..5aad930980 100644 --- a/src/main/java/org/alfresco/repo/content/transform/LocalTransformImpl.java +++ b/src/main/java/org/alfresco/repo/content/transform/LocalTransformImpl.java @@ -144,7 +144,7 @@ public class LocalTransformImpl extends AbstractLocalTransform ContentWriter writer, Map transformOptions, String sourceMimetype, String targetMimetype, String sourceExtension, String targetExtension, - String renditionName, NodeRef sourceNodeRef) throws Exception + String renditionName, NodeRef sourceNodeRef) { // Only pass the sourceEncoding and other dynamic values like it if they were supplied in the rendition // definition without a value. The sourceEncoding value is also supplied in the RenditionEventProducer in diff --git a/src/main/java/org/alfresco/repo/content/transform/RemoteTransformerClient.java b/src/main/java/org/alfresco/repo/content/transform/RemoteTransformerClient.java index de0d7e680c..dd30540ff3 100644 --- a/src/main/java/org/alfresco/repo/content/transform/RemoteTransformerClient.java +++ b/src/main/java/org/alfresco/repo/content/transform/RemoteTransformerClient.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2018 Alfresco Software Limited + * Copyright (C) 2005 - 2019 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -86,11 +86,11 @@ public class RemoteTransformerClient } public void request(ContentReader reader, ContentWriter writer, String sourceMimetype, String sourceExtension, - String targetExtension, long timeoutMs, Log logger, String... args) throws IllegalAccessException + String targetExtension, long timeoutMs, Log logger, String... args) { if (args.length % 2 != 0) { - throw new IllegalAccessException("There should be a value for each request property"); + throw new IllegalArgumentException("There should be a value for each request property"); } StringJoiner sj = new StringJoiner(" "); diff --git a/src/main/java/org/alfresco/repo/content/transform/RuntimeExecutableContentTransformerOptions.java b/src/main/java/org/alfresco/repo/content/transform/RuntimeExecutableContentTransformerOptions.java index 7ed61342f9..e7b5be9d57 100644 --- a/src/main/java/org/alfresco/repo/content/transform/RuntimeExecutableContentTransformerOptions.java +++ b/src/main/java/org/alfresco/repo/content/transform/RuntimeExecutableContentTransformerOptions.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2019 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -56,7 +56,12 @@ public class RuntimeExecutableContentTransformerOptions extends TransformationOp { this.propertyValues = propertyValues; } - + + public Map getPropertyValues() + { + return propertyValues; + } + /** * Overrides the base class implementation to add all values set in {@link #setPropertyValues(Map)} */ diff --git a/src/main/java/org/alfresco/repo/content/transform/TransformerConfigDynamicTransformers.java b/src/main/java/org/alfresco/repo/content/transform/TransformerConfigDynamicTransformers.java index 89bb6731d7..a37021da49 100644 --- a/src/main/java/org/alfresco/repo/content/transform/TransformerConfigDynamicTransformers.java +++ b/src/main/java/org/alfresco/repo/content/transform/TransformerConfigDynamicTransformers.java @@ -41,6 +41,7 @@ import java.util.Properties; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.rendition2.LegacySynchronousTransformClient; import org.alfresco.service.cmr.module.ModuleService; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.MimetypeService; @@ -63,16 +64,16 @@ public class TransformerConfigDynamicTransformers extends TransformerPropertyNam private final List dynamicTransformers = new ArrayList(); public TransformerConfigDynamicTransformers(TransformerConfig transformerConfig, TransformerProperties transformerProperties, - MimetypeService mimetypeService, ContentService contentService, ContentTransformerRegistry transformerRegistry, + MimetypeService mimetypeService, LegacySynchronousTransformClient legacySynchronousTransformClient, ContentTransformerRegistry transformerRegistry, TransformerDebug transformerDebug, ModuleService moduleService, DescriptorService descriptorService, Properties globalProperties) { - createDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + createDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, legacySynchronousTransformClient, transformerRegistry, transformerDebug, moduleService, descriptorService, globalProperties); } private void createDynamicTransformers(TransformerConfig transformerConfig, TransformerProperties transformerProperties, - MimetypeService mimetypeService, ContentService contentService, ContentTransformerRegistry transformerRegistry, + MimetypeService mimetypeService, LegacySynchronousTransformClient legacySynchronousTransformClient, ContentTransformerRegistry transformerRegistry, TransformerDebug transformerDebug, ModuleService moduleService, DescriptorService descriptorService, Properties globalProperties) { @@ -124,10 +125,10 @@ public class TransformerConfigDynamicTransformers extends TransformerPropertyNam AbstractContentTransformer2 transformer = property.suffix.equals(PIPELINE) ? createComplexTransformer(property, transformerConfig, mimetypeService, - contentService, transformerRegistry, transformerDebug, available, + legacySynchronousTransformClient, transformerRegistry, transformerDebug, available, globalProperties) : createFailoverTransformer(property, transformerConfig, mimetypeService, - contentService, transformerRegistry, transformerDebug, available, + transformerRegistry, transformerDebug, available, globalProperties); transformer.register(); processed.add(property); @@ -174,7 +175,7 @@ public class TransformerConfigDynamicTransformers extends TransformerPropertyNam private AbstractContentTransformer2 createComplexTransformer(TransformerSourceTargetSuffixValue property, TransformerConfig transformerConfig, - MimetypeService mimetypeService, ContentService contentService, + MimetypeService mimetypeService, LegacySynchronousTransformClient legacySynchronousTransformClient, ContentTransformerRegistry transformerRegistry, TransformerDebug transformerDebug, boolean available, Properties globalProperties) { @@ -192,11 +193,11 @@ public class TransformerConfigDynamicTransformers extends TransformerPropertyNam return getCommentNameAndAvailable(true); // suppress the ...available=false line as it is reported anyway if set } }; - setupContentTransformer2(property, transformerConfig, mimetypeService, contentService, + setupContentTransformer2(property, transformerConfig, mimetypeService, transformerRegistry, transformerDebug, available, transformer, transformers, globalProperties); // baseComplexContentTransformer - transformer.setContentService(contentService); + transformer.setLegacySynchronousTransformClient(legacySynchronousTransformClient); // ComplexContentTransformer transformer.setTransformers(transformers); @@ -207,7 +208,7 @@ public class TransformerConfigDynamicTransformers extends TransformerPropertyNam private AbstractContentTransformer2 createFailoverTransformer(TransformerSourceTargetSuffixValue property, TransformerConfig transformerConfig, - MimetypeService mimetypeService, ContentService contentService, + MimetypeService mimetypeService, ContentTransformerRegistry transformerRegistry, TransformerDebug transformerDebug, boolean available, Properties globalProperties) { @@ -224,7 +225,7 @@ public class TransformerConfigDynamicTransformers extends TransformerPropertyNam return getCommentNameAndAvailable(true); // suppress the ...available=false line as it is reported anyway if set } }; - setupContentTransformer2(property, transformerConfig, mimetypeService, contentService, + setupContentTransformer2(property, transformerConfig, mimetypeService, transformerRegistry, transformerDebug, available, transformer, transformers, globalProperties); // FailoverContentTransformer @@ -292,7 +293,7 @@ public class TransformerConfigDynamicTransformers extends TransformerPropertyNam // Set properties common to ComplexContentTransformer and FailoverContentTransformer. private void setupContentTransformer2(TransformerSourceTargetSuffixValue property, TransformerConfig transformerConfig, MimetypeService mimetypeService, - ContentService contentService, ContentTransformerRegistry transformerRegistry, + ContentTransformerRegistry transformerRegistry, TransformerDebug transformerDebug, boolean available, AbstractContentTransformer2 transformer, List transformers, Properties globalProperties) diff --git a/src/main/java/org/alfresco/repo/content/transform/TransformerConfigImpl.java b/src/main/java/org/alfresco/repo/content/transform/TransformerConfigImpl.java index 9078679962..093e37ca95 100644 --- a/src/main/java/org/alfresco/repo/content/transform/TransformerConfigImpl.java +++ b/src/main/java/org/alfresco/repo/content/transform/TransformerConfigImpl.java @@ -33,8 +33,8 @@ import java.util.Properties; import java.util.Set; import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory; +import org.alfresco.repo.rendition2.LegacySynchronousTransformClient; import org.alfresco.service.cmr.module.ModuleService; -import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.MalformedNodeRefException; import org.alfresco.service.cmr.repository.MimetypeService; import org.alfresco.service.cmr.repository.NodeRef; @@ -60,7 +60,7 @@ public class TransformerConfigImpl extends AbstractLifecycleBean implements Tran private MimetypeService mimetypeService; - private ContentService contentService; + private LegacySynchronousTransformClient legacySynchronousTransformClient; private ContentTransformerRegistry transformerRegistry; @@ -131,9 +131,9 @@ public class TransformerConfigImpl extends AbstractLifecycleBean implements Tran this.mimetypeService = mimetypeService; } - public void setContentService(ContentService contentService) + public void setLegacySynchronousTransformClient(LegacySynchronousTransformClient legacySynchronousTransformClient) { - this.contentService = contentService; + this.legacySynchronousTransformClient = legacySynchronousTransformClient; } public void setContentTransformerRegistry(ContentTransformerRegistry transformerRegistry) @@ -180,7 +180,7 @@ public class TransformerConfigImpl extends AbstractLifecycleBean implements Tran transformerProperties = new TransformerProperties(subsystem, globalProperties); dynamicTransformers = new TransformerConfigDynamicTransformers(this, transformerProperties, mimetypeService, - contentService, transformerRegistry, transformerDebug, moduleService, descriptorService, globalProperties); + legacySynchronousTransformClient, transformerRegistry, transformerDebug, moduleService, descriptorService, globalProperties); statistics= new TransformerConfigStatistics(this, mimetypeService); limits = new TransformerConfigLimits(transformerProperties, mimetypeService); supported = new TransformerConfigSupported(transformerProperties, mimetypeService); diff --git a/src/main/java/org/alfresco/repo/content/transform/TransformerDebug.java b/src/main/java/org/alfresco/repo/content/transform/TransformerDebug.java index 76b135a760..5b07b03b7d 100644 --- a/src/main/java/org/alfresco/repo/content/transform/TransformerDebug.java +++ b/src/main/java/org/alfresco/repo/content/transform/TransformerDebug.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2019 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -710,6 +710,16 @@ public class TransformerDebug implements ApplicationContextAware } } + public void debug(String sourceMimetype, String targetMimetype, NodeRef sourceNodeRef, long sourceSize, + String renditionName, String message) + { + String fileName = getFileName(sourceNodeRef, true, -1); + log(" "+getMimetypeExt(sourceMimetype)+getMimetypeExt(targetMimetype) + + ((fileName != null) ? fileName+' ' : "")+ + ((sourceSize >= 0) ? fileSize(sourceSize)+' ' : "") + + (renditionName != null ? "-- "+renditionName+" -- " : "") + message); + } + /** * Called after working out what transformers are available and any * resulting transform has been called. @@ -1369,7 +1379,7 @@ public class TransformerDebug implements ApplicationContextAware boolean componentTransformer = isComponentTransformer(transformer); - StringBuilder sb = new StringBuilder(name); + StringBuilder sb = new StringBuilder("Legacy:").append(name); if (componentTransformer || type.length() > 0) { sb.append("<<"); @@ -1671,7 +1681,6 @@ public class TransformerDebug implements ApplicationContextAware ContentReader reader = contentService.getReader(sourceNodeRef, ContentModel.PROP_CONTENT); SynchronousTransformClient synchronousTransformClient = getSynchronousTransformClient(); Map actualOptions = Collections.emptyMap(); - synchronousTransformClient.isSupported(sourceNodeRef, targetMimetype, actualOptions, null, nodeService); synchronousTransformClient.transform(reader, writer, actualOptions, null, sourceNodeRef); } catch (Exception e) diff --git a/src/main/java/org/alfresco/repo/jscript/ScriptNode.java b/src/main/java/org/alfresco/repo/jscript/ScriptNode.java index 35a1b675c2..4691511f32 100644 --- a/src/main/java/org/alfresco/repo/jscript/ScriptNode.java +++ b/src/main/java/org/alfresco/repo/jscript/ScriptNode.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2019 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -66,6 +66,7 @@ import org.alfresco.repo.node.getchildren.GetChildrenCannedQuery; import org.alfresco.repo.rendition2.RenditionDefinition2; import org.alfresco.repo.rendition2.RenditionDefinitionRegistry2; import org.alfresco.repo.rendition2.RenditionService2; +import org.alfresco.repo.rendition2.SynchronousTransformClient; import org.alfresco.repo.search.QueryParameterDefImpl; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; @@ -92,6 +93,7 @@ import org.alfresco.service.cmr.model.FileNotFoundException; import org.alfresco.service.cmr.repository.AssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.ContentIOException; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentWriter; @@ -100,7 +102,6 @@ import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.Path; import org.alfresco.service.cmr.repository.TemplateImageResolver; -import org.alfresco.service.cmr.repository.TransformationOptions; import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; import org.alfresco.service.cmr.search.QueryParameterDefinition; import org.alfresco.service.cmr.security.AccessPermission; @@ -208,6 +209,7 @@ public class ScriptNode implements Scopeable, NamespacePrefixResolverProvider private FileFolderService fileFolderService = null; private RenditionService2 renditionService2; private RenditionDefinitionRegistry2 renditionDefinitionRegistry2; + private SynchronousTransformClient synchronousTransformClient; private RetryingTransactionHelper retryingTransactionHelper = null; private Boolean isDocument = null; private Boolean isContainer = null; @@ -279,6 +281,7 @@ public class ScriptNode implements Scopeable, NamespacePrefixResolverProvider this.scope = scope; renditionService2 = services.getRenditionService2(); renditionDefinitionRegistry2 = renditionService2.getRenditionDefinitionRegistry2(); + synchronousTransformClient = services.getSynchronousTransformClient(); } @Override @@ -2742,18 +2745,31 @@ public class ScriptNode implements Scopeable, NamespacePrefixResolverProvider // the delegate definition for transforming a document Transformer transformer = new AbstractTransformer() { - protected void doTransform(ContentService contentService, - ContentReader reader, ContentWriter writer) + protected void doTransform(SynchronousTransformClient synchronousTransformClient, ContentReader reader, ContentWriter writer) { - TransformationOptions options = new TransformationOptions(); - options.setSourceNodeRef(sourceNodeRef); - contentService.transform(reader, writer, options); + transformNodeRef(synchronousTransformClient, reader, writer, Collections.emptyMap(), sourceNodeRef); } }; return transformNode(transformer, mimetype, destination); } - + + private void transformNodeRef(SynchronousTransformClient synchronousTransformClient, + ContentReader reader, ContentWriter writer, + Map actualOptions, NodeRef sourceNodeRef) + { + try + { + synchronousTransformClient.transform(reader, writer, actualOptions, null, sourceNodeRef); + } + catch (Exception e) + { + throw new ContentIOException("Content conversion failed: \n" + + " reader: " + reader + "\n" + + " writer: " + writer + "\n", e); + } + } + /** * Generic method to transform Node content from one mimetype to another. * @@ -2791,7 +2807,7 @@ public class ScriptNode implements Scopeable, NamespacePrefixResolverProvider writer.setEncoding(reader.getEncoding()); // original encoding // Try and transform the content using the supplied delegate - transformedNode = transformer.transform(contentService, copyNodeRef, reader, writer); + transformedNode = transformer.transform(synchronousTransformClient, copyNodeRef, reader, writer); } return transformedNode; @@ -2864,17 +2880,16 @@ public class ScriptNode implements Scopeable, NamespacePrefixResolverProvider // the delegate definition for transforming an image Transformer transformer = new AbstractTransformer() { - protected void doTransform(ContentService contentService, - ContentReader reader, ContentWriter writer) + protected void doTransform(SynchronousTransformClient synchronousTransformClient, ContentReader reader, ContentWriter writer) { - ImageTransformationOptions imageOptions = new ImageTransformationOptions(); - imageOptions.setSourceNodeRef(sourceNodeRef); - - if (options != null) + Map actualOptions = Collections.emptyMap(); + if (options != null || !options.trim().isEmpty()) { - imageOptions.setCommandOptions(options); + // TODO it might be possible to extract some of the 'known ones' into actualOptions. + throw new IllegalArgumentException("ImageMagick commandOptions '"+options+ + "' may no longer be passed blindly to the transformer for security reasons."); } - contentService.getImageTransformer().transform(reader, writer, imageOptions); + transformNodeRef(synchronousTransformClient, reader, writer, actualOptions, sourceNodeRef); } }; @@ -4155,27 +4170,27 @@ public class ScriptNode implements Scopeable, NamespacePrefixResolverProvider /** * Transform the reader to the specified writer * - * @param contentService ContentService + * @param synchronousTransformClient * @param noderef NodeRef of the destination for the transform * @param reader Source reader * @param writer Destination writer - * + * * @return Node representing the transformed entity */ - ScriptNode transform(ContentService contentService, NodeRef noderef, + ScriptNode transform(SynchronousTransformClient synchronousTransformClient, NodeRef noderef, ContentReader reader, ContentWriter writer); } private abstract class AbstractTransformer implements Transformer { - public ScriptNode transform(ContentService contentService, NodeRef nodeRef, + public ScriptNode transform(SynchronousTransformClient synchronousTransformClient, NodeRef nodeRef, ContentReader reader, ContentWriter writer) { ScriptNode transformedNode = null; try { - doTransform(contentService, reader, writer); + doTransform(synchronousTransformClient, reader, writer); transformedNode = newInstance(nodeRef, services, scope); } catch (NoTransformerException e) @@ -4205,7 +4220,7 @@ public class ScriptNode implements Scopeable, NamespacePrefixResolverProvider return transformedNode; } - protected abstract void doTransform(ContentService contentService, + protected abstract void doTransform(SynchronousTransformClient synchronousTransformClient, ContentReader reader, ContentWriter writer); }; diff --git a/src/main/java/org/alfresco/repo/rendition/executer/AbstractTransformationRenderingEngine.java b/src/main/java/org/alfresco/repo/rendition/executer/AbstractTransformationRenderingEngine.java index 02c1325826..75c6a85407 100644 --- a/src/main/java/org/alfresco/repo/rendition/executer/AbstractTransformationRenderingEngine.java +++ b/src/main/java/org/alfresco/repo/rendition/executer/AbstractTransformationRenderingEngine.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2019 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -29,6 +29,7 @@ package org.alfresco.repo.rendition.executer; import java.io.Serializable; import java.util.Collection; import java.util.Date; +import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; @@ -36,12 +37,12 @@ import java.util.concurrent.Executors; import java.util.concurrent.FutureTask; import org.alfresco.repo.action.ParameterDefinitionImpl; -import org.alfresco.repo.content.transform.ContentTransformer; import org.alfresco.repo.content.transform.TransformerConfig; -import org.alfresco.repo.content.transform.TransformerDebug; import org.alfresco.repo.content.transform.UnsupportedTransformationException; import org.alfresco.repo.rendition2.RenditionService2Impl; +import org.alfresco.repo.rendition2.SynchronousTransformClient; +import org.alfresco.repo.rendition2.TransformationOptionsConverter; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.service.cmr.action.ActionServiceException; import org.alfresco.service.cmr.action.ActionTrackingService; @@ -54,6 +55,7 @@ import org.alfresco.service.cmr.rendition.RenditionServiceException; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NoTransformerException; +import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.TransformationOptionLimits; import org.alfresco.service.cmr.repository.TransformationOptions; import org.alfresco.service.cmr.repository.TransformationSourceOptions; @@ -140,7 +142,10 @@ public abstract class AbstractTransformationRenderingEngine extends AbstractRend * transforms. */ private ExecutorService executorService; - + + private SynchronousTransformClient synchronousTransformClient; + private TransformationOptionsConverter converter; + /** * Gets the ExecutorService to be used for cancel-aware * transforms. @@ -155,7 +160,17 @@ public abstract class AbstractTransformationRenderingEngine extends AbstractRend { return executorService; } - + + public void setSynchronousTransformClient(SynchronousTransformClient synchronousTransformClient) + { + this.synchronousTransformClient = synchronousTransformClient; + } + + public void setConverter(TransformationOptionsConverter converter) + { + this.converter = converter; + } + public void init() { super.init(); @@ -174,37 +189,22 @@ public abstract class AbstractTransformationRenderingEngine extends AbstractRend { ContentReader contentReader = context.makeContentReader(); // There will have been an exception if there is no content data so contentReader is not null. - String sourceUrl = contentReader.getContentUrl(); + String contentUrl = contentReader.getContentUrl(); String sourceMimeType = contentReader.getMimetype(); String targetMimeType = getTargetMimeType(context); // The child NodeRef gets created here - TransformationOptions options = getTransformOptions(context); + TransformationOptions transformationOptions = getTransformOptions(context); - // Log the following getTransform() as trace so we can see the wood for the trees - ContentTransformer transformer; - boolean orig = TransformerDebug.setDebugOutput(false); - try - { - transformer = this.contentService.getTransformer(sourceUrl, sourceMimeType, contentReader.getSize(), targetMimeType, options); - } - finally - { - TransformerDebug.setDebugOutput(orig); - } - - if (null == transformer) - { - // There's no transformer available for the requested rendition! - throw new RenditionServiceException(String.format(TRANSFORMER_NOT_EXISTS_MESSAGE_PATTERN, sourceMimeType, - targetMimeType)); - } - - if (!transformer.isTransformable(sourceMimeType, contentReader.getSize(), targetMimeType, options)) + long sourceSizeInBytes = contentReader.getSize(); + Map options = converter.getOptions(transformationOptions); + NodeRef sourceNodeRef = transformationOptions.getSourceNodeRef(); + if (!synchronousTransformClient.isSupported(sourceMimeType, sourceSizeInBytes, contentUrl, targetMimeType, + options, null, sourceNodeRef)) { throw new RenditionServiceException(String.format(NOT_TRANSFORMABLE_MESSAGE_PATTERN, sourceMimeType, targetMimeType)); } - + long startTime = new Date().getTime(); boolean actionCancelled = false; boolean actionCompleted = false; @@ -226,7 +226,7 @@ public abstract class AbstractTransformationRenderingEngine extends AbstractRend // Call the transform in a different thread so we can move on if cancelled FutureTask transformTask = new FutureTask( - new TransformationCallable(contentReader, targetMimeType, options, context, + new TransformationCallable(contentReader, targetMimeType, transformationOptions, context, AuthenticationUtil.getFullyAuthenticatedUser())); getExecutorService().execute(transformTask); @@ -242,8 +242,8 @@ public abstract class AbstractTransformationRenderingEngine extends AbstractRend break; } // Check timeout in case transformer doesn't obey it - if (options.getTimeoutMs() > 0 && - new Date().getTime() - startTime > (options.getTimeoutMs() + CANCELLED_ACTION_POLLING_INTERVAL)) + if (transformationOptions.getTimeoutMs() > 0 && + new Date().getTime() - startTime > (transformationOptions.getTimeoutMs() + CANCELLED_ACTION_POLLING_INTERVAL)) { // We hit a timeout, let the transform thread continue but results will be ignored if (logger.isDebugEnabled()) @@ -418,16 +418,16 @@ public abstract class AbstractTransformationRenderingEngine extends AbstractRend { private ContentReader contentReader; private String targetMimeType; - private TransformationOptions options; + private Map options; private RenderingContext context; private String initiatingUsername; - + public TransformationCallable(ContentReader contentReader, String targetMimeType, - TransformationOptions options, RenderingContext context, String initiatingUsername) + TransformationOptions transformationOptions, RenderingContext context, String initiatingUsername) { this.contentReader = contentReader; this.targetMimeType = targetMimeType; - this.options = options; + this.options = converter.getOptions(transformationOptions); this.context = context; this.initiatingUsername = initiatingUsername; } @@ -446,10 +446,13 @@ public abstract class AbstractTransformationRenderingEngine extends AbstractRend { // ALF-15715: Use temporary write to avoid operating on the real node for fear of row locking while long transforms are in progress ContentWriter tempContentWriter = contentService.getTempWriter(); + String renditionName = context.getDefinition().getRenditionName().getLocalName(); + NodeRef sourceNode = context.getSourceNode(); tempContentWriter.setMimetype(targetMimeType); try { - contentService.transform(contentReader, tempContentWriter, options); + synchronousTransformClient.transform(contentReader, tempContentWriter, options, + renditionName, sourceNode); return tempContentWriter; } catch (NoTransformerException|UnsupportedTransformationException ntx) diff --git a/src/main/java/org/alfresco/repo/rendition2/ContentTransformServiceImpl.java b/src/main/java/org/alfresco/repo/rendition2/ContentTransformServiceImpl.java new file mode 100644 index 0000000000..557934b71a --- /dev/null +++ b/src/main/java/org/alfresco/repo/rendition2/ContentTransformServiceImpl.java @@ -0,0 +1,362 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2019 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.rendition2; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.content.filestore.FileContentWriter; +import org.alfresco.repo.content.transform.ContentTransformer; +import org.alfresco.repo.content.transform.ContentTransformerRegistry; +import org.alfresco.repo.content.transform.TransformerDebug; +import org.alfresco.repo.content.transform.UnimportantTransformException; +import org.alfresco.repo.content.transform.UnsupportedTransformationException; +import org.alfresco.service.cmr.repository.ContentIOException; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.MimetypeService; +import org.alfresco.service.cmr.repository.NoTransformerException; +import org.alfresco.service.cmr.repository.TransformationOptions; +import org.alfresco.util.PropertyCheck; +import org.alfresco.util.TempFileProvider; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.InitializingBean; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Contains deprecated code originally from {@link org.alfresco.repo.content.ContentServiceImpl} that is used to perform + * Legacy transforms. + * + * @author adavis + */ +@Deprecated +public abstract class ContentTransformServiceImpl implements InitializingBean +{ + protected static Log logger = LogFactory.getLog(LegacyTransformClient.class); + + private MimetypeService mimetypeService; + private ContentTransformerRegistry transformerRegistry; + private TransformerDebug transformerDebug; + + private boolean transformerFailover = true; + + public void setMimetypeService(MimetypeService mimetypeService) + { + this.mimetypeService = mimetypeService; + } + + public void setTransformerRegistry(ContentTransformerRegistry transformerRegistry) + { + this.transformerRegistry = transformerRegistry; + } + + public void setTransformerDebug(TransformerDebug transformerDebug) + { + this.transformerDebug = transformerDebug; + } + + public void setTransformerFailover(boolean transformerFailover) + { + this.transformerFailover = transformerFailover; + } + + @Override + public void afterPropertiesSet() throws Exception + { + PropertyCheck.mandatory(this, "mimetypeService", mimetypeService); + PropertyCheck.mandatory(this, "transformerRegistry", transformerRegistry); + PropertyCheck.mandatory(this, "transformerDebug", transformerDebug); + } + + @Deprecated + public void transform(ContentReader reader, ContentWriter writer, TransformationOptions options) + throws NoTransformerException, ContentIOException + { + // check that source and target mimetypes are available + if (reader == null) + { + throw new AlfrescoRuntimeException("The content reader must be set"); + } + String sourceMimetype = reader.getMimetype(); + if (sourceMimetype == null) + { + throw new AlfrescoRuntimeException("The content reader mimetype must be set: " + reader); + } + String targetMimetype = writer.getMimetype(); + if (targetMimetype == null) + { + throw new AlfrescoRuntimeException("The content writer mimetype must be set: " + writer); + } + + long sourceSize = reader.getSize(); + try + { + // look for a transformer + transformerDebug.pushAvailable(reader.getContentUrl(), sourceMimetype, targetMimetype, options); + List transformers = getActiveTransformers(sourceMimetype, sourceSize, targetMimetype, options); + transformerDebug.availableTransformers(transformers, sourceSize, options, "ContentService.transform(...)"); + + int count = transformers.size(); + if (count == 0) + { + throw new NoTransformerException(sourceMimetype, targetMimetype); + } + + if (count == 1 || !transformerFailover) + { + ContentTransformer transformer = transformers.size() == 0 ? null : transformers.get(0); + transformer.transform(reader, writer, options); + } + else + { + failoverTransformers(reader, writer, options, targetMimetype, transformers); + } + } + finally + { + if (transformerDebug.isEnabled()) + { + transformerDebug.popAvailable(); + debugTransformations(sourceMimetype, targetMimetype, sourceSize, options); + } + } + } + + private void failoverTransformers(ContentReader reader, ContentWriter writer, + TransformationOptions options, String targetMimetype, + List transformers) + { + List exceptions = null; + boolean done = false; + try + { + // Try the best transformer and then the next if it fails + // and so on down the list + char c = 'a'; + String outputFileExt = mimetypeService.getExtension(targetMimetype); + for (ContentTransformer transformer : transformers) + { + ContentWriter currentWriter = writer; + File tempFile = null; + try + { + // We can't know in advance which of the + // available transformer will work - if any. + // We can't write into the ContentWriter stream. + // So make a temporary file writer with the + // current transformer name. + tempFile = TempFileProvider.createTempFile( + "FailoverTransformer_intermediate_" + + transformer.getClass().getSimpleName() + "_", "." + + outputFileExt); + currentWriter = new FileContentWriter(tempFile); + currentWriter.setMimetype(targetMimetype); + currentWriter.setEncoding(writer.getEncoding()); + + if (c != 'a' && transformerDebug.isEnabled()) + { + transformerDebug.debug(""); + transformerDebug.debug("Try " + c + ")"); + } + c++; + + transformer.transform(reader, currentWriter, options); + + if (tempFile != null) + { + writer.putContent(tempFile); + } + + // No need to close input or output streams + // (according + // to comment in FailoverContentTransformer) + done = true; + return; + } + catch (Exception e) + { + if (exceptions == null) + { + exceptions = new ArrayList(); + } + if (!(e instanceof AlfrescoRuntimeException)) + { + e = new AlfrescoRuntimeException(e.getMessage(), e); + } + exceptions.add((AlfrescoRuntimeException)e); + + // Set a new reader to refresh the input stream. + reader = reader.getReader(); + } + } + // Throw the exception from the first transformer. The + // others are consumed. + if (exceptions != null) + { + throw exceptions.get(0); + } + } + finally + { + // Log exceptions that we have consumed. We may have thrown the first one if + // none of the transformers worked. + if (exceptions != null) + { + boolean first = true; + for (Exception e : exceptions) + { + Throwable rootCause = (e instanceof AlfrescoRuntimeException) ? ((AlfrescoRuntimeException)e).getRootCause() : null; + String message = (rootCause == null ? null : rootCause.getMessage()); + if (done) + { + message = "Transformer succeeded after previous transformer failed"+ (message == null ? "" : ": "+message); + if (rootCause instanceof UnsupportedTransformationException || + rootCause instanceof UnimportantTransformException) + { + logger.debug(message); + } + else + { + logger.warn(message, e); + } + } + else if (!first) // The first exception is logged later + { + message = "Transformer exception"+ (message == null ? "" : ": "+message); + if (rootCause instanceof UnsupportedTransformationException || + rootCause instanceof UnimportantTransformException) + { + logger.debug(message); + } + else + { + logger.error(message, e); + } + first = false; + } + } + } + } + } + + @Deprecated + public ContentTransformer getTransformer(String sourceUrl, String sourceMimetype, long sourceSize, String targetMimetype, TransformationOptions options) + { + List transformers = null; + try + { + transformerDebug.pushAvailable(sourceUrl, sourceMimetype, targetMimetype, options); + List activeTransformers = getActiveTransformers(sourceMimetype, sourceSize, targetMimetype, options); + transformerDebug.availableTransformers(activeTransformers, sourceSize, options, "ContentService.getTransformer(...)"); + transformers = activeTransformers.isEmpty() ? null : activeTransformers; + } + finally + { + transformerDebug.popAvailable(); + } + return (transformers == null) ? null : transformers.get(0); + } + + private void debugTransformations(String sourceMimetype, String targetMimetype, + long sourceSize, TransformationOptions transformOptions) + { + // check the file name + if (MimetypeMap.MIMETYPE_TEXT_PLAIN.equals(sourceMimetype) && + MimetypeMap.MIMETYPE_IMAGE_PNG.equals(targetMimetype)) + { + String fileName = transformerDebug.getFileName(transformOptions, true, 0); + if (fileName != null && fileName.contains("debugTransformers.txt")) + { + transformerDebug.transformationsByTransformer(null, false, false, null); + transformerDebug.transformationsByExtension(null, null, false, false, false, null); + } + } + } + + @Deprecated + public List getTransformers(String sourceUrl, String sourceMimetype, long sourceSize, String targetMimetype, TransformationOptions options) + { + try + { + // look for a transformer + transformerDebug.pushAvailable(sourceUrl, sourceMimetype, targetMimetype, options); + List transformers = getActiveTransformers(sourceMimetype, sourceSize, targetMimetype, options); + transformerDebug.availableTransformers(transformers, sourceSize, options, "ContentService.getTransformer(...)"); + return transformers.isEmpty() ? null : transformers; + } + finally + { + transformerDebug.popAvailable(); + } + } + + @Deprecated + public long getMaxSourceSizeBytes(String sourceMimetype, String targetMimetype, TransformationOptions options) + { + try + { + long maxSourceSize = 0; + transformerDebug.pushAvailable(null, sourceMimetype, targetMimetype, options); + List transformers = getActiveTransformers(sourceMimetype, -1, targetMimetype, options); + for (ContentTransformer transformer: transformers) + { + long maxSourceSizeKBytes = transformer.getMaxSourceSizeKBytes(sourceMimetype, targetMimetype, options); + if (maxSourceSize >= 0) + { + if (maxSourceSizeKBytes < 0) + { + maxSourceSize = -1; + } + else if (maxSourceSizeKBytes > 0 && maxSourceSize < maxSourceSizeKBytes) + { + maxSourceSize = maxSourceSizeKBytes; + } + } + // if maxSourceSizeKBytes == 0 this implies the transformation is disabled + } + if (transformerDebug.isEnabled()) + { + transformerDebug.availableTransformers(transformers, -1, options, + "ContentService.getMaxSourceSizeBytes() = "+transformerDebug.fileSize(maxSourceSize*1024)); + } + return (maxSourceSize > 0) ? maxSourceSize * 1024 : maxSourceSize; + } + finally + { + transformerDebug.popAvailable(); + } + } + + @Deprecated + public List getActiveTransformers(String sourceMimetype, long sourceSize, String targetMimetype, TransformationOptions options) + { + return transformerRegistry.getActiveTransformers(sourceMimetype, sourceSize, targetMimetype, options); + } +} diff --git a/src/main/java/org/alfresco/repo/rendition2/LegacySynchronousTransformClient.java b/src/main/java/org/alfresco/repo/rendition2/LegacySynchronousTransformClient.java index 6847a9deb2..c8a166dfab 100644 --- a/src/main/java/org/alfresco/repo/rendition2/LegacySynchronousTransformClient.java +++ b/src/main/java/org/alfresco/repo/rendition2/LegacySynchronousTransformClient.java @@ -27,14 +27,9 @@ package org.alfresco.repo.rendition2; import org.alfresco.repo.content.transform.ContentTransformer; 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.NodeRef; import org.alfresco.service.cmr.repository.TransformationOptions; -import org.alfresco.util.PropertyCheck; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.beans.factory.InitializingBean; import java.util.Map; @@ -46,19 +41,11 @@ import java.util.Map; * @author adavis */ @Deprecated -public class LegacySynchronousTransformClient implements SynchronousTransformClient, InitializingBean +public class LegacySynchronousTransformClient extends ContentTransformServiceImpl implements SynchronousTransformClient { private static final String TRANSFORM = "Legacy synchronous transform "; - private static Log logger = LogFactory.getLog(LegacyTransformClient.class); - private ContentService contentService; private TransformationOptionsConverter converter; - private ThreadLocal transform = new ThreadLocal<>(); - - public void setContentService(ContentService contentService) - { - this.contentService = contentService; - } public void setConverter(TransformationOptionsConverter converter) { @@ -66,22 +53,15 @@ public class LegacySynchronousTransformClient implements SynchronousTransformCli } @Override - public void afterPropertiesSet() throws Exception - { - PropertyCheck.mandatory(this, "contentService", contentService); - PropertyCheck.mandatory(this, "converter", converter); - } - - @Override - public boolean isSupported(NodeRef sourceNodeRef, String sourceMimetype, long sourceSizeInBytes, String contentUrl, - String targetMimetype, Map actualOptions, String transformName) + public boolean isSupported(String sourceMimetype, long sourceSizeInBytes, String contentUrl, String targetMimetype, + Map actualOptions, String transformName, NodeRef sourceNodeRef) { String renditionName = TransformDefinition.convertToRenditionName(transformName); TransformationOptions transformationOptions = converter.getTransformationOptions(renditionName, actualOptions); transformationOptions.setSourceNodeRef(sourceNodeRef); - ContentTransformer legacyTransform = contentService.getTransformer(contentUrl, sourceMimetype, sourceSizeInBytes, targetMimetype, transformationOptions); - transform.set(legacyTransform); + ContentTransformer legacyTransform = getTransformer(contentUrl, sourceMimetype, + sourceSizeInBytes, targetMimetype, transformationOptions); if (logger.isDebugEnabled()) { @@ -93,18 +73,13 @@ public class LegacySynchronousTransformClient implements SynchronousTransformCli @Override public void transform(ContentReader reader, ContentWriter writer, Map actualOptions, - String transformName, NodeRef sourceNodeRef) throws Exception + String transformName, NodeRef sourceNodeRef) { String renditionName = TransformDefinition.convertToRenditionName(transformName); - TransformationOptions options = converter.getTransformationOptions(renditionName, actualOptions); - options.setSourceNodeRef(sourceNodeRef); - ContentTransformer legacyTransform = transform.get(); try { - if (legacyTransform == null) - { - throw new IllegalStateException("isSupported was not called prior to transform."); - } + TransformationOptions options = converter.getTransformationOptions(renditionName, actualOptions); + options.setSourceNodeRef(sourceNodeRef); if (null == reader || !reader.exists()) { @@ -116,11 +91,11 @@ public class LegacySynchronousTransformClient implements SynchronousTransformCli logger.debug(TRANSFORM + "requested " + renditionName); } - // Note: we don't call legacyTransform.transform(reader, writer, options) as the Legacy - // transforms (unlike Local and Transform Service) automatically fail over to the next - // highest priority. This was not done for the newer transforms, as a fail over can always be - // defined and that makes it simpler to understand what is going on. - contentService.transform(reader, writer, options); + // We don't obtain the Legacy transformer and then call its transform method as they unlike Local and + // Transform Service transforms automatically fail over to the next highest priority. This was not done + // for the newer transforms, as a fail over can always be defined and that makes it simpler to understand + // what is going on. + transform(reader, writer, options); if (logger.isDebugEnabled()) { diff --git a/src/main/java/org/alfresco/repo/rendition2/LegacyTransformClient.java b/src/main/java/org/alfresco/repo/rendition2/LegacyTransformClient.java index 28c9290423..541819403f 100644 --- a/src/main/java/org/alfresco/repo/rendition2/LegacyTransformClient.java +++ b/src/main/java/org/alfresco/repo/rendition2/LegacyTransformClient.java @@ -62,6 +62,7 @@ public class LegacyTransformClient implements TransformClient, InitializingBean private ContentService contentService; private RenditionService2Impl renditionService2; private TransformationOptionsConverter converter; + private LegacySynchronousTransformClient legacySynchronousTransformClient; private ExecutorService executorService; private ThreadLocal transform = new ThreadLocal<>(); @@ -86,6 +87,11 @@ public class LegacyTransformClient implements TransformClient, InitializingBean this.converter = converter; } + public void setLegacySynchronousTransformClient(LegacySynchronousTransformClient legacySynchronousTransformClient) + { + this.legacySynchronousTransformClient = legacySynchronousTransformClient; + } + public void setExecutorService(ExecutorService executorService) { this.executorService = executorService; @@ -98,6 +104,7 @@ public class LegacyTransformClient implements TransformClient, InitializingBean PropertyCheck.mandatory(this, "contentService", contentService); PropertyCheck.mandatory(this, "renditionService2", renditionService2); PropertyCheck.mandatory(this, "converter", converter); + PropertyCheck.mandatory(this, "legacySynchronousTransformClient", legacySynchronousTransformClient); if (executorService == null) { executorService = Executors.newCachedThreadPool(); @@ -114,7 +121,7 @@ public class LegacyTransformClient implements TransformClient, InitializingBean TransformationOptions transformationOptions = converter.getTransformationOptions(renditionName, options); transformationOptions.setSourceNodeRef(sourceNodeRef); - ContentTransformer legacyTransform = contentService.getTransformer(contentUrl, sourceMimetype, sourceSizeInBytes, targetMimetype, transformationOptions); + ContentTransformer legacyTransform = legacySynchronousTransformClient.getTransformer(contentUrl, sourceMimetype, sourceSizeInBytes, targetMimetype, transformationOptions); transform.set(legacyTransform); String message = TRANSFORM + renditionName + " from " + sourceMimetype + @@ -145,7 +152,7 @@ public class LegacyTransformClient implements TransformClient, InitializingBean { if (legacyTransform == null) { - throw new IllegalStateException("isSupported was not called prior to transform."); + throw new IllegalStateException("isSupported was not called prior to an asynchronous transform."); } ContentReader reader = contentService.getReader(sourceNodeRef, ContentModel.PROP_CONTENT); @@ -165,7 +172,7 @@ public class LegacyTransformClient implements TransformClient, InitializingBean // defined and that makes it simpler to understand what is going on. ContentWriter writer = contentService.getTempWriter(); writer.setMimetype(targetMimetype); - contentService.transform(reader, writer, options); + legacySynchronousTransformClient.transform(reader, writer, options); InputStream inputStream = writer.getReader().getContentInputStream(); if (logger.isDebugEnabled()) diff --git a/src/main/java/org/alfresco/repo/rendition2/LegacyTransformServiceRegistry.java b/src/main/java/org/alfresco/repo/rendition2/LegacyTransformServiceRegistry.java index 77c1bd8e39..cdbd392f26 100644 --- a/src/main/java/org/alfresco/repo/rendition2/LegacyTransformServiceRegistry.java +++ b/src/main/java/org/alfresco/repo/rendition2/LegacyTransformServiceRegistry.java @@ -43,15 +43,15 @@ import java.util.Map; @Deprecated public class LegacyTransformServiceRegistry implements InitializingBean, TransformServiceRegistry { - private ContentService contentService; + private LegacySynchronousTransformClient legacySynchronousTransformClient; private TransformationOptionsConverter converter; private boolean enabled = true; private boolean firstTime = true; private TransformerDebug transformerDebug; - public void setContentService(ContentService contentService) + public void setLegacySynchronousTransformClient(LegacySynchronousTransformClient legacySynchronousTransformClient) { - this.contentService = contentService; + this.legacySynchronousTransformClient = legacySynchronousTransformClient; } public void setConverter(TransformationOptionsConverter converter) @@ -78,7 +78,7 @@ public class LegacyTransformServiceRegistry implements InitializingBean, Transfo @Override public void afterPropertiesSet() { - PropertyCheck.mandatory(this, "contentService", contentService); + PropertyCheck.mandatory(this, "legacySynchronousTransformClient", legacySynchronousTransformClient); PropertyCheck.mandatory(this, "converter", converter); PropertyCheck.mandatory(this, "transformerDebug", transformerDebug); } @@ -99,7 +99,7 @@ public class LegacyTransformServiceRegistry implements InitializingBean, Transfo try { TransformationOptions transformationOptions = converter.getTransformationOptions(renditionName, options); - maxSize = contentService.getMaxSourceSizeBytes(sourceMimetype, targetMimetype, transformationOptions); + maxSize = legacySynchronousTransformClient.getMaxSourceSizeBytes(sourceMimetype, targetMimetype, transformationOptions); } catch (IllegalArgumentException ignore) { diff --git a/src/main/java/org/alfresco/repo/rendition2/LocalSynchronousTransformClient.java b/src/main/java/org/alfresco/repo/rendition2/LocalSynchronousTransformClient.java index 542f3508fb..3d2fdc95a5 100644 --- a/src/main/java/org/alfresco/repo/rendition2/LocalSynchronousTransformClient.java +++ b/src/main/java/org/alfresco/repo/rendition2/LocalSynchronousTransformClient.java @@ -27,6 +27,8 @@ package org.alfresco.repo.rendition2; import org.alfresco.repo.content.transform.LocalTransform; import org.alfresco.repo.content.transform.LocalTransformServiceRegistry; +import org.alfresco.repo.content.transform.TransformerDebug; +import org.alfresco.repo.content.transform.UnsupportedTransformationException; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; @@ -38,20 +40,18 @@ import org.springframework.beans.factory.InitializingBean; import java.util.Map; /** - * Request synchronous transforms. Used in refactoring deprecated code, which called Legacy transforms, so that it will - * first try a Local transform, falling back to Legacy if not available. Transforms take place using transforms - * available on the local machine (based on {@link LocalTransform}). + * Request synchronous transforms. + * + * Transforms take place using transforms available on the local machine (based on {@link LocalTransform}). * * @author adavis */ -@Deprecated public class LocalSynchronousTransformClient implements SynchronousTransformClient, InitializingBean { private static final String TRANSFORM = "Local synchronous transform "; private static Log logger = LogFactory.getLog(LocalTransformClient.class); private LocalTransformServiceRegistry localTransformServiceRegistry; - private ThreadLocal transform = new ThreadLocal<>(); public void setLocalTransformServiceRegistry(LocalTransformServiceRegistry localTransformServiceRegistry) { @@ -65,46 +65,66 @@ public class LocalSynchronousTransformClient implements SynchronousTransformClie } @Override - public boolean isSupported(NodeRef sourceNodeRef, String sourceMimetype, long sourceSizeInBytes, String contentUrl, - String targetMimetype, Map actualOptions, String transformName) + public boolean isSupported(String sourceMimetype, long sourceSizeInBytes, String contentUrl, String targetMimetype, + Map actualOptions, String transformName, NodeRef sourceNodeRef) { String renditionName = TransformDefinition.convertToRenditionName(transformName); - LocalTransform localTransform = localTransformServiceRegistry.getLocalTransform(sourceMimetype, + LocalTransform transform = localTransformServiceRegistry.getLocalTransform(sourceMimetype, sourceSizeInBytes, targetMimetype, actualOptions, renditionName); - transform.set(localTransform); if (logger.isDebugEnabled()) { logger.debug(TRANSFORM + renditionName + " from " + sourceMimetype + - (localTransform == null ? " is unsupported" : " is supported")); + (transform == null ? " is unsupported" : " is supported")); } - return localTransform != null; + return transform != null; } @Override public void transform(ContentReader reader, ContentWriter writer, Map actualOptions, - String transformName, NodeRef sourceNodeRef) throws Exception + String transformName, NodeRef sourceNodeRef) { String renditionName = TransformDefinition.convertToRenditionName(transformName); - LocalTransform localTransform = transform.get(); try { - if (localTransform == null) + if (reader == null) { - throw new IllegalStateException("isSupported was not called prior to transform."); + throw new IllegalArgumentException("The content reader must be set"); } - - if (null == reader || !reader.exists()) + if (!reader.exists()) { throw new IllegalArgumentException("sourceNodeRef "+sourceNodeRef+" has no content."); } + String sourceMimetype = reader.getMimetype(); + long sourceSizeInBytes = reader.getSize(); + if (sourceMimetype == null) + { + throw new IllegalArgumentException("The content reader mimetype must be set"); + } + + String targetMimetype = writer.getMimetype(); + if (targetMimetype == null) + { + throw new IllegalArgumentException("The content writer mimetype must be set"); + } + + LocalTransform transform = localTransformServiceRegistry.getLocalTransform(sourceMimetype, + sourceSizeInBytes, targetMimetype, actualOptions, renditionName); + + if (transform == null) + { + throw new UnsupportedTransformationException("Transformation of " + sourceMimetype + + (sourceSizeInBytes > 0 ? " size "+sourceSizeInBytes : "")+ " to " + targetMimetype + + " unsupported"); + } + if (logger.isDebugEnabled()) { logger.debug(TRANSFORM + " requested " + renditionName); } - localTransform.transform(reader, writer, actualOptions, renditionName, sourceNodeRef); + transform.transform(reader, writer, actualOptions, renditionName, sourceNodeRef); if (logger.isDebugEnabled()) { diff --git a/src/main/java/org/alfresco/repo/rendition2/LocalTransformClient.java b/src/main/java/org/alfresco/repo/rendition2/LocalTransformClient.java index 4964a7035e..fc7931d06d 100644 --- a/src/main/java/org/alfresco/repo/rendition2/LocalTransformClient.java +++ b/src/main/java/org/alfresco/repo/rendition2/LocalTransformClient.java @@ -142,7 +142,7 @@ public class LocalTransformClient implements TransformClient, InitializingBean { if (localTransform == null) { - throw new IllegalStateException("isSupported was not called prior to transform."); + throw new IllegalStateException("isSupported was not called prior to an asynchronous transform."); } ContentReader reader = contentService.getReader(sourceNodeRef, ContentModel.PROP_CONTENT); diff --git a/src/main/java/org/alfresco/repo/rendition2/RenditionService2Impl.java b/src/main/java/org/alfresco/repo/rendition2/RenditionService2Impl.java index 75f122882b..57d35f9daa 100644 --- a/src/main/java/org/alfresco/repo/rendition2/RenditionService2Impl.java +++ b/src/main/java/org/alfresco/repo/rendition2/RenditionService2Impl.java @@ -86,7 +86,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea private static Log logger = LogFactory.getLog(RenditionService2Impl.class); // As Async transforms and renditions are so similar, this class provides a way to provide the code that is different. - private abstract static class renderOrTransformCallBack + private abstract static class RenderOrTransformCallBack { abstract String getName(); @@ -208,7 +208,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() @@ -228,7 +228,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() @@ -278,7 +278,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea }); } - private void requestAsyncTransformOrRendition(NodeRef sourceNodeRef, renderOrTransformCallBack renderOrTransform) + private void requestAsyncTransformOrRendition(NodeRef sourceNodeRef, RenderOrTransformCallBack renderOrTransform) { try { diff --git a/src/main/java/org/alfresco/repo/rendition2/SwitchingSynchronousTransformClient.java b/src/main/java/org/alfresco/repo/rendition2/SwitchingSynchronousTransformClient.java index 299e1911dd..e15ba0c33b 100644 --- a/src/main/java/org/alfresco/repo/rendition2/SwitchingSynchronousTransformClient.java +++ b/src/main/java/org/alfresco/repo/rendition2/SwitchingSynchronousTransformClient.java @@ -25,9 +25,12 @@ */ package org.alfresco.repo.rendition2; +import org.alfresco.repo.content.transform.TransformerDebug; +import org.alfresco.repo.content.transform.UnsupportedTransformationException; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.util.Pair; import java.util.Map; @@ -43,7 +46,8 @@ public class SwitchingSynchronousTransformClient implements SynchronousTransform { private final SynchronousTransformClient primary; private final SynchronousTransformClient secondary; - private ThreadLocal synchronousTransformClient = new ThreadLocal<>(); + + private TransformerDebug transformerDebug; public SwitchingSynchronousTransformClient(SynchronousTransformClient primary, SynchronousTransformClient secondary) { @@ -51,31 +55,47 @@ public class SwitchingSynchronousTransformClient implements SynchronousTransform this.secondary = secondary; } - @Override - public boolean isSupported(NodeRef sourceNodeRef, String sourceMimetype, long sourceSizeInBytes, String contentUrl, - String targetMimetype, Map actualOptions, String transformName) + public void setTransformerDebug(TransformerDebug transformerDebug) { - boolean supported = true; - if (primary.isSupported(sourceNodeRef, sourceMimetype, sourceSizeInBytes, contentUrl, - targetMimetype, actualOptions, transformName)) + this.transformerDebug = transformerDebug; + } + + @Override + public boolean isSupported(String sourceMimetype, long sourceSizeInBytes, String contentUrl, String targetMimetype, + Map actualOptions, String transformName, NodeRef sourceNodeRef) + { + boolean supported = + primary.isSupported(sourceMimetype, sourceSizeInBytes, contentUrl, targetMimetype, actualOptions, + transformName, sourceNodeRef) || + secondary.isSupported(sourceMimetype, sourceSizeInBytes, contentUrl, targetMimetype, actualOptions, + transformName, sourceNodeRef); + if (transformerDebug.isEnabled()) { - synchronousTransformClient.set(primary); - } - else if (secondary.isSupported(sourceNodeRef, sourceMimetype, sourceSizeInBytes, contentUrl, - targetMimetype, actualOptions, transformName)) - { - synchronousTransformClient.set(secondary); - } - else - { - supported = false; + String renditionName = TransformDefinition.convertToRenditionName(transformName); + transformerDebug.debug(sourceMimetype, targetMimetype, sourceNodeRef, sourceSizeInBytes, renditionName, + " is "+(supported ? "" : "not ")+"supported"); } return supported; } @Override - public void transform(ContentReader reader, ContentWriter writer, Map actualOptions, String transformName, NodeRef sourceNodeRef) throws Exception + public void transform(ContentReader reader, ContentWriter writer, Map actualOptions, + String transformName, NodeRef sourceNodeRef) { - synchronousTransformClient.get().transform(reader, writer, actualOptions, transformName, sourceNodeRef); + try + { + primary.transform(reader, writer, actualOptions, transformName, sourceNodeRef); + } + catch (UnsupportedTransformationException primaryException) + { + try + { + secondary.transform(reader, writer, actualOptions, transformName, sourceNodeRef); + } + catch (UnsupportedTransformationException secondaryException) + { + throw primaryException; + } + } } } diff --git a/src/main/java/org/alfresco/repo/rendition2/SwitchingTransformClient.java b/src/main/java/org/alfresco/repo/rendition2/SwitchingTransformClient.java index e0144acec0..73348c8752 100644 --- a/src/main/java/org/alfresco/repo/rendition2/SwitchingTransformClient.java +++ b/src/main/java/org/alfresco/repo/rendition2/SwitchingTransformClient.java @@ -49,13 +49,21 @@ public class SwitchingTransformClient implements TransformClient { try { - transformClient.set(primary); primary.checkSupported(sourceNodeRef, renditionDefinition, sourceMimetype, sourceSizeInBytes, contentUrl); + transformClient.set(primary); } catch (UnsupportedOperationException e) { - transformClient.set(secondary); - secondary.checkSupported(sourceNodeRef, renditionDefinition, sourceMimetype, sourceSizeInBytes, contentUrl); + try + { + secondary.checkSupported(sourceNodeRef, renditionDefinition, sourceMimetype, sourceSizeInBytes, contentUrl); + transformClient.set(secondary); + } + catch (UnsupportedOperationException e2) + { + transformClient.set(null); + throw e2; + } } } diff --git a/src/main/java/org/alfresco/repo/rendition2/SynchronousTransformClient.java b/src/main/java/org/alfresco/repo/rendition2/SynchronousTransformClient.java index 06113de327..c0952af2fc 100644 --- a/src/main/java/org/alfresco/repo/rendition2/SynchronousTransformClient.java +++ b/src/main/java/org/alfresco/repo/rendition2/SynchronousTransformClient.java @@ -26,29 +26,29 @@ package org.alfresco.repo.rendition2; import org.alfresco.model.ContentModel; +import org.alfresco.repo.content.transform.UnsupportedTransformationException; import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.ContentIOException; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.TransformationOptions; import java.util.Map; /** - * Request synchronous transforms. Used in refactoring deprecated code, which called Legacy transforms, so that it will - * first try a Local transform, falling back to Legacy if not available. + * Request synchronous transforms. * * @author adavis */ -@Deprecated public interface SynchronousTransformClient { /** - * Works out if it is possible to transform content of a given source mimetype and size into a - * target mimetype given a list of actual transform option names and values (Strings) plus the data contained in the - * Transformer objects registered with this class. + * Works out if it is possible to transform content of a given source mimetype and size into a target mimetype + * given a list of actual transform option names and values. Ideally, just call the transform method and catch the + * UnsupportedTransformationException to avoid extra work. * - * @param sourceNodeRef the NodeRef of the source content. Optional as it is only used in debug messages. * @param sourceMimetype the mimetype of the source content * @param sourceSizeInBytes the size in bytes of the source content. Ignored if negative. * @param contentUrl The url of the content. Optional as it is only used in debug messages. @@ -57,46 +57,14 @@ public interface SynchronousTransformClient * @param transformName (optional) name for the set of options and target mimetype. If supplied is used to cache * results to avoid having to work out if a given transformation is supported a second time. The sourceMimetype * and sourceSizeInBytes may still change. In the case of ACS this is the rendition name. + * @param sourceNodeRef (optional) NodeRef of the source content. Only used in debug messages. * @return {@code}true{@code} if it is supported. */ - @Deprecated - boolean isSupported(NodeRef sourceNodeRef, String sourceMimetype, long sourceSizeInBytes, String contentUrl, - String targetMimetype, Map actualOptions, String transformName); + boolean isSupported(String sourceMimetype, long sourceSizeInBytes, String contentUrl, String targetMimetype, + Map actualOptions, String transformName, NodeRef sourceNodeRef); /** - * Helper method to call {@link #isSupported(NodeRef, String, long, String, String, Map, String)}. Uses the - * {@code nodeService} and {@code sourceNodeRef} to work out the {@code sourceMimetype}, {@code sourceSizeInBytes} - * and {@code contentUrl}. - * @param sourceNodeRef the NodeRef of the source content. - * @param targetMimetype the mimetype of the target - * @param actualOptions the actual name value pairs available that could be passed to the Transform Service. - * @param transformName (optional) name for the set of options and target mimetype. If supplied is used to cache - * results to avoid having to work out if a given transformation is supported a second time. The sourceMimetype - * and sourceSizeInBytes may still change. In the case of ACS this is the rendition name. - * @param nodeService to access the sourceNodeRef content property. - * @return {@code}true{@code} if it is supported. - */ - @Deprecated - default boolean isSupported(NodeRef sourceNodeRef, String targetMimetype, - Map actualOptions, String transformName, NodeService nodeService) - { - boolean supported = false; - ContentData contentData = (ContentData) nodeService.getProperty(sourceNodeRef, ContentModel.PROP_CONTENT); - if (contentData != null && contentData.getContentUrl() != null) - { - String sourceMimetype = contentData.getMimetype(); - long sourceSizeInBytes = contentData.getSize(); - String contentUrl = contentData.getContentUrl(); - supported = isSupported(sourceNodeRef, sourceMimetype, sourceSizeInBytes, contentUrl, - targetMimetype, actualOptions, transformName); - } - return supported; - } - - /** - * Requests a synchronous transform. Not used for renditions. - * The call to this method MUST be proceeded by a successful call to - * {@link #isSupported(NodeRef, String, long, String, String, Map, String)} in the SAME Thread. + * Requests a synchronous transform. * @param reader of the source content * @param writer to the target node's content * @param actualOptions the actual name value pairs available that could be passed to the Transform Service. @@ -104,8 +72,13 @@ public interface SynchronousTransformClient * results to avoid having to work out if a given transformation is supported a second time. The sourceMimetype * and sourceSizeInBytes may still change. In the case of ACS this is the rendition name. * @param sourceNodeRef the source node + * @throws UnsupportedTransformationException if there is an unexpected failure to transform, normally in a + * pipeline, where an intermediate transform may not be performed after all because an intermediate + * converion is too big. + * @throws ContentIOException there is an unexpected communication or transformation failure. + * @throws UnsupportedTransformationException if isSupported has not been called and */ - @Deprecated void transform(ContentReader reader, ContentWriter writer, Map actualOptions, - String transformName, NodeRef sourceNodeRef) throws Exception; + String transformName, NodeRef sourceNodeRef) + throws UnsupportedTransformationException, ContentIOException; } diff --git a/src/main/java/org/alfresco/repo/rendition2/TransformClient.java b/src/main/java/org/alfresco/repo/rendition2/TransformClient.java index 2867abcbaa..8555a3372e 100644 --- a/src/main/java/org/alfresco/repo/rendition2/TransformClient.java +++ b/src/main/java/org/alfresco/repo/rendition2/TransformClient.java @@ -25,6 +25,8 @@ */ package org.alfresco.repo.rendition2; +import org.alfresco.repo.content.transform.UnsupportedTransformationException; +import org.alfresco.service.cmr.repository.ContentIOException; import org.alfresco.service.cmr.repository.NodeRef; /** @@ -57,6 +59,12 @@ public interface TransformClient * @param renditionDefinition which rendition to perform * @param user that requested the transform. * @param sourceContentHashCode the hash code of the source node's content URL. Used to check the transform result + * @throws UnsupportedTransformationException if there is an unexpected failure to transform, normally in a + * pipeline, where an intermediate transform may not be performed after all because an intermediate + * conversion is too big. + * @throws ContentIOException there is an unexpected communication or transformation failure. */ - void transform(NodeRef sourceNodeRef, RenditionDefinition2 renditionDefinition, String user, int sourceContentHashCode); + void transform(NodeRef sourceNodeRef, RenditionDefinition2 renditionDefinition, String user, + int sourceContentHashCode) + throws UnsupportedTransformationException, ContentIOException; } diff --git a/src/main/java/org/alfresco/repo/rendition2/TransformationOptionsConverter.java b/src/main/java/org/alfresco/repo/rendition2/TransformationOptionsConverter.java index e6f33462ed..83c39865e4 100644 --- a/src/main/java/org/alfresco/repo/rendition2/TransformationOptionsConverter.java +++ b/src/main/java/org/alfresco/repo/rendition2/TransformationOptionsConverter.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2018 Alfresco Software Limited + * Copyright (C) 2005 - 2019 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -25,6 +25,7 @@ */ package org.alfresco.repo.rendition2; +import org.alfresco.repo.content.transform.RuntimeExecutableContentTransformerOptions; import org.alfresco.repo.content.transform.magick.ImageResizeOptions; import org.alfresco.repo.content.transform.magick.ImageTransformationOptions; import org.alfresco.repo.content.transform.swf.SWFTransformationOptions; @@ -35,10 +36,14 @@ import org.alfresco.service.cmr.repository.TransformationOptionLimits; import org.alfresco.service.cmr.repository.TransformationOptions; import org.alfresco.service.cmr.repository.TransformationSourceOptions; import org.alfresco.util.PropertyCheck; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.InitializingBean; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -74,7 +79,7 @@ import static org.alfresco.repo.rendition2.RenditionDefinition2.WIDTH; import static org.springframework.util.CollectionUtils.containsAny; /** - * @deprecated converts the new flat name value pair transformer options to the depreacted TransformationOptions. + * @deprecated converts the new flat name value pair transformer options to the deprecated TransformationOptions. * * @author adavis */ @@ -111,6 +116,7 @@ public class TransformationOptionsConverter implements InitializingBean IMAGE_OPTIONS.addAll(CROP_OPTIONS); IMAGE_OPTIONS.addAll(TEMPORAL_OPTIONS); IMAGE_OPTIONS.addAll(RESIZE_OPTIONS); + IMAGE_OPTIONS.add(AUTO_ORIENT); } private static Set PDF_OPTIONS = new HashSet<>(Arrays.asList(new String[] @@ -133,6 +139,8 @@ public class TransformationOptionsConverter implements InitializingBean void set(String s); } + private static Log logger = LogFactory.getLog(TransformationOptionsConverter.class); + // The default valued in the old TransformationOptionsLimits private long maxSourceSizeKBytes; private long readLimitTimeMs; @@ -233,16 +241,17 @@ public class TransformationOptionsConverter implements InitializingBean if (containsPaged || containsCrop || containsTemporal) { List sourceOptionsList = new ArrayList<>(); - opts.setSourceOptionsList(sourceOptionsList); if (containsPaged) { + // The legacy transformer options start at page 1, where as image magick and the local + // transforms start at 0; PagedSourceOptions pagedSourceOptions = new PagedSourceOptions(); sourceOptionsList.add(pagedSourceOptions); - ifSet(options, START_PAGE, (v) -> pagedSourceOptions.setStartPageNumber(Integer.parseInt(v))); - ifSet(options, END_PAGE, (v) -> pagedSourceOptions.setEndPageNumber(Integer.parseInt(v))); + ifSet(options, START_PAGE, (v) -> pagedSourceOptions.setStartPageNumber(Integer.parseInt(v) + 1)); + ifSet(options, END_PAGE, (v) -> pagedSourceOptions.setEndPageNumber(Integer.parseInt(v) + 1)); ifSet(options, PAGE, (v) -> { - int i = Integer.parseInt(v); + int i = Integer.parseInt(v) + 1; pagedSourceOptions.setStartPageNumber(i); pagedSourceOptions.setEndPageNumber(i); }); @@ -267,6 +276,7 @@ public class TransformationOptionsConverter implements InitializingBean ifSet(options, DURATION, (v) -> temporalSourceOptions.setDuration(v)); ifSet(options, OFFSET, (v) -> temporalSourceOptions.setOffset(v)); } + opts.setSourceOptionsList(sourceOptionsList); } } } @@ -321,4 +331,122 @@ public class TransformationOptionsConverter implements InitializingBean setter.set(value); } } + + public Map getOptions(TransformationOptions options) + { + Map map = new HashMap<>(); + map.put(TIMEOUT, "-1"); + if (options != null) + { + if (options instanceof ImageTransformationOptions) + { + ImageTransformationOptions opts = (ImageTransformationOptions) options; + + // TODO We don't support this any more for security reasons, however it might be possible to + // extract some of the well know values and add them to the newer ImageMagick transform options. + String commandOptions = opts.getCommandOptions(); + if (commandOptions != null && !commandOptions.isBlank()) + { + logger.error("ImageMagick commandOptions are no longer supported for security reasons: " + commandOptions); + } + + ImageResizeOptions imageResizeOptions = opts.getResizeOptions(); + if (imageResizeOptions != null) + { + int width = imageResizeOptions.getWidth(); + int height = imageResizeOptions.getHeight(); + ifSet(width != -1, map, RESIZE_WIDTH, width); + ifSet(height != -1, map, RESIZE_HEIGHT, height); + ifSet(imageResizeOptions.isResizeToThumbnail(), map, THUMBNAIL, true); + ifSet(imageResizeOptions.isPercentResize(), map, RESIZE_PERCENTAGE, true); + ifSet(!imageResizeOptions.getAllowEnlargement(), map, ALLOW_ENLARGEMENT, false); + ifSet(imageResizeOptions.isMaintainAspectRatio(), map, MAINTAIN_ASPECT_RATIO, true); + } + + ifSet(!opts.isAutoOrient(), map, AUTO_ORIENT, false); + + Collection sourceOptionsList = opts.getSourceOptionsList(); + if (sourceOptionsList != null) + { + for (TransformationSourceOptions transformationSourceOptions : sourceOptionsList) + { + if (transformationSourceOptions instanceof PagedSourceOptions) + { + PagedSourceOptions pagedSourceOptions = (PagedSourceOptions) transformationSourceOptions; + + // The legacy transformer options start at page 1, where as image magick and the local + // transforms start at 0; + Integer startPageNumber = pagedSourceOptions.getStartPageNumber() - 1; + Integer endPageNumber = pagedSourceOptions.getEndPageNumber() - 1; + if (startPageNumber == endPageNumber) + { + map.put(PAGE, Integer.toString(startPageNumber)); + } + else + { + map.put(START_PAGE, Integer.toString(startPageNumber)); + map.put(END_PAGE, Integer.toString(endPageNumber)); + } + } + else if (transformationSourceOptions instanceof CropSourceOptions) + { + CropSourceOptions cropSourceOptions = (CropSourceOptions) transformationSourceOptions; + String gravity = cropSourceOptions.getGravity(); + boolean percentageCrop = cropSourceOptions.isPercentageCrop(); + int height = cropSourceOptions.getHeight(); + int width = cropSourceOptions.getWidth(); + int xOffset = cropSourceOptions.getXOffset(); + int yOffset = cropSourceOptions.getYOffset(); + ifSet(gravity != null, map, CROP_GRAVITY, gravity); + ifSet(percentageCrop, map, CROP_PERCENTAGE, percentageCrop); + ifSet(width != -1, map, CROP_WIDTH, width); + ifSet(height != -1, map, CROP_HEIGHT, height); + ifSet(xOffset != 0, map, CROP_X_OFFSET, xOffset); + ifSet(yOffset != 0, map, CROP_Y_OFFSET, yOffset); + } + else if (transformationSourceOptions instanceof TemporalSourceOptions) + { + TemporalSourceOptions temporalSourceOptions = (TemporalSourceOptions) transformationSourceOptions; + String duration = temporalSourceOptions.getDuration(); + String offset = temporalSourceOptions.getOffset(); + ifSet(duration != null, map, DURATION, duration); + ifSet(offset != null, map, OFFSET, offset); + } + else + { + logger.error("TransformationOption sourceOptionsList contained a " + + transformationSourceOptions.getClass().getName() + + ". It is not know how to convert this into newer transform options."); + } + } + } + } + else if (options instanceof SWFTransformationOptions) + { + SWFTransformationOptions opts = (SWFTransformationOptions) options; + map.put(FLASH_VERSION, opts.getFlashVersion()); + } + else if (options instanceof RuntimeExecutableContentTransformerOptions) + { + RuntimeExecutableContentTransformerOptions opts = (RuntimeExecutableContentTransformerOptions) options; + map.putAll(opts.getPropertyValues()); + } + else if (!options.getClass().equals(TransformationOptions.class)) + { + throw new IllegalArgumentException("Unable to convert " + + options.getClass().getSimpleName() + " to new transform options held in a Map.\n" + + "The TransformOptionConverter may need to be sub classed to support this conversion."); + } + } + + return map; + } + + protected void ifSet(boolean condition, Map options, String key, Object value) + { + if (condition) + { + options.put(key, value.toString()); + } + } } diff --git a/src/main/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java b/src/main/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java index 38a5fa18a2..5215cc38e6 100644 --- a/src/main/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java +++ b/src/main/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2019 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -37,6 +37,7 @@ import org.alfresco.repo.lock.JobLockService; import org.alfresco.repo.nodelocator.NodeLocatorService; import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.rendition2.RenditionService2; +import org.alfresco.repo.rendition2.SynchronousTransformClient; import org.alfresco.repo.search.impl.solr.facet.SolrFacetHelper; import org.alfresco.repo.search.impl.solr.facet.handler.FacetLabelDisplayHandlerRegistry; import org.alfresco.repo.transaction.RetryingTransactionHelper; @@ -161,6 +162,11 @@ public class ServiceDescriptorRegistry return (ContentService)getService(CONTENT_SERVICE); } + public SynchronousTransformClient getSynchronousTransformClient() + { + return (SynchronousTransformClient)getService(SYNCHRONOUS_TRANSFORM_CLIENT); + } + @Override public MimetypeService getMimetypeService() { diff --git a/src/main/java/org/alfresco/repo/template/BaseContentNode.java b/src/main/java/org/alfresco/repo/template/BaseContentNode.java index b7b5772b88..99745d676b 100644 --- a/src/main/java/org/alfresco/repo/template/BaseContentNode.java +++ b/src/main/java/org/alfresco/repo/template/BaseContentNode.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2019 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -28,6 +28,7 @@ package org.alfresco.repo.template; import java.io.Serializable; import java.text.MessageFormat; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Set; @@ -35,10 +36,12 @@ import org.alfresco.model.ApplicationModel; import org.alfresco.model.ContentModel; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.transform.UnsupportedTransformationException; +import org.alfresco.repo.rendition2.SynchronousTransformClient; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.ContentIOException; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentWriter; @@ -601,6 +604,7 @@ public abstract class BaseContentNode implements TemplateContent { // get the content reader ContentService contentService = services.getContentService(); + SynchronousTransformClient synchronousTransformClient = services.getSynchronousTransformClient(); NodeRef nodeRef = getNodeRef(); ContentReader reader = contentService.getReader(nodeRef, property); if (reader == null) @@ -619,22 +623,28 @@ public abstract class BaseContentNode implements TemplateContent // try and transform the content try { - contentService.transform(reader, writer, options); - - ContentReader resultReader = writer.getReader(); - if (resultReader != null && reader.exists()) + try + { + synchronousTransformClient.transform(reader, writer, Collections.emptyMap(), null, nodeRef); + + ContentReader resultReader = writer.getReader(); + if (resultReader != null && reader.exists()) + { + if (length != -1) + { + result = resultReader.getContentString(length); + } + else + { + result = resultReader.getContentString(); + } + } + } + catch (UnsupportedTransformationException ignore) { - if (length != -1) - { - result = resultReader.getContentString(length); - } - else - { - result = resultReader.getContentString(); - } } } - catch (NoTransformerException|UnsupportedTransformationException e) + catch (NoTransformerException|UnsupportedTransformationException| ContentIOException e) { // ignore } diff --git a/src/main/java/org/alfresco/repo/thumbnail/ThumbnailRegistry.java b/src/main/java/org/alfresco/repo/thumbnail/ThumbnailRegistry.java index c21a65bb02..56dafcacc3 100644 --- a/src/main/java/org/alfresco/repo/thumbnail/ThumbnailRegistry.java +++ b/src/main/java/org/alfresco/repo/thumbnail/ThumbnailRegistry.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2019 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -31,6 +31,7 @@ import org.alfresco.repo.lock.JobLockService; import org.alfresco.repo.lock.LockAcquisitionException; import org.alfresco.repo.rendition2.RenditionDefinition2; import org.alfresco.repo.rendition2.RenditionDefinitionRegistry2; +import org.alfresco.repo.rendition2.TransformationOptionsConverter; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.tenant.Tenant; @@ -38,7 +39,6 @@ import org.alfresco.repo.tenant.TenantAdminService; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.service.cmr.rendition.RenditionDefinition; import org.alfresco.service.cmr.rendition.RenditionService; -import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.TransformationOptions; import org.alfresco.service.cmr.thumbnail.ThumbnailException; @@ -78,9 +78,6 @@ public class ThumbnailRegistry implements ApplicationContextAware, ApplicationLi /** Logger */ private static Log logger = LogFactory.getLog(ThumbnailRegistry.class); - /** Content service */ - private ContentService contentService; - /** Transaction service */ protected TransactionService transactionService; @@ -93,6 +90,10 @@ public class ThumbnailRegistry implements ApplicationContextAware, ApplicationLi private TransformServiceRegistry transformServiceRegistry; + private TransformServiceRegistry localTransformServiceRegistry; + + private TransformationOptionsConverter converter; + private RenditionDefinitionRegistry2 renditionDefinitionRegistry2; private boolean redeployStaticDefsOnStartup; @@ -118,16 +119,6 @@ public class ThumbnailRegistry implements ApplicationContextAware, ApplicationLi return thumbnailRenditionConvertor; } - /** - * Content service - * - * @param contentService content service - */ - public void setContentService(ContentService contentService) - { - this.contentService = contentService; - } - /** * Transaction service * @@ -169,6 +160,16 @@ public class ThumbnailRegistry implements ApplicationContextAware, ApplicationLi mimetypeMap.clear(); } + public void setLocalTransformServiceRegistry(TransformServiceRegistry localTransformServiceRegistry) + { + this.localTransformServiceRegistry = localTransformServiceRegistry; + } + + public void setConverter(TransformationOptionsConverter converter) + { + this.converter = converter; + } + public void setRenditionDefinitionRegistry2(RenditionDefinitionRegistry2 renditionDefinitionRegistry2) { this.renditionDefinitionRegistry2 = renditionDefinitionRegistry2; @@ -319,58 +320,6 @@ public class ThumbnailRegistry implements ApplicationContextAware, ApplicationLi return result; } - public List getThumbnailDefinitions(String sourceUrl, String mimetype, long sourceSize) - { - List thumbnailDefinitionsLimitsForMimetype = this.mimetypeMap.get(mimetype); - - if (thumbnailDefinitionsLimitsForMimetype == null) - { - boolean foundAtLeastOneTransformer = false; - thumbnailDefinitionsLimitsForMimetype = new ArrayList(7); - - for (ThumbnailDefinition thumbnailDefinition : this.thumbnailDefinitions.values()) - { - if (isThumbnailDefinitionAvailable(sourceUrl, mimetype, sourceSize, thumbnailDefinition)) - { - long maxSourceSizeBytes = getMaxSourceSizeBytes(mimetype, thumbnailDefinition); - if (maxSourceSizeBytes != 0) - { - thumbnailDefinitionsLimitsForMimetype.add(new ThumbnailDefinitionLimits(thumbnailDefinition, maxSourceSizeBytes)); - foundAtLeastOneTransformer = true; - } - } - } - - // If we have found no transformers for the given MIME type then we do - // not cache the empty list. We prevent this because we want to allow for - // transformers only coming online *during* system operation - as opposed - // to coming online during startup. - // - // An example of such a transient transformer would be those that use OpenOffice.org. - // It is possible that the system might start without OOo-based transformers - // being available. Therefore we must not cache an empty list for the relevant - // MIME types - otherwise this class would hide the fact that OOo (soffice) has - // been launched and that new transformers are available. - if (foundAtLeastOneTransformer) - { - this.mimetypeMap.put(mimetype, thumbnailDefinitionsLimitsForMimetype); - } - } - - // Only return ThumbnailDefinition for this specific source - may be limited on size. - List result = new ArrayList(thumbnailDefinitionsLimitsForMimetype.size()); - for (ThumbnailDefinitionLimits thumbnailDefinitionLimits: thumbnailDefinitionsLimitsForMimetype) - { - long maxSourceSizeBytes = thumbnailDefinitionLimits.getMaxSourceSizeBytes(); - if (sourceSize <= 0 || maxSourceSizeBytes < 0 || maxSourceSizeBytes >= sourceSize) - { - result.add(thumbnailDefinitionLimits.getThumbnailDefinition()); - } - } - - return result; - } - /** * * @param mimetype String @@ -395,8 +344,8 @@ public class ThumbnailRegistry implements ApplicationContextAware, ApplicationLi */ public boolean isThumbnailDefinitionAvailable(String sourceUrl, String sourceMimetype, long sourceSize, NodeRef sourceNodeRef, ThumbnailDefinition thumbnailDefinition) { - // Use RenditionService2 if it knows about the definition, otherwise use contentService as before. Needed as - // disabling local transforms should not disable thumbnails if they can be done remotely. + // Use RenditionService2 if it knows about the definition, otherwise use localTransformServiceRegistry. + // Needed as disabling local transforms should not disable thumbnails if they can be done remotely. boolean supported = false; String targetMimetype = thumbnailDefinition.getMimetype(); RenditionDefinition2 renditionDefinition = getEquivalentRenditionDefinition2(thumbnailDefinition); @@ -404,18 +353,19 @@ public class ThumbnailRegistry implements ApplicationContextAware, ApplicationLi { Map options = renditionDefinition.getTransformOptions(); String renditionName = renditionDefinition.getRenditionName(); - supported = transformServiceRegistry.isSupported(sourceMimetype, sourceSize, targetMimetype, options, renditionName); + supported = transformServiceRegistry.isSupported(sourceMimetype, sourceSize, targetMimetype, + options, renditionName); } else { boolean orig = TransformerDebug.setDebugOutput(false); try { - // Copy the thumbnail's TransformationOptions and set the sourceNodeRef, for use by debug. - TransformationOptions options = thumbnailDefinition.getTransformationOptions().deepCopy(); - options.setSourceNodeRef(sourceNodeRef); - supported = contentService.getTransformer(sourceUrl, sourceMimetype, sourceSize, - targetMimetype, options) != null; + TransformationOptions transformationOptions = thumbnailDefinition.getTransformationOptions(); + String renditionName = thumbnailDefinition.getName(); + Map options = converter.getOptions(transformationOptions); + supported = localTransformServiceRegistry.isSupported(sourceMimetype, sourceSize, targetMimetype, + options, renditionName); } finally { @@ -448,8 +398,8 @@ public class ThumbnailRegistry implements ApplicationContextAware, ApplicationLi */ public long getMaxSourceSizeBytes(String sourceMimetype, ThumbnailDefinition thumbnailDefinition) { - // Use RenditionService2 if it knows about the definition, otherwise use contentService as before. Needed as - // disabling local transforms should not disable thumbnails if they can be done remotely. + // Use RenditionService2 if it knows about the definition, otherwise use localTransformServiceRegistry. + // Needed as disabling local transforms should not disable thumbnails if they can be done remotely. long maxSize = 0; String targetMimetype = thumbnailDefinition.getMimetype(); RenditionDefinition2 renditionDefinition = getEquivalentRenditionDefinition2(thumbnailDefinition); @@ -464,8 +414,10 @@ public class ThumbnailRegistry implements ApplicationContextAware, ApplicationLi boolean orig = TransformerDebug.setDebugOutput(false); try { - TransformationOptions options = thumbnailDefinition.getTransformationOptions(); - maxSize = contentService.getMaxSourceSizeBytes(sourceMimetype, targetMimetype, options); + TransformationOptions transformationOptions = thumbnailDefinition.getTransformationOptions(); + String renditionName = thumbnailDefinition.getName(); + Map options = converter.getOptions(transformationOptions); + maxSize = localTransformServiceRegistry.findMaxSize(sourceMimetype, targetMimetype, options, renditionName); } finally { diff --git a/src/main/java/org/alfresco/repo/thumbnail/conditions/NodeEligibleForRethumbnailingEvaluator.java b/src/main/java/org/alfresco/repo/thumbnail/conditions/NodeEligibleForRethumbnailingEvaluator.java index 58ed854a55..edf18492cd 100644 --- a/src/main/java/org/alfresco/repo/thumbnail/conditions/NodeEligibleForRethumbnailingEvaluator.java +++ b/src/main/java/org/alfresco/repo/thumbnail/conditions/NodeEligibleForRethumbnailingEvaluator.java @@ -63,7 +63,7 @@ import org.apache.commons.logging.LogFactory; * {@link FailureHandlingOptions#getRetryCount() system.thumbnail.retryCount} times. *

  • These initial retries to produce a thumbnail will occur not more often than every * {@link FailureHandlingOptions#getRetryPeriod() system.thumbnail.retryPeriod} seconds - * and will use which ever content transformers the {@link ContentService#getTransformer(String, String) content service} gives.
  • + * and will use which ever content transformers the ContentService#getTransformer(String, String content service gives. *
  • If a thumbnail is not successfully produced for a node after these attempts then it is considered to be * a 'difficult' piece of content with respect to thumbnailing and the assumption is that a thumbnail may * never be available for it. However, in order to allow for the possibility of software upgrades or similiar, which may diff --git a/src/main/java/org/alfresco/service/ServiceRegistry.java b/src/main/java/org/alfresco/service/ServiceRegistry.java index a91c1ae863..0797adb4b9 100644 --- a/src/main/java/org/alfresco/service/ServiceRegistry.java +++ b/src/main/java/org/alfresco/service/ServiceRegistry.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2019 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -38,6 +38,7 @@ import org.alfresco.repo.lock.JobLockService; import org.alfresco.repo.nodelocator.NodeLocatorService; import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.rendition2.RenditionService2; +import org.alfresco.repo.rendition2.SynchronousTransformClient; import org.alfresco.repo.search.impl.solr.facet.SolrFacetHelper; import org.alfresco.repo.search.impl.solr.facet.handler.FacetLabelDisplayHandlerRegistry; import org.alfresco.repo.transaction.RetryingTransactionHelper; @@ -112,6 +113,7 @@ public interface ServiceRegistry static final QName DICTIONARY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "DictionaryService"); static final QName NODE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "NodeService"); static final QName CONTENT_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ContentService"); + static final QName SYNCHRONOUS_TRANSFORM_CLIENT = QName.createQName(NamespaceService.ALFRESCO_URI, "synchronousTransformClient"); static final QName MIMETYPE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "MimetypeService"); static final QName CONTENT_FILTER_LANGUAGES_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ContentFilterLanguagesService"); static final QName MULTILINGUAL_CONTENT_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "MultilingualContentService"); @@ -620,4 +622,13 @@ public interface ServiceRegistry */ @NotAuditable RenditionService2 getRenditionService2(); + + /** + * @return the synchronous transform client (or null, if one is not provided) + */ + @NotAuditable + default SynchronousTransformClient getSynchronousTransformClient() + { + return null; + } } diff --git a/src/main/java/org/alfresco/service/cmr/repository/ContentService.java b/src/main/java/org/alfresco/service/cmr/repository/ContentService.java index 1027fafdf2..a42781d4d4 100644 --- a/src/main/java/org/alfresco/service/cmr/repository/ContentService.java +++ b/src/main/java/org/alfresco/service/cmr/repository/ContentService.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2019 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -25,11 +25,7 @@ */ package org.alfresco.service.cmr.repository; -import java.util.List; -import java.util.Map; - import org.alfresco.api.AlfrescoPublicApi; -import org.alfresco.repo.content.transform.ContentTransformer; import org.alfresco.service.Auditable; import org.alfresco.service.cmr.dictionary.InvalidTypeException; import org.alfresco.service.namespace.QName; @@ -56,7 +52,7 @@ import org.alfresco.service.namespace.QName; * @author Derek Hulley */ @AlfrescoPublicApi -public interface ContentService +public interface ContentService extends ContentTransformService { /** * Gets the total space of the underlying content store (not exclusively Alfresco-controlled binaries). @@ -156,207 +152,4 @@ public interface ContentService */ @Auditable public ContentWriter getTempWriter(); - - /** - * Transforms the content from the reader and writes the content - * back out to the writer. - *

    - * The mimetypes used for the transformation must be set both on - * the {@link ContentAccessor#getMimetype() reader} and on the - * {@link ContentAccessor#getMimetype() writer}. - * - * @param reader the source content location and mimetype - * @param writer the target content location and mimetype - * @throws NoTransformerException if no transformer exists for the - * given source and target mimetypes of the reader and writer - * @throws ContentIOException if the transformation fails - * - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. - */ - @Deprecated - @Auditable(parameters = {"reader", "writer"}) - public void transform(ContentReader reader, ContentWriter writer) - throws NoTransformerException, ContentIOException; - - - /** - * @see ContentService#transform(ContentReader, ContentWriter) - * @see ContentService#transform(ContentReader, ContentWriter, TransformationOptions) - * - * A map of transform options can be provided. - * - * @param reader the source content location and mimetype - * @param writer the target content location and mimetype - * @param options the options for the transformation - * @throws NoTransformerException if no transformer exists for the - * given source and target mimetypes of the reader and writer - * @throws ContentIOException if the transformation fails - * - * @deprecated - * As of release 3.0 the TransformOptions class should be used to pass transformation options - * to a transformation execution. - */ - @Deprecated - @Auditable(parameters = {"reader", "writer", "options"}) - public void transform(ContentReader reader, ContentWriter writer, Map options) - throws NoTransformerException, ContentIOException; - - /** - * @see ContentService#transform(ContentReader, ContentWriter) - * - * A transformation options can be provided. - * - * @param reader the source content location and mimetype - * @param writer the target content location and mimetype - * @param options the options for the transformation - * @throws NoTransformerException if no transformer exists for the - * given source and target mimetypes of the reader and writer - * @throws ContentIOException if the transformation fails - * - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. - */ - @Deprecated - @Auditable(parameters = {"reader", "writer", "options"}) - public void transform(ContentReader reader, ContentWriter writer, TransformationOptions options) - throws NoTransformerException, ContentIOException; - - /** - * Fetch the transformer that is capable of transforming the content in the - * given source mimetype to the given target mimetype. - *

    - * Since no transformation options are provided only the source and destination mimetypes are - * considered when getting the correct transformer. - * - * @param sourceMimetype the source mimetype - * @param targetMimetype the target mimetype - * @return Returns a transformer that can be used, or null if one was not available - * - * @see ContentService#getTransformer(String, String, long, String, TransformationOptions) - * @see ContentAccessor#getMimetype() - * - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. - */ - @Deprecated - @Auditable(parameters = {"sourceMimetype", "targetMimetype"}) - public ContentTransformer getTransformer(String sourceMimetype, String targetMimetype); - - /** - * Fetch the transformers that are capable of transforming the content in the - * given source mimetype to the given target mimetype with the provided transformation - * options. - *

    - * The transformation options provide a finer grain way of discovering the correct transformer, - * since the values and type of the options provided are considered by the transformer when - * deciding whether it can satisfy the transformation request. - * @param sourceUrl TODO - * @param sourceMimetype the source mimetype - * @param sourceSize the source size (bytes). Ignored if negative. - * @param targetMimetype the target mimetype - * @param options the transformation options - * - * @return ContentTransformer the transformers that can be used, or null if none are available - * - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. - */ - @Deprecated - @Auditable(parameters = {"sourceMimetype", "sourceSize", "targetMimetype", "options"}) - public List getTransformers(String sourceUrl, String sourceMimetype, long sourceSize, String targetMimetype, TransformationOptions options); - - /** - * Fetch the transformer that is capable of transforming the content in the - * given source mimetype to the given target mimetype with the provided transformation - * options. - *

    - * The transformation options provide a finer grain way of discovering the correct transformer, - * since the values and type of the options provided are considered by the transformer when - * deciding whether it can satisfy the transformation request. - * @param sourceUrl TODO - * @param sourceMimetype the source mimetype - * @param sourceSize the source size (bytes). Ignored if negative. - * @param targetMimetype the target mimetype - * @param options the transformation options - * - * @return ContentTransformer a transformer that can be used, or null if one was not available - * - * @see ContentAccessor#getMimetype() - * - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. - */ - @Deprecated - @Auditable(parameters = {"sourceMimetype", "sourceSize", "targetMimetype", "options"}) - public ContentTransformer getTransformer(String sourceUrl, String sourceMimetype, long sourceSize, String targetMimetype, TransformationOptions options); - - /** - * @deprecated use overloaded method with sourceSize parameter. - */ - @Deprecated - public ContentTransformer getTransformer(String sourceMimetype, String targetMimetype, TransformationOptions options); - - /** - * Returns the maximum source size of any content that may transformed between the supplied - * mimetypes using the supplied options. - * @param sourceMimetype source mime type - * @param targetMimetype target mime type - * @param options transformation options - * @return 0 if there are no transformers, -1 if there is no limit or if positive number the size in bytes. - * - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. - */ - @Deprecated - @Auditable(parameters = {"sourceMimetype", "targetMimetype", "options"}) - public long getMaxSourceSizeBytes(String sourceMimetype, String targetMimetype, TransformationOptions options); - - /** - * @deprecated use {@link #getTransformers(String, String, long, String, TransformationOptions)}. - * @since 3.5 - */ - @Auditable(parameters = {"sourceMimetype", "sourceSize", "targetMimetype", "options"}) - public List getActiveTransformers(String sourceMimetype, long sourceSize, String targetMimetype, TransformationOptions options); - - /** - * @deprecated use {@link #getTransformers(String, String, long, String, TransformationOptions)}. - */ - public List getActiveTransformers(String sourceMimetype, String targetMimetype, TransformationOptions options); - - /** - * Fetch the transformer that is capable of transforming image content. - * - * @return Returns a transformer that can be used, or null if one was not available - * - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. - */ - @Deprecated - @Auditable - public ContentTransformer getImageTransformer(); - - /** - * @deprecated use {@link #isTransformable(ContentReader, ContentWriter, TransformationOptions)}. - */ - @Deprecated - @Auditable(parameters = {"reader", "writer"}) - public boolean isTransformable(ContentReader reader, ContentWriter writer); - - /** - * Returns whether a transformer exists that can read the content from - * the reader and write the content back out to the writer with the - * provided tranformation options. - *

    - * If you are about to call {@link #transform(ContentReader, ContentWriter, TransformationOptions)} - * it is best NOT to call this method first as it must perform the same steps and will throw - * NoTransformerException if there are no transformers.

    - * - * The mimetypes used for the transformation must be set both on - * the {@link ContentAccessor#getMimetype() reader} and on the - * {@link ContentAccessor#getMimetype() writer}. - * - * @param reader the source content location and mimetype - * @param writer the target content location and mimetype - * @param options the transformation options - * @return boolean true if a transformer exists, false otherwise - * - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. - */ - @Deprecated - @Auditable(parameters = {"reader", "writer", "options"}) - public boolean isTransformable(ContentReader reader, ContentWriter writer, TransformationOptions options); } diff --git a/src/main/java/org/alfresco/service/cmr/repository/ContentTransformService.java b/src/main/java/org/alfresco/service/cmr/repository/ContentTransformService.java new file mode 100644 index 0000000000..9f9a5cd0f2 --- /dev/null +++ b/src/main/java/org/alfresco/service/cmr/repository/ContentTransformService.java @@ -0,0 +1,134 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2019 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.service.cmr.repository; + +import org.alfresco.repo.content.transform.ContentTransformer; +import org.alfresco.repo.rendition2.SynchronousTransformClient; +import org.alfresco.service.Auditable; +import org.alfresco.transform.client.registry.TransformServiceRegistry; + +import java.util.List; +import java.util.Map; + +/** + * Contains deprecated code from {@link ContentService} that is used to perform Legacy transforms. + * + * @author adavis + */ +public interface ContentTransformService +{ + /** + * @deprecated use {@link SynchronousTransformClient#transform(ContentReader, ContentWriter, Map, String, NodeRef)}. + */ + @Deprecated + @Auditable(parameters = {"reader", "writer"}) + void transform(ContentReader reader, ContentWriter writer) + throws NoTransformerException, ContentIOException; + + + /** + * @deprecated use {@link SynchronousTransformClient#transform(ContentReader, ContentWriter, Map, String, NodeRef)}. + */ + @Deprecated + @Auditable(parameters = {"reader", "writer", "options"}) + void transform(ContentReader reader, ContentWriter writer, Map options) + throws NoTransformerException, ContentIOException; + + /** + * @deprecated use {@link SynchronousTransformClient#transform(ContentReader, ContentWriter, Map, String, NodeRef)}. + */ + @Deprecated + @Auditable(parameters = {"reader", "writer", "options"}) + void transform(ContentReader reader, ContentWriter writer, TransformationOptions options) + throws NoTransformerException, ContentIOException; + + /** + * @deprecated use {@link SynchronousTransformClient#isSupported(String, long, String, String, Map, String, NodeRef)}. + */ + @Deprecated + @Auditable(parameters = {"sourceMimetype", "targetMimetype"}) + ContentTransformer getTransformer(String sourceMimetype, String targetMimetype); + + /** + * @deprecated use {@link SynchronousTransformClient#isSupported(String, long, String, String, Map, String, NodeRef)}. + */ + @Deprecated + @Auditable(parameters = {"sourceMimetype", "sourceSize", "targetMimetype", "options"}) + List getTransformers(String sourceUrl, String sourceMimetype, long sourceSize, String targetMimetype, TransformationOptions options); + + /** + * @deprecated use {@link SynchronousTransformClient#isSupported(String, long, String, String, Map, String, NodeRef)}. + */ + @Deprecated + @Auditable(parameters = {"sourceMimetype", "sourceSize", "targetMimetype", "options"}) + ContentTransformer getTransformer(String sourceUrl, String sourceMimetype, long sourceSize, String targetMimetype, TransformationOptions options); + + /** + * @deprecated use {@link SynchronousTransformClient#isSupported(String, long, String, String, Map, String, NodeRef)}. + */ + @Deprecated + ContentTransformer getTransformer(String sourceMimetype, String targetMimetype, TransformationOptions options); + + /** + * @deprecated use {@link TransformServiceRegistry#findMaxSize(String, String, Map, String)}. + */ + @Deprecated + @Auditable(parameters = {"sourceMimetype", "targetMimetype", "options"}) + long getMaxSourceSizeBytes(String sourceMimetype, String targetMimetype, TransformationOptions options); + + /** + * @deprecated use {@link SynchronousTransformClient#isSupported(String, long, String, String, Map, String, NodeRef)}. + */ + @Auditable(parameters = {"sourceMimetype", "sourceSize", "targetMimetype", "options"}) + List getActiveTransformers(String sourceMimetype, long sourceSize, String targetMimetype, TransformationOptions options); + + /** + * @deprecated use {@link SynchronousTransformClient#isSupported(String, long, String, String, Map, String, NodeRef)}. + */ + List getActiveTransformers(String sourceMimetype, String targetMimetype, TransformationOptions options); + + /** + * @deprecated there is no longer a need to obtain a transformer specifically for images. + * Use {@link SynchronousTransformClient#transform(ContentReader, ContentWriter, Map, String, NodeRef)}. + */ + @Deprecated + @Auditable + ContentTransformer getImageTransformer(); + + /** + * @deprecated use {@link SynchronousTransformClient#isSupported(String, long, String, String, Map, String, NodeRef)}. + */ + @Deprecated + @Auditable(parameters = {"reader", "writer"}) + boolean isTransformable(ContentReader reader, ContentWriter writer); + + /** + * @deprecated use {@link SynchronousTransformClient#isSupported(String, long, String, String, Map, String, NodeRef)}. + */ + @Deprecated + @Auditable(parameters = {"reader", "writer", "options"}) + boolean isTransformable(ContentReader reader, ContentWriter writer, TransformationOptions options); +} diff --git a/src/main/resources/alfresco/action-services-context.xml b/src/main/resources/alfresco/action-services-context.xml index 321ee9d495..8c7f5258b4 100644 --- a/src/main/resources/alfresco/action-services-context.xml +++ b/src/main/resources/alfresco/action-services-context.xml @@ -440,6 +440,8 @@ + + {http://www.alfresco.org/model/content/1.0}content diff --git a/src/main/resources/alfresco/content-services-context.xml b/src/main/resources/alfresco/content-services-context.xml index f0e69ac5b8..d49bf8052f 100644 --- a/src/main/resources/alfresco/content-services-context.xml +++ b/src/main/resources/alfresco/content-services-context.xml @@ -148,9 +148,6 @@ - - - @@ -166,9 +163,11 @@ ${policy.content.update.ignoreEmpty} - - - + + + + + @@ -458,9 +457,7 @@ abstract="true" init-method="register" parent="baseContentTransformer"> - - - + application/pdf + diff --git a/src/main/resources/alfresco/extension/video-thumbnail-context.xml.sample b/src/main/resources/alfresco/extension/video-thumbnail-context.xml.sample index f0c1e8ffc3..30c8e48f4b 100644 --- a/src/main/resources/alfresco/extension/video-thumbnail-context.xml.sample +++ b/src/main/resources/alfresco/extension/video-thumbnail-context.xml.sample @@ -111,6 +111,7 @@ content.transformer.ffmpeg.thumbnail.extensions.wmv.jpg.supported=true image/jpeg + diff --git a/src/main/resources/alfresco/extension/xpdf-transform-context.xml.sample b/src/main/resources/alfresco/extension/xpdf-transform-context.xml.sample index c74803587c..890f3f6e4e 100644 --- a/src/main/resources/alfresco/extension/xpdf-transform-context.xml.sample +++ b/src/main/resources/alfresco/extension/xpdf-transform-context.xml.sample @@ -99,5 +99,6 @@ application/pdf + \ No newline at end of file diff --git a/src/main/resources/alfresco/public-services-security-context.xml b/src/main/resources/alfresco/public-services-security-context.xml index 77a5d66ccf..a5a2b401a6 100644 --- a/src/main/resources/alfresco/public-services-security-context.xml +++ b/src/main/resources/alfresco/public-services-security-context.xml @@ -493,11 +493,9 @@ org.alfresco.service.cmr.repository.ContentService.getRawReader=ACL_METHOD.ROLE_ADMINISTRATOR org.alfresco.service.cmr.repository.ContentService.getReader=ACL_NODE.0.sys:base.ReadContent org.alfresco.service.cmr.repository.ContentService.getWriter=ACL_NODE.0.sys:base.WriteContent - org.alfresco.service.cmr.repository.ContentService.isTransformable=ACL_ALLOW - org.alfresco.service.cmr.repository.ContentService.getTransformer=ACL_ALLOW - org.alfresco.service.cmr.repository.ContentService.getMaxSourceSizeBytes=ACL_ALLOW - org.alfresco.service.cmr.repository.ContentService.getImageTransformer=ACL_ALLOW - org.alfresco.service.cmr.repository.ContentService.transform=ACL_ALLOW + org.alfresco.service.cmr.repository.ContentTransformService.getTransformer=ACL_ALLOW + org.alfresco.service.cmr.repository.ContentTransformService.getMaxSourceSizeBytes=ACL_ALLOW + org.alfresco.service.cmr.repository.ContentTransformService.transform=ACL_ALLOW org.alfresco.service.cmr.repository.ContentService.getTempWriter=ACL_ALLOW org.alfresco.service.cmr.repository.ContentService.*=ACL_DENY diff --git a/src/main/resources/alfresco/rendition-services-context.xml b/src/main/resources/alfresco/rendition-services-context.xml index 51849d394d..d4ea1c3330 100644 --- a/src/main/resources/alfresco/rendition-services-context.xml +++ b/src/main/resources/alfresco/rendition-services-context.xml @@ -137,6 +137,12 @@ + + + + + @@ -146,12 +152,12 @@ + parent="baseTransformationRenderingEngine"> + parent="baseTransformationRenderingEngine"> diff --git a/src/main/resources/alfresco/rendition-services2-context.xml b/src/main/resources/alfresco/rendition-services2-context.xml index 72685a5d18..8265dca616 100644 --- a/src/main/resources/alfresco/rendition-services2-context.xml +++ b/src/main/resources/alfresco/rendition-services2-context.xml @@ -92,12 +92,16 @@ + + + + @@ -107,7 +111,9 @@ - + + + @@ -144,7 +150,7 @@ - + diff --git a/src/main/resources/alfresco/subsystems/Transformers/default/transformers-context.xml b/src/main/resources/alfresco/subsystems/Transformers/default/transformers-context.xml index 02e7d4145d..0f17696462 100644 --- a/src/main/resources/alfresco/subsystems/Transformers/default/transformers-context.xml +++ b/src/main/resources/alfresco/subsystems/Transformers/default/transformers-context.xml @@ -7,8 +7,8 @@ - - + + diff --git a/src/main/resources/alfresco/thumbnail-service-context.xml b/src/main/resources/alfresco/thumbnail-service-context.xml index baeef132d3..4f1020f6b2 100644 --- a/src/main/resources/alfresco/thumbnail-service-context.xml +++ b/src/main/resources/alfresco/thumbnail-service-context.xml @@ -232,12 +232,13 @@ - + + diff --git a/src/test/java/org/alfresco/MiscContextTestSuite.java b/src/test/java/org/alfresco/MiscContextTestSuite.java index 6dc71f675c..3d2c6a843d 100644 --- a/src/test/java/org/alfresco/MiscContextTestSuite.java +++ b/src/test/java/org/alfresco/MiscContextTestSuite.java @@ -88,6 +88,22 @@ import org.springframework.context.ApplicationContext; org.alfresco.repo.content.metadata.MappingMetadataExtracterTest.class, + // ---------------------------------------------------------------------- + // Transformer/Rendition contexts + // + // The following tests can be extracted in a separate test suite + // if/when we decide to move the transformations in a separate component + // ---------------------------------------------------------------------- + + // [classpath:alfresco/application-context.xml, classpath:org/alfresco/repo/thumbnail/test-thumbnail-context.xml] + // some tests fail locally - on windows + org.alfresco.repo.thumbnail.ThumbnailServiceImplTest.class, + + // [classpath:/test/alfresco/test-renditions-context.xml, classpath:alfresco/application-context.xml, + // classpath:alfresco/test/global-integration-test-context.xml] + // this does NOT passes locally + org.alfresco.repo.rendition.RenditionServicePermissionsTest.class, + // ---------------------------------------------------------------------- // Misc contexts // ---------------------------------------------------------------------- @@ -122,22 +138,6 @@ import org.springframework.context.ApplicationContext; // [module/module-component-test-beans.xml] org.alfresco.repo.module.ComponentsTest.class, - // ---------------------------------------------------------------------- - // Transformer/Rendition contexts - // - // The following tests can be extracted in a separate test suite - // if/when we decide to move the transformations in a separate component - // ---------------------------------------------------------------------- - - // [classpath:alfresco/application-context.xml, classpath:org/alfresco/repo/thumbnail/test-thumbnail-context.xml] - // some tests fail locally - on windows - org.alfresco.repo.thumbnail.ThumbnailServiceImplTest.class, - - // [classpath:/test/alfresco/test-renditions-context.xml, classpath:alfresco/application-context.xml, - // classpath:alfresco/test/global-integration-test-context.xml] - // this does NOT passes locally - org.alfresco.repo.rendition.RenditionServicePermissionsTest.class, - // [ibatis/hierarchy-test/hierarchy-test-context.xml] org.alfresco.ibatis.HierarchicalSqlSessionFactoryBeanTest.class }) diff --git a/src/test/java/org/alfresco/repo/content/AbstractJodConverterBasedTest.java b/src/test/java/org/alfresco/repo/content/AbstractJodConverterBasedTest.java index da51bfc819..0a1523d53d 100644 --- a/src/test/java/org/alfresco/repo/content/AbstractJodConverterBasedTest.java +++ b/src/test/java/org/alfresco/repo/content/AbstractJodConverterBasedTest.java @@ -27,6 +27,7 @@ package org.alfresco.repo.content; import java.io.File; import java.io.Serializable; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; @@ -38,6 +39,7 @@ import org.alfresco.repo.content.transform.AbstractContentTransformerTest; import org.alfresco.repo.content.transform.ContentTransformer; import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory; import org.alfresco.repo.model.Repository; +import org.alfresco.repo.rendition2.SynchronousTransformClient; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.ServiceRegistry; @@ -128,17 +130,9 @@ public abstract class AbstractJodConverterBasedTest */ protected boolean isOpenOfficeAvailable() { - ContentTransformer transformer = contentService.getTransformer(null, MimetypeMap.MIMETYPE_WORD, -1, MimetypeMap.MIMETYPE_PDF, new TransformationOptions()); - - // A transformer may not be returned here if it is unavailable. - if (transformer == null) - { - return false; - } - - // Maybe it's non-null, but not available. - boolean isTransformable = transformer.isTransformable(MimetypeMap.MIMETYPE_WORD, -1, MimetypeMap.MIMETYPE_PDF, null); - return isTransformable; + SynchronousTransformClient synchronousTransformClient = serviceRegistry.getSynchronousTransformClient(); + return synchronousTransformClient.isSupported(MimetypeMap.MIMETYPE_WORD, -1, null, + MimetypeMap.MIMETYPE_PDF, Collections.emptyMap(), null, null); } @Before diff --git a/src/test/java/org/alfresco/repo/content/transform/ArchiveContentTransformerTest.java b/src/test/java/org/alfresco/repo/content/transform/ArchiveContentTransformerTest.java index 3c2d1b5440..55565ef544 100644 --- a/src/test/java/org/alfresco/repo/content/transform/ArchiveContentTransformerTest.java +++ b/src/test/java/org/alfresco/repo/content/transform/ArchiveContentTransformerTest.java @@ -2,7 +2,8 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + + * Copyright (C) 2005 - 2019 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -27,11 +28,13 @@ package org.alfresco.repo.content.transform; import java.io.File; import java.io.IOException; +import java.util.Collections; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.filestore.FileContentReader; import org.alfresco.repo.content.filestore.FileContentWriter; import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory; +import org.alfresco.repo.rendition2.SynchronousTransformClient; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentWriter; diff --git a/src/test/java/org/alfresco/repo/content/transform/EMLTransformerTest.java b/src/test/java/org/alfresco/repo/content/transform/EMLTransformerTest.java index d0161da515..965d78156d 100644 --- a/src/test/java/org/alfresco/repo/content/transform/EMLTransformerTest.java +++ b/src/test/java/org/alfresco/repo/content/transform/EMLTransformerTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2019 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -26,18 +26,20 @@ package org.alfresco.repo.content.transform; -import java.io.File; - import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.filestore.FileContentReader; import org.alfresco.repo.content.filestore.FileContentWriter; import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory; +import org.alfresco.repo.rendition2.SynchronousTransformClient; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.TransformationOptions; import org.alfresco.util.TempFileProvider; +import java.io.File; +import java.util.Collections; + /** * @see org.alfresco.repo.content.transform.EMLTransformer * @@ -65,6 +67,7 @@ public class EMLTransformerTest extends AbstractContentTransformerTest private EMLTransformer transformer; private ContentTransformerRegistry registry; + private SynchronousTransformClient synchronousTransformClient; @Override public void setUp() throws Exception @@ -77,6 +80,7 @@ public class EMLTransformerTest extends AbstractContentTransformerTest transformer.setTransformerConfig(transformerConfig); registry = (ContentTransformerRegistry) ctx.getBean("contentTransformerRegistry"); + synchronousTransformClient = (SynchronousTransformClient) ctx.getBean("synchronousTransformClient"); } @Override @@ -136,8 +140,7 @@ public class EMLTransformerTest extends AbstractContentTransformerTest } AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); - ContentTransformer transformer = serviceRegistry.getContentService().getTransformer(sourceMimetype, targetMimetype); - assertNotNull(transformer); + assertTrue(synchronousTransformClient.isSupported(sourceMimetype, -1, null, targetMimetype, Collections.emptyMap(), null, null)); String sourceExtension = mimetypeService.getExtension(sourceMimetype); String targetExtension = mimetypeService.getExtension(targetMimetype); @@ -153,7 +156,7 @@ public class EMLTransformerTest extends AbstractContentTransformerTest // do the transformation sourceReader.setMimetype(sourceMimetype); targetWriter.setMimetype(targetMimetype); - transformer.transform(sourceReader.getReader(), targetWriter); + synchronousTransformClient.transform(sourceReader, targetWriter, Collections.emptyMap(), null, null); ContentReader targetReader = new FileContentReader(targetFile); assertTrue(targetReader.getSize() > 0); diff --git a/src/test/java/org/alfresco/repo/content/transform/RemoteTransformerClientTest.java b/src/test/java/org/alfresco/repo/content/transform/RemoteTransformerClientTest.java index db574bf756..2887a48b41 100644 --- a/src/test/java/org/alfresco/repo/content/transform/RemoteTransformerClientTest.java +++ b/src/test/java/org/alfresco/repo/content/transform/RemoteTransformerClientTest.java @@ -168,7 +168,7 @@ public class RemoteTransformerClientTest Pair available = remoteTransformerClient.check(mockLogger); assertFalse("Any failure should result in false", available.getFirst()); - assertEquals("Remote TRANSFORMER check returned a 1234 status AN ERROR MESSAGE http://localhost:1234/test/version", + assertEquals("TRANSFORMER check returned a 1234 status AN ERROR MESSAGE http://localhost:1234/test/version", getMessage(available.getSecond())); } @@ -178,7 +178,7 @@ public class RemoteTransformerClientTest when(mockStatusLine.getStatusCode()).thenReturn(1234); doReturn("\"message\":\"AN ERROR MESSAGE\",\"path\":").when(remoteTransformerClient).getContent(any()); - assertRequestTransformError("Remote TRANSFORMER returned a 1234 status AN ERROR MESSAGE http://localhost:1234/test/transform"); + assertRequestTransformError("TRANSFORMER returned a 1234 status AN ERROR MESSAGE http://localhost:1234/test/transform"); } @Test @@ -190,7 +190,7 @@ public class RemoteTransformerClientTest Pair available = remoteTransformerClient.check(mockLogger); assertFalse("Any failure should result in false", available.getFirst()); - assertEquals("Remote TRANSFORMER check failed to connect or to read the response", + assertEquals("TRANSFORMER check failed to connect or to read the response", getMessage(available.getSecond())); assertTransformerBecomesAvailableAgainAfterFailure(); @@ -204,7 +204,7 @@ public class RemoteTransformerClientTest // Mock a connection failure, check the error and reset the mock doThrow(IOException.class).when(remoteTransformerClient).execute(any(), any(HttpPost.class)); - assertRequestTransformError("Remote TRANSFORMER failed to connect or to read the response"); + assertRequestTransformError("TRANSFORMER failed to connect or to read the response"); assertFalse(remoteTransformerClient.isAvailable()); doReturn(mockHttpResponse).when(remoteTransformerClient).execute(any(), any(HttpPost.class)); diff --git a/src/test/java/org/alfresco/repo/content/transform/TransformerConfigDynamicTransformersTest.java b/src/test/java/org/alfresco/repo/content/transform/TransformerConfigDynamicTransformersTest.java index 499c96a661..bc0c4a8a9d 100644 --- a/src/test/java/org/alfresco/repo/content/transform/TransformerConfigDynamicTransformersTest.java +++ b/src/test/java/org/alfresco/repo/content/transform/TransformerConfigDynamicTransformersTest.java @@ -32,9 +32,9 @@ import static org.mockito.Mockito.when; import java.util.Properties; +import org.alfresco.repo.rendition2.LegacySynchronousTransformClient; import org.alfresco.service.cmr.module.ModuleDetails; import org.alfresco.service.cmr.module.ModuleService; -import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.MimetypeService; import org.alfresco.service.cmr.repository.TransformationOptions; import org.alfresco.service.descriptor.Descriptor; @@ -64,7 +64,7 @@ public class TransformerConfigDynamicTransformersTest private MimetypeService mimetypeService; @Mock - private ContentService contentService; + private LegacySynchronousTransformClient legacySynchronousTransformClient; @Mock private TransformerDebug transformerDebug; @@ -118,7 +118,7 @@ public class TransformerConfigDynamicTransformersTest mockProperties(transformerProperties, "content.transformer.transformerA.pipeline", "transformer1|pdf|transformer2|png|transformer3"); - assertEquals(0, new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + assertEquals(0, new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, legacySynchronousTransformClient, transformerRegistry, transformerDebug, null, null, null).getErrorCount()); assertEquals(4, transformerRegistry.getAllTransformers().size()); @@ -149,7 +149,7 @@ public class TransformerConfigDynamicTransformersTest mockProperties(transformerProperties, "content.transformer.transformerA.pipeline", "transformer1|pdf"); - assertEquals(1, new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + assertEquals(1, new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, legacySynchronousTransformClient, transformerRegistry, transformerDebug, null, null, null).getErrorCount()); } @@ -160,7 +160,7 @@ public class TransformerConfigDynamicTransformersTest mockProperties(transformerProperties, "content.transformer.transformerA.pipeline", "transformer1|pdf|transformer2|png"); - assertEquals(1, new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + assertEquals(1, new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, legacySynchronousTransformClient, transformerRegistry, transformerDebug, null, null, null).getErrorCount()); } @@ -171,7 +171,7 @@ public class TransformerConfigDynamicTransformersTest mockProperties(transformerProperties, "content.transformer.transformer3.pipeline", "transformer1|pdf|transformer2"); - assertEquals(1, new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + assertEquals(1, new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, legacySynchronousTransformClient, transformerRegistry, transformerDebug, null, null, null).getErrorCount()); } @@ -182,7 +182,7 @@ public class TransformerConfigDynamicTransformersTest mockProperties(transformerProperties, "content.transformer.transformerA.pipeline", "transformer1|*|transformer2|png|transformer3"); - new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, legacySynchronousTransformClient, transformerRegistry, transformerDebug, null, null, null); transformerRegistry.getTransformer("transformer.transformerA"); @@ -195,7 +195,7 @@ public class TransformerConfigDynamicTransformersTest mockProperties(transformerProperties, "content.transformer.transformerA.pipeline", "transformer1|pdf|*|png|transformer3"); - new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, legacySynchronousTransformClient, transformerRegistry, transformerDebug, null, null, null); transformerRegistry.getTransformer("transformer.transformerA"); @@ -208,7 +208,7 @@ public class TransformerConfigDynamicTransformersTest mockProperties(transformerProperties, "content.transformer.transformerA.pipeline", "unknown1|pdf|unknown2|png|unknown3"); - assertEquals(1, new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + assertEquals(1, new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, legacySynchronousTransformClient, transformerRegistry, transformerDebug, null, null, null).getErrorCount()); } @@ -220,7 +220,7 @@ public class TransformerConfigDynamicTransformersTest "content.transformer.transformerA.pipeline", "transformer1|pdf|transformer2|png|transformer3", "content.transformer.transformerA.available", "false"); - new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, legacySynchronousTransformClient, transformerRegistry, transformerDebug, null, null, null); assertEquals(4, transformerRegistry.getAllTransformers().size()); @@ -238,7 +238,7 @@ public class TransformerConfigDynamicTransformersTest mockProperties(transformerProperties, "content.transformer.transformerA.failover", "transformer1|transformer2|transformer3"); - assertEquals(0, new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + assertEquals(0, new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, legacySynchronousTransformClient, transformerRegistry, transformerDebug, null, null, null).getErrorCount()); assertEquals(4, transformerRegistry.getAllTransformers().size()); @@ -255,7 +255,7 @@ public class TransformerConfigDynamicTransformersTest mockProperties(transformerProperties, "content.transformer.transformerA.failover", "transformer1"); - assertEquals(1, new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + assertEquals(1, new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, legacySynchronousTransformClient, transformerRegistry, transformerDebug, null, null, null).getErrorCount()); } @@ -266,7 +266,7 @@ public class TransformerConfigDynamicTransformersTest mockProperties(transformerProperties, "content.transformer.transformer3.failover", "transformer1|transformer2"); - assertEquals(1, new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + assertEquals(1, new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, legacySynchronousTransformClient, transformerRegistry, transformerDebug, null, null, null).getErrorCount()); } @@ -277,7 +277,7 @@ public class TransformerConfigDynamicTransformersTest mockProperties(transformerProperties, "content.transformer.transformerA.failover", "transformer1|*|transformer3"); - new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, legacySynchronousTransformClient, transformerRegistry, transformerDebug, null, null, null); transformerRegistry.getTransformer("transformer.transformerA"); @@ -290,7 +290,7 @@ public class TransformerConfigDynamicTransformersTest mockProperties(transformerProperties, "content.transformer.transformerA.failover", "unknown1|unknown2|unknown3"); - assertEquals(1, new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + assertEquals(1, new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, legacySynchronousTransformClient, transformerRegistry, transformerDebug, null, null, null).getErrorCount()); } @@ -302,7 +302,7 @@ public class TransformerConfigDynamicTransformersTest "content.transformer.transformerA.failover", "transformer1|transformer2|transformer3", "content.transformer.transformerA.available", "false"); - new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, legacySynchronousTransformClient, transformerRegistry, transformerDebug, null, null, null); assertEquals(4, transformerRegistry.getAllTransformers().size()); @@ -322,7 +322,7 @@ public class TransformerConfigDynamicTransformersTest "content.transformer.transformerD.failover", "transformer1|transformerE", "content.transformer.transformerE.failover", "transformer1|transformer1"); - new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, legacySynchronousTransformClient, transformerRegistry, transformerDebug, null, null, null); assertEquals(5, transformerRegistry.getTransformers().size()); @@ -341,7 +341,7 @@ public class TransformerConfigDynamicTransformersTest "content.transformer.transformerD.failover", "transformer1|transformerE", "content.transformer.transformerE.failover", "transformer1|transformerC"); - new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, legacySynchronousTransformClient, transformerRegistry, transformerDebug, null, null, null); assertEquals(2, transformerRegistry.getTransformers().size()); @@ -358,7 +358,7 @@ public class TransformerConfigDynamicTransformersTest "content.transformer.transformerA.failover", "transformer1|transformer2|transformer3", "content.transformer.transformerA.edition", "Enterprise"); - new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, legacySynchronousTransformClient, transformerRegistry, transformerDebug, null, descriptorService, null); } @@ -370,7 +370,7 @@ public class TransformerConfigDynamicTransformersTest "content.transformer.transformerA.failover", "transformer1|transformer2|transformer3", "content.transformer.transformerA.amp", moduleId); - new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, legacySynchronousTransformClient, transformerRegistry, transformerDebug, moduleService, null, null); } @@ -477,7 +477,7 @@ public class TransformerConfigDynamicTransformersTest mockProperties(transformerProperties, transformerNamesAndValues); - assertEquals(0, new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + assertEquals(0, new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, legacySynchronousTransformClient, transformerRegistry, transformerDebug, null, null, properties).getErrorCount()); // Throws an exception if it does not exist diff --git a/src/test/java/org/alfresco/repo/content/transform/TransformerDebugTest.java b/src/test/java/org/alfresco/repo/content/transform/TransformerDebugTest.java index e6e0940300..6cdf92c3dc 100644 --- a/src/test/java/org/alfresco/repo/content/transform/TransformerDebugTest.java +++ b/src/test/java/org/alfresco/repo/content/transform/TransformerDebugTest.java @@ -170,9 +170,9 @@ public class TransformerDebugTest assertDebugEntriesEquals(new String[] { "0 pdf txt 1.5 MB ContentService.transform(...) NO transformers\n"+ - "0 --a) [---] transformer1<> > 50 KB\n"+ - "0 --b) [---] transformer3<> > 50 KB\n"+ - "0 --c) [---] transformer4<> > 50 KB\n"+ + "0 --a) [---] Legacy:transformer1<> > 50 KB\n"+ + "0 --b) [---] Legacy:transformer3<> > 50 KB\n"+ + "0 --c) [---] Legacy:transformer4<> > 50 KB\n"+ "0 Finished in NN ms Just checking if a transformer is available"}, unnumbered(untimed(debug.getEntries(10)))); assertArrayEquals(new String[] { "0 pdf txt WARN 1.5 MB NN ms No transformers as file is > 50 KB"}, unnumbered(untimed(stripDateStamp(log.getEntries(10))))); diff --git a/src/test/java/org/alfresco/repo/rendition/MockedTestServiceRegistry.java b/src/test/java/org/alfresco/repo/rendition/MockedTestServiceRegistry.java index 64f5458cd8..c2f81c16f8 100644 --- a/src/test/java/org/alfresco/repo/rendition/MockedTestServiceRegistry.java +++ b/src/test/java/org/alfresco/repo/rendition/MockedTestServiceRegistry.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2019 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -90,11 +90,6 @@ import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.transaction.TransactionService; -/** - * - * @deprecated We are introducing the new async RenditionService2. - */ -@Deprecated public class MockedTestServiceRegistry implements ServiceRegistry { private final ActionService actionService = mock(ActionService.class); diff --git a/src/test/java/org/alfresco/repo/rendition2/AbstractRenditionIntegrationTest.java b/src/test/java/org/alfresco/repo/rendition2/AbstractRenditionIntegrationTest.java index efd5066195..719234cedd 100644 --- a/src/test/java/org/alfresco/repo/rendition2/AbstractRenditionIntegrationTest.java +++ b/src/test/java/org/alfresco/repo/rendition2/AbstractRenditionIntegrationTest.java @@ -115,6 +115,9 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest @Autowired protected LegacyTransformServiceRegistry legacyTransformServiceRegistry; + @Autowired + protected TransformationOptionsConverter converter; + static String PASSWORD = "password"; protected static final String ADMIN = "admin"; @@ -207,6 +210,8 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest renditionDefinitionRegistry2.afterPropertiesSet(); thumbnailRegistry.setTransformServiceRegistry(transformServiceRegistry); + thumbnailRegistry.setLocalTransformServiceRegistry(localTransformServiceRegistry); + thumbnailRegistry.setConverter(converter); } @After diff --git a/src/test/java/org/alfresco/repo/rendition2/LocalSynchronousTransformClientIntegrationTest.java b/src/test/java/org/alfresco/repo/rendition2/LocalSynchronousTransformClientIntegrationTest.java index 68bd3eb536..a5cde83a8f 100644 --- a/src/test/java/org/alfresco/repo/rendition2/LocalSynchronousTransformClientIntegrationTest.java +++ b/src/test/java/org/alfresco/repo/rendition2/LocalSynchronousTransformClientIntegrationTest.java @@ -25,7 +25,9 @@ */ package org.alfresco.repo.rendition2; +import org.alfresco.model.ContentModel; import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; @@ -35,7 +37,13 @@ import org.junit.BeforeClass; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import static org.alfresco.model.ContentModel.PROP_CONTENT; @@ -86,6 +94,23 @@ public class LocalSynchronousTransformClientIntegrationTest extends AbstractRend checkTransform("quick.docx", "doclib", true); } + @Test + public void testParallelTransforms() throws Exception + { + Collection> transforms = new ArrayList<>(); + ExecutorService executorService = Executors.newWorkStealingPool(10); + for (int i=0; i<50; i++) + { + Callable callable = () -> + { + checkTransform("quick.txt", "text/plain", Collections.emptyMap(), true); + return null; + }; + transforms.add(callable); + } + executorService.invokeAll(transforms); + } + @Test public void testTransformDocxJpegImgpreview() throws Exception { @@ -136,16 +161,21 @@ public class LocalSynchronousTransformClientIntegrationTest extends AbstractRend { if (expectedToPass) { - NodeRef sourceNode = transactionService.getRetryingTransactionHelper().doInTransaction(() -> - createContentNodeFromQuickFile(testFileName)); - RenditionDefinition2 renditionDefinition = renditionDefinitionRegistry2.getRenditionDefinition(renditionDefinitionName); String targetMimetype = renditionDefinition.getTargetMimetype(); Map actualOptions = renditionDefinition.getTransformOptions(); - synchronousTransformClient.isSupported(sourceNode, targetMimetype, actualOptions, null, nodeService); + checkTransform(testFileName, targetMimetype, actualOptions, expectedToPass); + } + } + private void checkTransform(String testFileName, String targetMimetype, Map actualOptions, boolean expectedToPass) + { + if (expectedToPass) + { + NodeRef sourceNode = transactionService.getRetryingTransactionHelper().doInTransaction(() -> + createContentNodeFromQuickFile(testFileName)); ContentReader reader = contentService.getReader(sourceNode, PROP_CONTENT); ContentWriter writer = contentService.getTempWriter(); writer.setMimetype(targetMimetype); @@ -154,7 +184,7 @@ public class LocalSynchronousTransformClientIntegrationTest extends AbstractRend ContentReader transformReader = writer.getReader(); String content = transformReader == null ? null : transformReader.getContentString(); content = content == null || content.isEmpty() ? null : content; - assertNotNull("The synchronous transform similar to "+renditionDefinitionName+" resulted in no content", content); + assertNotNull("The synchronous transform resulted in no content", content); } } } diff --git a/src/test/java/org/alfresco/repo/rendition2/LocalTransformServiceRegistryIntegrationTest.java b/src/test/java/org/alfresco/repo/rendition2/LocalTransformServiceRegistryIntegrationTest.java index c858058a23..f70b02d6d5 100644 --- a/src/test/java/org/alfresco/repo/rendition2/LocalTransformServiceRegistryIntegrationTest.java +++ b/src/test/java/org/alfresco/repo/rendition2/LocalTransformServiceRegistryIntegrationTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2018 Alfresco Software Limited + * Copyright (C) 2005 - 2019 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -27,14 +27,11 @@ package org.alfresco.repo.rendition2; import org.alfresco.repo.content.transform.LocalTransformServiceRegistry; import org.alfresco.transform.client.registry.TransformServiceRegistry; -import org.alfresco.transform.client.registry.TransformServiceRegistryImpl; -import org.junit.After; import org.junit.AfterClass; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; -import org.quartz.CronExpression; import org.springframework.beans.factory.annotation.Autowired; import java.util.HashMap; diff --git a/src/test/java/org/alfresco/repo/rendition2/RenditionDefinitionTest.java b/src/test/java/org/alfresco/repo/rendition2/RenditionDefinitionTest.java index 96883f007e..c79eea9138 100644 --- a/src/test/java/org/alfresco/repo/rendition2/RenditionDefinitionTest.java +++ b/src/test/java/org/alfresco/repo/rendition2/RenditionDefinitionTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2018 Alfresco Software Limited + * Copyright (C) 2005 - 2019 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -118,6 +118,7 @@ public class RenditionDefinitionTest extends BaseSpringTest RenditionDefinition2 definition2 = renditionDefinitionRegistry2.getRenditionDefinition(renditionName); Map options = definition2.getTransformOptions(); TransformationOptions transformationOptions2 = transformationOptionsConverter.getTransformationOptions(renditionName, options); + Map options2 = transformationOptionsConverter.getOptions(transformationOptions2); transformationOptions2.setUse(null); // The use is not set in the original until much later // The original pdf and webpreview thumbnails are wrong, as they don't include the 'limits' and in the @@ -127,6 +128,8 @@ public class RenditionDefinitionTest extends BaseSpringTest { assertEquals("The TransformationOptions used in transforms for " + renditionName + " should be the same", transformationOptions.toStringAll(), transformationOptions2.toStringAll()); + assertEquals("The transformationOptionsConverter back to the newer format was not the same for " + + renditionName, options, options2); } else { diff --git a/src/test/java/org/alfresco/repo/rule/RuleServiceCoverageTest.java b/src/test/java/org/alfresco/repo/rule/RuleServiceCoverageTest.java index f7d913e081..aa038af72e 100644 --- a/src/test/java/org/alfresco/repo/rule/RuleServiceCoverageTest.java +++ b/src/test/java/org/alfresco/repo/rule/RuleServiceCoverageTest.java @@ -1,28 +1,28 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ package org.alfresco.repo.rule; import java.io.File; @@ -1076,14 +1076,6 @@ public class RuleServiceCoverageTest extends TestCase */ public void testTransformAction() throws Throwable { - ContentTransformer transformer = transformerRegistry.getTransformer( - MimetypeMap.MIMETYPE_EXCEL, -1, - MimetypeMap.MIMETYPE_TEXT_PLAIN, - new TransformationOptions()); - if (transformer == null) - { - return; - } Map params = new HashMap(1); params.put(TransformActionExecuter.PARAM_MIME_TYPE, MimetypeMap.MIMETYPE_TEXT_PLAIN); params.put(TransformActionExecuter.PARAM_DESTINATION_FOLDER, this.rootNodeRef); @@ -1157,14 +1149,6 @@ public class RuleServiceCoverageTest extends TestCase */ public void testImageTransformAction() throws Throwable { - ContentTransformer transformer = transformerRegistry.getTransformer( - MimetypeMap.MIMETYPE_IMAGE_GIF, -1, - MimetypeMap.MIMETYPE_IMAGE_JPEG, - new TransformationOptions()); - if (transformer == null) - { - return; - } Map params = new HashMap(1); params.put(ImageTransformActionExecuter.PARAM_DESTINATION_FOLDER, this.rootNodeRef); params.put(ImageTransformActionExecuter.PARAM_ASSOC_TYPE_QNAME, ContentModel.ASSOC_CHILDREN); @@ -1993,59 +1977,56 @@ public class RuleServiceCoverageTest extends TestCase public void testAsyncExecutionWithPotentialLoop() { - if (this.transformerRegistry.getTransformer(MimetypeMap.MIMETYPE_EXCEL, -1, MimetypeMap.MIMETYPE_TEXT_PLAIN, new TransformationOptions()) != null) + try { - try - { - Map params = new HashMap(1); - params.put(TransformActionExecuter.PARAM_MIME_TYPE, MimetypeMap.MIMETYPE_TEXT_PLAIN); - params.put(TransformActionExecuter.PARAM_DESTINATION_FOLDER, this.nodeRef); - params.put(TransformActionExecuter.PARAM_ASSOC_TYPE_QNAME, ContentModel.ASSOC_CONTAINS); - params.put(TransformActionExecuter.PARAM_ASSOC_QNAME, QName.createQName(TEST_NAMESPACE, "transformed")); - - Rule rule = createRule( - RuleType.INBOUND, - TransformActionExecuter.NAME, - params, - NoConditionEvaluator.NAME, - null); - rule.setExecuteAsynchronously(true); - rule.setTitle("Transform document to text"); - - UserTransaction tx0 = transactionService.getUserTransaction(); - tx0.begin(); - this.ruleService.saveRule(this.nodeRef, rule); - tx0.commit(); - - UserTransaction tx = transactionService.getUserTransaction(); - tx.begin(); - - Map props =new HashMap(1); - props.put(ContentModel.PROP_NAME, "test.xls"); - - // Create the node at the root - NodeRef newNodeRef = this.nodeService.createNode( - this.nodeRef, - ContentModel.ASSOC_CHILDREN, - QName.createQName(TEST_NAMESPACE, "origional"), - ContentModel.TYPE_CONTENT, - props).getChildRef(); - - // Set some content on the origional - ContentWriter contentWriter = this.contentService.getWriter(newNodeRef, ContentModel.PROP_CONTENT, true); - contentWriter.setMimetype(MimetypeMap.MIMETYPE_EXCEL); - File testFile = AbstractContentTransformerTest.loadQuickTestFile("xls"); - contentWriter.putContent(testFile); - - tx.commit(); - - // Sleep to ensure work is done b4 execution is canceled - Thread.sleep(10000); - } - catch (Exception exception) - { - throw new RuntimeException(exception); - } + Map params = new HashMap(1); + params.put(TransformActionExecuter.PARAM_MIME_TYPE, MimetypeMap.MIMETYPE_TEXT_PLAIN); + params.put(TransformActionExecuter.PARAM_DESTINATION_FOLDER, this.nodeRef); + params.put(TransformActionExecuter.PARAM_ASSOC_TYPE_QNAME, ContentModel.ASSOC_CONTAINS); + params.put(TransformActionExecuter.PARAM_ASSOC_QNAME, QName.createQName(TEST_NAMESPACE, "transformed")); + + Rule rule = createRule( + RuleType.INBOUND, + TransformActionExecuter.NAME, + params, + NoConditionEvaluator.NAME, + null); + rule.setExecuteAsynchronously(true); + rule.setTitle("Transform document to text"); + + UserTransaction tx0 = transactionService.getUserTransaction(); + tx0.begin(); + this.ruleService.saveRule(this.nodeRef, rule); + tx0.commit(); + + UserTransaction tx = transactionService.getUserTransaction(); + tx.begin(); + + Map props =new HashMap(1); + props.put(ContentModel.PROP_NAME, "test.xls"); + + // Create the node at the root + NodeRef newNodeRef = this.nodeService.createNode( + this.nodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName(TEST_NAMESPACE, "origional"), + ContentModel.TYPE_CONTENT, + props).getChildRef(); + + // Set some content on the origional + ContentWriter contentWriter = this.contentService.getWriter(newNodeRef, ContentModel.PROP_CONTENT, true); + contentWriter.setMimetype(MimetypeMap.MIMETYPE_EXCEL); + File testFile = AbstractContentTransformerTest.loadQuickTestFile("xls"); + contentWriter.putContent(testFile); + + tx.commit(); + + // Sleep to ensure work is done b4 execution is canceled + Thread.sleep(10000); + } + catch (Exception exception) + { + throw new RuntimeException(exception); } } } diff --git a/src/test/java/org/alfresco/repo/thumbnail/ThumbnailServiceImplTest.java b/src/test/java/org/alfresco/repo/thumbnail/ThumbnailServiceImplTest.java index da4b2759d4..8ce6cd2eea 100644 --- a/src/test/java/org/alfresco/repo/thumbnail/ThumbnailServiceImplTest.java +++ b/src/test/java/org/alfresco/repo/thumbnail/ThumbnailServiceImplTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2019 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -26,22 +26,11 @@ package org.alfresco.repo.thumbnail; -import java.io.File; -import java.io.IOException; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - import org.alfresco.model.ContentModel; import org.alfresco.model.RenditionModel; import org.alfresco.repo.action.evaluator.NoConditionEvaluator; import org.alfresco.repo.action.executer.AddFeaturesActionExecuter; import org.alfresco.repo.content.MimetypeMap; -import org.alfresco.repo.content.transform.AbstractContentTransformer2; import org.alfresco.repo.content.transform.AbstractContentTransformerTest; import org.alfresco.repo.content.transform.ContentTransformer; import org.alfresco.repo.content.transform.magick.ImageResizeOptions; @@ -51,6 +40,8 @@ import org.alfresco.repo.domain.dialect.Oracle9Dialect; import org.alfresco.repo.domain.dialect.SQLServerDialect; import org.alfresco.repo.jscript.ClasspathScriptLocation; import org.alfresco.repo.model.Repository; +import org.alfresco.repo.rendition2.SynchronousTransformClient; +import org.alfresco.repo.rendition2.TransformationOptionsConverter; import org.alfresco.repo.thumbnail.script.ScriptThumbnailService; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.RetryingTransactionHelper; @@ -64,7 +55,6 @@ import org.alfresco.service.cmr.lock.LockType; 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.ContentIOException; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentServiceTransientException; import org.alfresco.service.cmr.repository.ContentWriter; @@ -88,8 +78,10 @@ import org.alfresco.service.namespace.QNamePattern; import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.service.transaction.TransactionService; import org.alfresco.test_category.OwnJVMTestsCategory; +import org.alfresco.transform.client.registry.TransformServiceRegistry; import org.alfresco.util.BaseAlfrescoSpringTest; import org.alfresco.util.GUID; +import org.alfresco.util.Pair; import org.alfresco.util.TempFileProvider; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -105,6 +97,16 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.transaction.TestTransaction; import org.springframework.transaction.annotation.Transactional; +import java.io.File; +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + /** * Thumbnail service implementation unit test * @@ -135,6 +137,8 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest private PermissionService permissionService; private LockService lockService; private CopyService copyService; + private SynchronousTransformClient synchronousTransformClient; + private TransformationOptionsConverter converter; private NodeRef folder; private static final String TEST_FAILING_MIME_TYPE = "application/vnd.alfresco.test.transientfailure"; @@ -161,6 +165,8 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest this.permissionService = (PermissionService) applicationContext.getBean("PermissionService"); this.lockService = (LockService) applicationContext.getBean("lockService"); this.copyService = (CopyService) applicationContext.getBean("CopyService"); + synchronousTransformClient = (SynchronousTransformClient) applicationContext.getBean("synchronousTransformClient"); + converter = (TransformationOptionsConverter) applicationContext.getBean("transformOptionsConverter"); // Create a folder and some content Map folderProps = new HashMap(1); @@ -172,13 +178,8 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest private void checkTransformer() { - ContentTransformer transformer = this.contentService.getImageTransformer(); - assertNotNull("No transformer returned for 'getImageTransformer'", transformer); - - // Check that it is working - ImageTransformationOptions imageTransformationOptions = new ImageTransformationOptions(); - if (!transformer.isTransformable(MimetypeMap.MIMETYPE_IMAGE_JPEG, -1, MimetypeMap.MIMETYPE_IMAGE_JPEG, - imageTransformationOptions)) + if (!synchronousTransformClient.isSupported(MimetypeMap.MIMETYPE_IMAGE_JPEG, -1, null, + MimetypeMap.MIMETYPE_IMAGE_JPEG, Collections.emptyMap(), null, null)) { fail("Image transformer is not working. Please check your image conversion command setup."); } @@ -551,10 +552,17 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest final NodeRef testNode = this.secureNodeService.createNode(folder, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "transientThumbnail.transientThumbnail"), ContentModel.TYPE_CONTENT, props).getChildRef(); - + + // Modified test to add content. Having no content was failing to find a transformer with Legacy, but now + // does not with both Legacy and Local transforms. As a result the test was passing for the wrong reason. secureNodeService.setProperty(testNode, ContentModel.PROP_CONTENT, new ContentData(null, TEST_FAILING_MIME_TYPE, 0L, null)); - // We don't need to write any content into this node, as our test transformer will fail immediately. + File testFile = AbstractContentTransformerTest.loadNamedQuickTestFile("quick.pdf"); + assertNotNull("Failed to load required test file.", testFile); + ContentWriter writer = contentService.getWriter(testNode, ContentModel.PROP_CONTENT, true); + writer.setMimetype(TEST_FAILING_MIME_TYPE); + writer.setEncoding("UTF-8"); + writer.putContent(testFile); logger.debug("Running failing thumbnail on " + testNode); @@ -1118,10 +1126,12 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest { NodeRef nodeRef = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_HTML); ThumbnailDefinition def = this.thumbnailService.getThumbnailRegistry().getThumbnailDefinition("medium"); - - ContentTransformer transformer = this.contentService.getTransformer(null, MimetypeMap.MIMETYPE_HTML, -1, def - .getMimetype(), def.getTransformationOptions()); - if (transformer != null) + TransformationOptions transformationOptions = def.getTransformationOptions(); + Map options = converter.getOptions(transformationOptions); + String targetMimetype = def.getMimetype(); + boolean supported = synchronousTransformClient.isSupported(MimetypeMap.MIMETYPE_HTML, -1, null, + targetMimetype, options, null, null); + if (supported) { NodeRef thumb = this.thumbnailService.createThumbnail(nodeRef, ContentModel.PROP_CONTENT, def.getMimetype(), def.getTransformationOptions(), def.getName()); @@ -1133,7 +1143,7 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest } def = this.thumbnailService.getThumbnailRegistry().getThumbnailDefinition("webpreview"); - if (transformer != null) + if (supported) { NodeRef thumb = this.thumbnailService.createThumbnail(nodeRef, ContentModel.PROP_CONTENT, def.getMimetype(), def.getTransformationOptions(), def.getName()); @@ -1183,27 +1193,8 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest checkRendition("smallPng", thumbnail1); outputThumbnailTempContentLocation(thumbnail1, "png", "smallPng - 64x64, marked as thumbnail"); - // Create thumbnail - different content property - // TODO - - // Create thumbnail - different command options - // We'll pass illegal command options to ImageMagick in order to trigger an exception - Exception x = null; - try - { - imageTransformationOptions.setCommandOptions("-noSuchOption"); - thumbnail1 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, - MimetypeMap.MIMETYPE_IMAGE_PNG, imageTransformationOptions, "smallCO"); - } catch (ContentIOException ciox) - { - x = ciox; - ciox.printStackTrace(); - } - assertNotNull("Expected exception from ImageMagick due to invalid option", x); - // Reset the command options - imageTransformationOptions.setCommandOptions(""); - - +// Removd code: We now automatically discard all extra command options for security reasons. + // Create thumbnail - different target assoc details ThumbnailParentAssociationDetails tpad = new ThumbnailParentAssociationDetails(otherFolder, @@ -1224,7 +1215,7 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest MimetypeMap.MIMETYPE_IMAGE_PNG, imageTransformationOptions, null); assertNotNull(thumbnail1); checkRenditioned(jpgOrig, - Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, null, 5))); + Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, null, 4))); checkRendition(null, thumbnail1); outputThumbnailTempContentLocation(thumbnail1, "png", "'null' - 64x64, marked as thumbnail"); } @@ -1304,11 +1295,6 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest try { - // Reset our transformer count for each test - LongRunningTransformer transformer = (LongRunningTransformer) contentService - .getTransformer(TEST_LONG_RUNNING_MIME_TYPE, MimetypeMap.MIMETYPE_IMAGE_JPEG); - transformer.setTransformCount(0); - Map props = new HashMap(1); props.put(ContentModel.PROP_NAME, "original.test"); final NodeRef source = secureNodeService.createNode(folder, ContentModel.ASSOC_CONTAINS, @@ -1500,68 +1486,90 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest performLongRunningThumbnailTest(expectedThumbnails, expectedAssocs, new EmptyLongRunningConcurrentWork(), 1, 1); } - - /** - * Test transformer. - * - * @since 4.0.1 - */ - private static class TransientFailTransformer extends AbstractContentTransformer2 + + public static class TestTransformServiceRegistry implements TransformServiceRegistry { - public boolean isTransformable(String sourceMimetype, String targetMimetype, TransformationOptions options) + private TransformServiceRegistry delegate; + + public TestTransformServiceRegistry(TransformServiceRegistry delegate) { - return sourceMimetype.equals(MimetypeMap.MIMETYPE_PDF) && targetMimetype.equals(TEST_FAILING_MIME_TYPE); + this.delegate = delegate; } - - protected void transformInternal(ContentReader reader, ContentWriter writer, TransformationOptions options) throws Exception + + @Override + public boolean isSupported(String sourceMimetype, long sourceSizeInBytes, String targetMimetype, + Map actualOptions, String transformName) { - // fail every time. - throw new ContentServiceTransientException("Transformation intentionally failed for test purposes."); + return sourceMimetype.equals(TEST_FAILING_MIME_TYPE) || sourceMimetype.equals(TEST_LONG_RUNNING_MIME_TYPE) + ? true + : delegate.isSupported(sourceMimetype, sourceSizeInBytes, targetMimetype, actualOptions, transformName); + } + + @Override + public long findMaxSize(String sourceMimetype, String targetMimetype, Map actualOptions, String transformName) + { + return sourceMimetype.equals(TEST_FAILING_MIME_TYPE) || sourceMimetype.equals(TEST_LONG_RUNNING_MIME_TYPE) + ? -1 + : delegate.findMaxSize(sourceMimetype, targetMimetype, actualOptions, transformName); + } + + @Override + public String findTransformerName(String sourceMimetype, long sourceSizeInBytes, + String targetMimetype, Map actualOptions, String renditionName) + { + throw new UnsupportedOperationException("not implemented"); } } - - /** - * Bogus transformer that simulates a somewhat longer running transformation - */ - private static class LongRunningTransformer extends AbstractContentTransformer2 + + public static class TestSynchronousTransformClient implements SynchronousTransformClient { - private int transformCount = 0; + private SynchronousTransformClient delegate; - @Override - public void register() + public TestSynchronousTransformClient(SynchronousTransformClient delegate) { - super.register(); - setStrictMimeTypeCheck(false); - } - - public void setTransformCount(int transformCount) - { - this.transformCount = transformCount; - } - - public int getTransformCount() - { - return transformCount; - } - - @Override - public boolean isTransformable(String sourceMimetype, String targetMimetype, TransformationOptions options) - { - return sourceMimetype.equals(TEST_LONG_RUNNING_MIME_TYPE) && - (targetMimetype.equals(MimetypeMap.MIMETYPE_IMAGE_JPEG) || - targetMimetype.equals(MimetypeMap.MIMETYPE_IMAGE_PNG)); + this.delegate = delegate; } @Override - protected void transformInternal(ContentReader reader, ContentWriter writer, TransformationOptions options) - throws Exception + public boolean isSupported(String sourceMimetype, long sourceSizeInBytes, String contentUrl, String targetMimetype, + Map actualOptions, String transformName, NodeRef sourceNodeRef) { - Thread.sleep(TEST_LONG_RUNNING_TRANSFORM_TIME); - writer.putContent("SUCCESS"); - transformCount++; + boolean supported = true; + if (!sourceMimetype.equals(TEST_FAILING_MIME_TYPE) && !sourceMimetype.equals(TEST_LONG_RUNNING_MIME_TYPE)) + { + supported = delegate.isSupported(sourceMimetype, sourceSizeInBytes, contentUrl, targetMimetype, actualOptions, + transformName, sourceNodeRef); + } + return supported; + } + + @Override + public void transform(ContentReader reader, ContentWriter writer, Map actualOptions, String transformName, NodeRef sourceNodeRef) + { + String sourceMimetype = reader.getMimetype(); + if (sourceMimetype.equals(TEST_FAILING_MIME_TYPE)) + { + throw new ContentServiceTransientException("Transformation intentionally failed for test purposes."); + } + else if (sourceMimetype.equals(TEST_LONG_RUNNING_MIME_TYPE)) + { + try + { + Thread.sleep(TEST_LONG_RUNNING_TRANSFORM_TIME); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + writer.putContent("SUCCESS"); + } + else + { + delegate.transform(reader, writer, actualOptions, transformName, sourceNodeRef); + } } } - + /** * Defines the work to be done while long running transformations are being performed * and the means to verify that work completed successfully. diff --git a/src/test/java/org/alfresco/repo/version/ContentServiceImplTest.java b/src/test/java/org/alfresco/repo/version/ContentServiceImplTest.java index 6981801fba..6bf24565f6 100644 --- a/src/test/java/org/alfresco/repo/version/ContentServiceImplTest.java +++ b/src/test/java/org/alfresco/repo/version/ContentServiceImplTest.java @@ -30,8 +30,6 @@ import org.alfresco.model.ContentModel; import org.alfresco.repo.content.EmptyContentReader; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.MimetypeMapTest; -import org.alfresco.repo.content.transform.ContentTransformer; -import org.alfresco.service.cmr.repository.ContentIOException; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentWriter; @@ -44,7 +42,6 @@ import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; -import java.util.HashMap; import org.springframework.transaction.annotation.Transactional; /** @@ -114,12 +111,6 @@ public class ContentServiceImplTest extends BaseVersionStoreTest ContentReader contentReader = this.contentService.getReader(versionableNode, ContentModel.PROP_CONTENT); ContentWriter contentWriter = this.contentService.getWriter(versionableNode, ContentModel.PROP_CONTENT, false); - //Call deprecated methods - assertTrue(this.contentService.isTransformable(contentReader, contentWriter)); - this.contentService.transform(contentReader, contentWriter, new HashMap()); - assertNotNull(this.contentService.getActiveTransformers(contentReader.getMimetype(), contentWriter.getMimetype(), new TransformationOptions())); - assertNull(this.contentService.getTransformer(MimetypeMap.MIMETYPE_TEXT_PLAIN, MimetypeMap.MIMETYPE_IMAGE_PNG, new TransformationOptions())); - // this.nodeService.setProperty(versionableNode, ContentModel.PROP_NAME, "for debugTransformers.txt"); try { @@ -133,7 +124,7 @@ public class ContentServiceImplTest extends BaseVersionStoreTest try { - this.contentService.transform(null, null); + this.contentService.transform(null, null, new TransformationOptions()); fail("Should throw exception"); } catch (AlfrescoRuntimeException are) @@ -144,17 +135,7 @@ public class ContentServiceImplTest extends BaseVersionStoreTest ContentReader empty = new EmptyContentReader("empty.txt"); try { - this.contentService.transform(empty, null); - fail("Should throw exception"); - } - catch (AlfrescoRuntimeException are) - { - are.getMessage().contains("The content reader mimetype must be set"); - } - - try - { - this.contentService.isTransformable(empty, null, new TransformationOptions()); + this.contentService.transform(empty, null, new TransformationOptions()); fail("Should throw exception"); } catch (AlfrescoRuntimeException are) @@ -165,18 +146,7 @@ public class ContentServiceImplTest extends BaseVersionStoreTest try { contentWriter.setMimetype(null); - this.contentService.transform(contentReader, contentWriter); - fail("Should throw exception"); - } - catch (AlfrescoRuntimeException are) - { - are.getMessage().contains("The content writer mimetype must be set"); - } - - try - { - contentWriter.setMimetype(null); - this.contentService.isTransformable(contentReader, contentWriter, new TransformationOptions()); + this.contentService.transform(contentReader, contentWriter, new TransformationOptions()); fail("Should throw exception"); } catch (AlfrescoRuntimeException are) diff --git a/src/test/java/org/alfresco/transform/client/registry/LocalTransformServiceRegistryConfigTest.java b/src/test/java/org/alfresco/transform/client/registry/LocalTransformServiceRegistryConfigTest.java index b014111a1a..ee5c53d135 100644 --- a/src/test/java/org/alfresco/transform/client/registry/LocalTransformServiceRegistryConfigTest.java +++ b/src/test/java/org/alfresco/transform/client/registry/LocalTransformServiceRegistryConfigTest.java @@ -29,13 +29,11 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.transform.AbstractLocalTransform; import org.alfresco.repo.content.transform.LocalPipelineTransform; -import org.alfresco.repo.content.transform.LocalTransform; import org.alfresco.repo.content.transform.LocalTransformServiceRegistry; import org.alfresco.repo.content.transform.TransformerDebug; import org.alfresco.transform.client.model.config.SupportedSourceAndTarget; import org.alfresco.transform.client.model.config.TransformOption; import org.alfresco.transform.client.model.config.Transformer; -import org.apache.camel.processor.Pipeline; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.Before; diff --git a/src/test/resources/alfresco-global.properties b/src/test/resources/alfresco-global.properties index 4cd14982c1..ea7ea2d5ad 100644 --- a/src/test/resources/alfresco-global.properties +++ b/src/test/resources/alfresco-global.properties @@ -47,5 +47,8 @@ identity-service.client-socket-timeout=1000 identity-service.client-connection-timeout=3000 identity-service.authentication.enable-username-password-authentication=false -# In the past so it data is read straight away rather than being scheduled for the first time in a few milliseconds +# Use a date in the past, so data is read straight away rather than being scheduled in tests. A few ms is too late. mimetype.config.cronExpression=0 0 0 ? JAN * 1970 +rendition.config.cronExpression=0 0 0 ? JAN * 1970 +local.transform.service.cronExpression=0 0 0 ? JAN * 1970 +transform.service.cronExpression=0 0 0 ? JAN * 1970 \ No newline at end of file diff --git a/src/test/resources/org/alfresco/repo/thumbnail/test-thumbnail-context.xml b/src/test/resources/org/alfresco/repo/thumbnail/test-thumbnail-context.xml index e65ff7bd4a..0571cf9ad4 100644 --- a/src/test/resources/org/alfresco/repo/thumbnail/test-thumbnail-context.xml +++ b/src/test/resources/org/alfresco/repo/thumbnail/test-thumbnail-context.xml @@ -1,13 +1,16 @@ - - - + + + + - + + + + \ No newline at end of file diff --git a/src/test/resources/test/alfresco/test-renditions-context.xml b/src/test/resources/test/alfresco/test-renditions-context.xml index 0c717a0dcd..0f83e5eb4a 100644 --- a/src/test/resources/test/alfresco/test-renditions-context.xml +++ b/src/test/resources/test/alfresco/test-renditions-context.xml @@ -5,7 +5,21 @@ + parent="baseTransformationRenderingEngine"> + + + + + + + + + + + + + + \ No newline at end of file