diff --git a/src/main/java/org/alfresco/repo/action/executer/TransformActionExecuter.java b/src/main/java/org/alfresco/repo/action/executer/TransformActionExecuter.java index feb736b10b..786967daf3 100644 --- a/src/main/java/org/alfresco/repo/action/executer/TransformActionExecuter.java +++ b/src/main/java/org/alfresco/repo/action/executer/TransformActionExecuter.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.action.executer; import java.util.List; @@ -59,6 +59,7 @@ import org.apache.commons.logging.LogFactory; * * @author Roy Wetherall */ +@Deprecated public class TransformActionExecuter extends ActionExecuterAbstractBase { /* Error messages */ diff --git a/src/main/java/org/alfresco/repo/content/transform/AbstractLocalTransformer.java b/src/main/java/org/alfresco/repo/content/transform/AbstractLocalTransformer.java new file mode 100644 index 0000000000..bf99df3ed7 --- /dev/null +++ b/src/main/java/org/alfresco/repo/content/transform/AbstractLocalTransformer.java @@ -0,0 +1,259 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 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.transform; + +import org.alfresco.error.AlfrescoRuntimeException; +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.NodeRef; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.Map; +import java.util.Set; + +/** + * Abstract supper class for local transformer using flat transform options. + */ +public abstract class AbstractLocalTransformer implements LocalTransformer +{ + protected static final Log log = LogFactory.getLog(LocalTransformer.class); + + protected final String name; + protected final MimetypeService mimetypeService; + protected final TransformerDebug transformerDebug; + + private final LocalTransformServiceRegistry localTransformServiceRegistry; + private final boolean strictMimeTypeCheck; + private final Map> strictMimetypeExceptions; + private final boolean retryTransformOnDifferentMimeType; + private final static ThreadLocal depth = ThreadLocal.withInitial(()->0); + + AbstractLocalTransformer(String name, TransformerDebug transformerDebug, + MimetypeService mimetypeService, boolean strictMimeTypeCheck, + Map> strictMimetypeExceptions, boolean retryTransformOnDifferentMimeType, + LocalTransformServiceRegistry localTransformServiceRegistry) + { + this.name = name; + this.transformerDebug = transformerDebug; + this.mimetypeService = mimetypeService; + this.strictMimeTypeCheck = strictMimeTypeCheck; + this.strictMimetypeExceptions = strictMimetypeExceptions; + this.retryTransformOnDifferentMimeType = retryTransformOnDifferentMimeType; + this.localTransformServiceRegistry = localTransformServiceRegistry; + } + + public abstract boolean isAvailable(); + + protected abstract void transformImpl(ContentReader reader, + ContentWriter writer, Map transformOptions, + String sourceMimetype, String targetMimetype, + String sourceExtension, String targetExtension, + String targetEncoding, String renditionName, NodeRef sourceNodeRef) + throws Exception; + + @Override + public void transform(ContentReader reader, ContentWriter writer, Map transformOptions, + String renditionName, NodeRef sourceNodeRef) + throws Exception + { + if (isAvailable()) + { + String sourceMimetype = reader.getMimetype(); + String targetMimetype = writer.getMimetype(); + String targetEncoding = writer.getEncoding(); + + String sourceExtension = mimetypeService.getExtension(sourceMimetype); + String targetExtension = mimetypeService.getExtension(targetMimetype); + if (sourceExtension == null || targetExtension == null) + { + throw new AlfrescoRuntimeException("Unknown extensions for mimetypes: \n" + + " source mimetype: " + sourceMimetype + "\n" + + " source extension: " + sourceExtension + "\n" + + " target mimetype: " + targetMimetype + "\n" + + " target extension: " + targetExtension + "\n" + + " target encoding: " + targetEncoding); + } + + transformWithDebug(reader, writer, transformOptions, renditionName, sourceNodeRef, sourceMimetype, + targetMimetype, targetEncoding, sourceExtension, targetExtension); + + if (log.isDebugEnabled()) + { + log.debug("Local transformation completed: \n" + + " source: " + reader + "\n" + + " target: " + writer + "\n" + + " options: " + transformOptions); + } + } + else + { + log.debug("Local transformer not available: \n" + + " source: " + reader + "\n" + + " target: " + writer + "\n" + + " options: " + transformOptions); + } + } + + private void transformWithDebug(ContentReader reader, ContentWriter writer, Map transformOptions, + String renditionName, NodeRef sourceNodeRef, String sourceMimetype, String targetMimetype, + String targetEncoding, String sourceExtension, String targetExtension) throws Exception + { + + try + { + depth.set(depth.get()+1); + + if (transformerDebug.isEnabled()) + { + transformerDebug.pushTransform(name, reader.getContentUrl(), sourceMimetype, + targetMimetype, reader.getSize(), renditionName, sourceNodeRef); + } + + strictMimetypeCheck(reader, sourceNodeRef, sourceMimetype); + transformImpl(reader, writer, transformOptions, sourceMimetype, + targetMimetype, sourceExtension, targetExtension, targetEncoding, renditionName, sourceNodeRef); + } + catch (Throwable e) + { + retryWithDifferentMimetype(reader, writer, targetMimetype, transformOptions, renditionName, sourceNodeRef, e); + } + finally + { + transformerDebug.popTransform(); + depth.set(depth.get()-1); + } + } + + private void strictMimetypeCheck(ContentReader reader, NodeRef sourceNodeRef, String declaredMimetype) + throws UnsupportedTransformationException + { + if (mimetypeService != null && strictMimeTypeCheck && depth.get() == 1) + { + String detectedMimetype = mimetypeService.getMimetypeIfNotMatches(reader.getReader()); + + if (!strictMimetypeCheck(declaredMimetype, detectedMimetype)) + { + Set allowedMimetypes = strictMimetypeExceptions.get(declaredMimetype); + if (allowedMimetypes != null && allowedMimetypes.contains(detectedMimetype)) + { + String fileName = transformerDebug.getFileName(sourceNodeRef, true, 0); + String readerSourceMimetype = reader.getMimetype(); + String message = "Transformation of ("+fileName+ + ") has not taken place because the declared mimetype ("+ + readerSourceMimetype+") does not match the detected mimetype ("+ + detectedMimetype+")."; + log.warn(message); + throw new UnsupportedTransformationException(message); + } + } + } + } + + /** + * When strict mimetype checking is performed before a transformation, this method is called. + * There are a few issues with the Tika mimetype detection. As a result we still allow some + * transformations to take place even if there is a discrepancy between the detected and + * declared mimetypes. + * @param declaredMimetype the mimetype on the source node + * @param detectedMimetype returned by Tika having looked at the content. + * @return true if the transformation should take place. This includes the case where the + * detectedMimetype is null (returned by Tika when the mimetypes are the same), or + * the supplied pair of mimetypes have been added to the + * {@code}transformer.strict.mimetype.check.whitelist{@code}. + */ + private boolean strictMimetypeCheck(String declaredMimetype, String detectedMimetype) + { + if (detectedMimetype == null) + { + return true; + } + + Set detectedMimetypes = strictMimetypeExceptions.get(declaredMimetype); + return detectedMimetypes != null && detectedMimetypes.contains(detectedMimetype); + } + + private void retryWithDifferentMimetype(ContentReader reader, ContentWriter writer, String targetMimetype, + Map transformOptions, String renditionName, + NodeRef sourceNodeRef, Throwable e) throws Exception + { + if (mimetypeService != null && localTransformServiceRegistry != null) + { + String differentType = mimetypeService.getMimetypeIfNotMatches(reader.getReader()); + if (differentType == null) + { + transformerDebug.debug(" Failed", e); + throw new ContentIOException("Content conversion failed: \n" + + " reader: " + reader + "\n" + + " writer: " + writer + "\n" + + " options: " + transformOptions, + e); + } + else + { + transformerDebug.debug(" Failed: Mime type was '" + differentType + "'", e); + String claimedMimetype = reader.getMimetype(); + + if (retryTransformOnDifferentMimeType) + { + reader = reader.getReader(); + reader.setMimetype(differentType); + long sourceSizeInBytes = reader.getSize(); + + LocalTransformer localTransformer = localTransformServiceRegistry.getLocalTransformer( + transformOptions, renditionName, differentType, targetMimetype, sourceSizeInBytes); + if (localTransformer == null) + { + transformerDebug.debug(" Failed", e); + throw new ContentIOException("Content conversion failed: \n" + + " reader: " + reader + "\n" + + " writer: " + writer + "\n" + + " options: " + transformOptions + "\n" + + " claimed mime type: " + claimedMimetype + "\n" + + " detected mime type: " + differentType + "\n" + + " transformer not found" + "\n", + e + ); + } + localTransformer.transform(reader, writer, transformOptions, renditionName, sourceNodeRef); + } + else + { + throw new ContentIOException("Content conversion failed: \n" + + " reader: " + reader + "\n" + + " writer: " + writer + "\n" + + " options: " + transformOptions + "\n" + + " claimed mime type: " + claimedMimetype + "\n" + + " detected mime type: " + differentType, + e + ); + } + } + } + } +} diff --git a/src/main/java/org/alfresco/repo/content/transform/AbstractRemoteContentTransformer.java b/src/main/java/org/alfresco/repo/content/transform/AbstractRemoteContentTransformer.java index 066bea2423..0ddc4044bf 100644 --- a/src/main/java/org/alfresco/repo/content/transform/AbstractRemoteContentTransformer.java +++ b/src/main/java/org/alfresco/repo/content/transform/AbstractRemoteContentTransformer.java @@ -42,10 +42,17 @@ import org.apache.commons.logging.Log; @Deprecated public abstract class AbstractRemoteContentTransformer extends AbstractContentTransformer2 { + private boolean enabled = true; + private RemoteTransformerClient remoteTransformerClient; private boolean available = false; + public void setEnabled(boolean enabled) + { + this.enabled = enabled; + } + /** * Sets the optional remote transformer client which will be used in preference to a local command if available. * @@ -75,47 +82,50 @@ public abstract class AbstractRemoteContentTransformer extends AbstractContentTr public void afterPropertiesSet() { - // check availability - if (remoteTransformerClientConfigured()) + if (enabled) { - Log logger = getLogger(); - try + // check availability + if (remoteTransformerClientConfigured()) { - Pair result = remoteTransformerClient.check(logger); - Boolean isAvailable = result.getFirst(); - String msg = result.getSecond() == null ? "" : result.getSecond(); - if (isAvailable != null && isAvailable) + Log logger = getLogger(); + try { - String versionString = msg; - setAvailable(true); - logger.info("Using remote " + getName() + ": " + versionString); - } - else - { - setAvailable(false); - String message = "Remote " + getName() + " is not available for transformations. " + msg; - if (isAvailable == null) + Pair result = remoteTransformerClient.check(logger); + Boolean isAvailable = result.getFirst(); + String msg = result.getSecond() == null ? "" : result.getSecond(); + if (isAvailable != null && isAvailable) { - logger.debug(message); + String versionString = msg; + setAvailable(true); + logger.info("Using legacy local " + getName() + ": " + versionString); } else { - logger.error(message); + setAvailable(false); + String message = "Legacy local " + getName() + " is not available for transformations. " + msg; + if (isAvailable == null) + { + logger.debug(message); + } + else + { + logger.error(message); + } } } + catch (Throwable e) + { + setAvailable(false); + logger.error("Remote " + getName() + " is not available: " + (e.getMessage() != null ? e.getMessage() : "")); + // debug so that we can trace the issue if required + logger.debug(e); + } } - catch (Throwable e) + else { - setAvailable(false); - logger.error("Remote " + getName() + " is not available: " + (e.getMessage() != null ? e.getMessage() : "")); - // debug so that we can trace the issue if required - logger.debug(e); + available = true; } } - else - { - available = true; - } } public boolean isAvailable() diff --git a/src/main/java/org/alfresco/repo/content/transform/ContentTransformerRegistry.java b/src/main/java/org/alfresco/repo/content/transform/ContentTransformerRegistry.java index 731cc7f562..aa8e3036a8 100644 --- a/src/main/java/org/alfresco/repo/content/transform/ContentTransformerRegistry.java +++ b/src/main/java/org/alfresco/repo/content/transform/ContentTransformerRegistry.java @@ -197,7 +197,7 @@ public class ContentTransformerRegistry if (firstTime) { firstTime = false; - transformerDebug.debug("Local legacy transformers are " + (enabled ? "enabled" : "disabled")); + transformerDebug.debug("Legacy transforms are " + (enabled ? "enabled" : "disabled")); } // Get the list of transformers diff --git a/src/main/java/org/alfresco/repo/content/transform/JodContentTransformer.java b/src/main/java/org/alfresco/repo/content/transform/JodContentTransformer.java index 3ec51b5e59..6e8ab1d66c 100644 --- a/src/main/java/org/alfresco/repo/content/transform/JodContentTransformer.java +++ b/src/main/java/org/alfresco/repo/content/transform/JodContentTransformer.java @@ -48,8 +48,15 @@ public class JodContentTransformer extends OOoContentTransformerHelper implement { private static Log logger = LogFactory.getLog(JodContentTransformer.class); + private boolean enabled = true; + private JodConverter jodconverter; + public void setEnabled(boolean enabled) + { + this.enabled = enabled; + } + public void setJodConverter(JodConverter jodc) { this.jodconverter = jodc; @@ -83,26 +90,29 @@ public class JodContentTransformer extends OOoContentTransformerHelper implement @Override public void afterPropertiesSet() { - super.afterPropertiesSet(); - if (remoteTransformerClientConfigured()) + if (enabled) { - Pair result = remoteTransformerClient.check(logger); - Boolean isAvailable = result.getFirst(); - if (isAvailable != null && isAvailable) + super.afterPropertiesSet(); + if (remoteTransformerClientConfigured()) { - String versionString = result.getSecond().trim(); - logger.info("Using remote JodCoverter: "+versionString); - } - else - { - String message = "Remote JodConverter is not available for transformations. " + result.getSecond(); - if (isAvailable == null) + Pair result = remoteTransformerClient.check(logger); + Boolean isAvailable = result.getFirst(); + if (isAvailable != null && isAvailable) { - logger.debug(message); + String versionString = result.getSecond().trim(); + logger.info("Using legacy local JodCoverter: " + versionString); } else { - logger.error(message); + String message = "Legacy local JodConverter is not available for transformations. " + result.getSecond(); + if (isAvailable == null) + { + logger.debug(message); + } + else + { + logger.error(message); + } } } } diff --git a/src/main/java/org/alfresco/repo/content/transform/LocalPipelineTransformer.java b/src/main/java/org/alfresco/repo/content/transform/LocalPipelineTransformer.java new file mode 100644 index 0000000000..ac8c877c23 --- /dev/null +++ b/src/main/java/org/alfresco/repo/content/transform/LocalPipelineTransformer.java @@ -0,0 +1,128 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 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.transform; + +import org.alfresco.repo.content.filestore.FileContentWriter; +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.NodeRef; +import org.alfresco.util.TempFileProvider; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Transformer that passes a document through a pipeline of transformations to arrive at an target mimetype. + * + * Instances are automatically created for transformers identified by alfresco/transform json files and returned from + * T-Engines which are themselves identified by global properties the match the pattern localTransformer.<name>.url. + * The transforms take place in a separate process (typically a Docker container). + */ +public class LocalPipelineTransformer extends AbstractLocalTransformer +{ + private final List transformers = new ArrayList<>(); + + private class IntermediateTransformer + { + LocalTransformer intermediateTransformer; + String targetMimetype; + } + + public LocalPipelineTransformer(String name, TransformerDebug transformerDebug, + MimetypeService mimetypeService, boolean strictMimeTypeCheck, + Map> strictMimetypeExceptions, + boolean retryTransformOnDifferentMimeType, + LocalTransformServiceRegistry localTransformServiceRegistry) + { + super(name, transformerDebug, mimetypeService, strictMimeTypeCheck, strictMimetypeExceptions, + retryTransformOnDifferentMimeType, localTransformServiceRegistry); + } + + @Override + public boolean isAvailable() + { + return true; + } + + public void addIntermediateTransformer(LocalTransformer intermediateTransformer, String targetMimetype) + { + IntermediateTransformer transformer = new IntermediateTransformer(); + transformer.intermediateTransformer = intermediateTransformer; + transformer.targetMimetype = targetMimetype; + transformers.add(transformer); + } + + @Override + protected void transformImpl(ContentReader reader, + ContentWriter writer, Map transformOptions, + String sourceMimetype, String targetMimetype, + String sourceExtension, String targetExtension, + String targetEncoding, String renditionName, NodeRef sourceNodeRef) throws Exception + { + ContentReader currentReader = reader; + int lastI = transformers.size() - 1; + for (int i = 0; i <= lastI; i++) + { + IntermediateTransformer transformer = transformers.get(i); + + ContentWriter currentWriter; + if (i == lastI) + { + currentWriter = writer; + } + else + { + // make a temp file writer with the correct extension + String sourceExt = mimetypeService.getExtension(currentReader.getMimetype()); + String targetExt = mimetypeService.getExtension(transformer.targetMimetype); + File tempFile = TempFileProvider.createTempFile( + "LocalPipelineTransformer_intermediate_" + sourceExt + "_", + "." + targetExt); + currentWriter = new FileContentWriter(tempFile); + currentWriter.setMimetype(transformer.targetMimetype); + } + + transformer.intermediateTransformer.transform(currentReader, currentWriter, transformOptions, renditionName, sourceNodeRef); + + // Clear the sourceNodeRef after the first transformation to avoid later transformers thinking the + // intermediate file is the original node. + if (i == 0) + { + sourceNodeRef = null; + } + + // Pass the output to the next transformer + if (i < lastI) + { + currentReader = currentWriter.getReader(); + } + } + } +} diff --git a/src/main/java/org/alfresco/repo/content/transform/LocalTransformServiceRegistry.java b/src/main/java/org/alfresco/repo/content/transform/LocalTransformServiceRegistry.java new file mode 100644 index 0000000000..5c63c41007 --- /dev/null +++ b/src/main/java/org/alfresco/repo/content/transform/LocalTransformServiceRegistry.java @@ -0,0 +1,398 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 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.transform; + +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.NodeRef; +import org.alfresco.transform.client.model.config.TransformServiceRegistry; +import org.alfresco.transform.client.model.config.TransformServiceRegistryImpl; +import org.alfresco.transform.client.model.config.TransformStep; +import org.alfresco.transform.client.model.config.Transformer; +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.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +/** + * Implements {@link TransformServiceRegistry} providing a mechanism of validating if a local transformation + * (based on {@link LocalTransformer} request is supported. It also extends this interface to provide a + * {@link #transform} method. + * @author adavis + */ +public class LocalTransformServiceRegistry extends TransformServiceRegistryImpl implements InitializingBean +{ + private static final Log log = LogFactory.getLog(LocalTransformer.class); + + private static final String LOCAL_TRANSFORMER = "localTransformer."; + private static final String URL = ".url"; + static final String STRICT_MIMETYPE_CHECK_WHITELIST_MIMETYPES = "transformer.strict.mimetype.check.whitelist.mimetypes"; + + private String pipelineConfigFolder; + private boolean enabled = true; + private boolean firstTime = true; + private Properties properties; + private MimetypeService mimetypeService; + private TransformerDebug transformerDebug; + private boolean strictMimeTypeCheck; + private Map> strictMimetypeExceptions; + private boolean retryTransformOnDifferentMimeType; + + private Map transformers = new HashMap<>(); + + public void setPipelineConfigFolder(String pipelineConfigFolder) + { + this.pipelineConfigFolder = pipelineConfigFolder; + } + + public void setEnabled(boolean enabled) + { + this.enabled = enabled; + firstTime = true; + } + + /** + * The Alfresco global properties. + */ + public void setProperties(Properties properties) + { + this.properties = properties; + } + + public void setMimetypeService(MimetypeService mimetypeService) + { + this.mimetypeService = mimetypeService; + } + + public void setTransformerDebug(TransformerDebug transformerDebug) + { + this.transformerDebug = transformerDebug; + } + + public void setStrictMimeTypeCheck(boolean strictMimeTypeCheck) + { + this.strictMimeTypeCheck = strictMimeTypeCheck; + } + + public void setRetryTransformOnDifferentMimeType(boolean retryTransformOnDifferentMimeType) + { + this.retryTransformOnDifferentMimeType = retryTransformOnDifferentMimeType; + } + + @Override + public void afterPropertiesSet() throws Exception + { + PropertyCheck.mandatory(this, "mimetypeService", mimetypeService); + PropertyCheck.mandatory(this, "pipelineConfigFolder", pipelineConfigFolder); + PropertyCheck.mandatory(this, "properties", properties); + PropertyCheck.mandatory(this, "transformerDebug", transformerDebug); + super.afterPropertiesSet(); + transformers.clear(); + + strictMimetypeExceptions = getStrictMimetypeExceptions(); + + // TODO read json from the T-Engines. Need to find urls to these by looking for a-g.p or system props that match "localTransformer.*.url" + // Do before reading local files, so the files can override T-Engine values. + List urls = getTEngineUrls(); + + // Reads files alfresco/transformers from resource path + register(pipelineConfigFolder); + } + + @Override + public void register(Transformer transformer) + { + super.register(transformer); + + try + { + String name = transformer.getTransformerName(); + + if (name == null || transformers.get(name) != null) + { + throw new IllegalArgumentException("Local transformer names must exist and be unique (" + name + ")."); + } + + List transformerPipeline = transformer.getTransformerPipeline(); + LocalTransformer localTransformer; + if (transformerPipeline == null) + { + String baseUrl = getBaseUrl(name); + int startupRetryPeriodSeconds = getStartupRetryPeriodSeconds(name); + localTransformer = new LocalTransformerImpl(name, transformerDebug, mimetypeService, + strictMimeTypeCheck, strictMimetypeExceptions, retryTransformOnDifferentMimeType, + this, baseUrl, startupRetryPeriodSeconds); + } + else + { + int transformerCount = transformerPipeline.size(); + if (transformerCount <= 1) + { + throw new IllegalArgumentException("Local pipeline transformer " + name + + " must have more than one intermediate transformer defined."); + } + + localTransformer = new LocalPipelineTransformer(name, transformerDebug, mimetypeService, + strictMimeTypeCheck, strictMimetypeExceptions, retryTransformOnDifferentMimeType, + this); + for (int i=0; i < transformerCount; i++) + { + TransformStep intermediateTransformerStep = transformerPipeline.get(i); + String intermediateTransformerName = intermediateTransformerStep.getTransformerName(); + if (name == null || transformers.get(name) != null) + { + throw new IllegalArgumentException("Local pipeline transformer " + name + + " did not specified an intermediate transformer name."); + } + + LocalTransformer intermediateTransformer = transformers.get(intermediateTransformerName); + if (intermediateTransformer == null) + { + throw new IllegalArgumentException("Local pipeline transformer " + name + + " specified an intermediate transformer (" + + intermediateTransformerName + " that has not previously been defined."); + } + + String targetMimetype = intermediateTransformerStep.getTargetMediaType(); + if (i == transformerCount-1) + { + if (targetMimetype != null) + { + throw new IllegalArgumentException("Local pipeline transformer " + name + + " must not specify targetExt for the final intermediate transformer, " + + "as this is defined via the supportedSourceAndTargetList."); + } + } + else + { + if (targetMimetype == null) + { + throw new IllegalArgumentException("Local pipeline transformer " + name + + " must specify targetExt for all intermediate transformers except for the final one."); + } + } + ((LocalPipelineTransformer)localTransformer).addIntermediateTransformer(intermediateTransformer, targetMimetype); + } + } + transformers.put(name, localTransformer); + } + catch (IllegalArgumentException e) + { + String msg = e.getMessage(); + getLog().error(msg); + } + } + + @Override + protected Log getLog() + { + return log; + } + + private List getTEngineUrls() + { + List urls = new ArrayList<>(); + for (Object o : getKeySet()) + { + if (o instanceof String) + { + String key = (String)o; + if (key.startsWith(LOCAL_TRANSFORMER) && key.endsWith(URL)) + { + Object url = getProperty(key, null); + if (url instanceof String) + { + String urlStr = ((String)url).trim(); + if (!urlStr.isEmpty()) + { + urls.add((String) url); + getLog().debug("T-Engine "+key+"="+url); + } + } + } + } + } + + return urls; + } + + private String getBaseUrl(String name) + { + String baseUrlName = LOCAL_TRANSFORMER + name + URL; + String baseUrl = getProperty(baseUrlName, null); + if (baseUrl == null) + { + throw new IllegalArgumentException("Local transformer property " + baseUrlName + " was not set"); + } + return baseUrl; + } + + private int getStartupRetryPeriodSeconds(String name) + { + String startupRetryPeriodSecondsName = LOCAL_TRANSFORMER + name + ".startupRetryPeriodSeconds"; + String property = getProperty(startupRetryPeriodSecondsName, "60"); + int startupRetryPeriodSeconds; + try + { + startupRetryPeriodSeconds = Integer.parseInt(property); + } + catch (NumberFormatException e) + { + throw new IllegalArgumentException("Local transformer property " + startupRetryPeriodSecondsName + + " should be an integer"); + } + return startupRetryPeriodSeconds; + } + + private Map> getStrictMimetypeExceptions() + { + Map> strictMimetypeExceptions = new HashMap<>(); + + String whitelist = getProperty(STRICT_MIMETYPE_CHECK_WHITELIST_MIMETYPES, "").trim(); + if (!whitelist.isEmpty()) + { + String[] mimetypes = whitelist.split(";"); + + if (mimetypes.length % 2 != 0) + { + getLog().error(STRICT_MIMETYPE_CHECK_WHITELIST_MIMETYPES+" should have an even number of mimetypes as a ; separated list."); + } + else + { + Set detectedMimetypes = null; + for (String mimetype: mimetypes) + { + mimetype = mimetype.trim(); + if (mimetype.isEmpty()) + { + getLog().error(STRICT_MIMETYPE_CHECK_WHITELIST_MIMETYPES+" contains a blank mimetype."); + // Still okay to use it in the map though, but it will be ignored. + } + + if (detectedMimetypes == null) + { + detectedMimetypes = strictMimetypeExceptions.get(mimetype); + if (detectedMimetypes == null) + { + detectedMimetypes = new HashSet<>(); + strictMimetypeExceptions.put(mimetype, detectedMimetypes); + } + } + else + { + detectedMimetypes.add(mimetype); + detectedMimetypes = null; + } + } + } + } + + return strictMimetypeExceptions; + } + + /** + * @return the set of property keys and System keys. + */ + private Set getKeySet() + { + Set systemKeys = System.getProperties().keySet(); + Set alfrescoGlobalKeys = this.properties.keySet(); + Set keys = new HashSet<>(systemKeys.size()+alfrescoGlobalKeys.size()); + addStrings(keys, systemKeys); + addStrings(keys, alfrescoGlobalKeys); + return keys; + } + + private void addStrings(Set setOfStrings, Set objects) + { + objects.forEach(object->{ + if (object instanceof String) + { + setOfStrings.add((String)object); + } + }); + } + + /** + * Gets a property from an alfresco global property but falls back to a System property with the same name to + * allow dynamic creation of transformers without having to have an AMP to add the alfresco global property. + */ + private String getProperty(String name, String defaultValue) + { + String value = properties.getProperty(name); + if (value == null || value.isEmpty()) + { + value = System.getProperty(name); + if (value != null && value.isEmpty()) + { + value = null; + } + } + return value == null ? defaultValue : value; + } + + @Override + public long getMaxSize(String sourceMimetype, String targetMimetype, Map options, String renditionName) + { + // This message is not logged if placed in afterPropertiesSet + if (firstTime) + { + firstTime = false; + transformerDebug.debug("Local transforms are " + (enabled ? "enabled" : "disabled")); + } + + return enabled + ? super.getMaxSize(sourceMimetype, targetMimetype, options, renditionName) + : 0; + } + + public void transform(ContentReader reader, ContentWriter writer, Map actualOptions, + String renditionName, NodeRef sourceNodeRef) throws Exception + { + + String sourceMimetype = reader.getMimetype(); + String targetMimetype = writer.getMimetype(); + long sourceSizeInBytes = reader.getSize(); + LocalTransformer localTransformer = getLocalTransformer(actualOptions, renditionName, sourceMimetype, targetMimetype, sourceSizeInBytes); + localTransformer.transform(reader, writer, actualOptions, renditionName, sourceNodeRef); + } + + public LocalTransformer getLocalTransformer(Map actualOptions, String renditionName, + String sourceMimetype, String targetMimetype, long sourceSizeInBytes) + { + String name = getTransformerName(sourceMimetype, sourceSizeInBytes, targetMimetype, actualOptions, renditionName); + return transformers.get(name); + } +} diff --git a/src/main/java/org/alfresco/repo/content/transform/LocalTransformer.java b/src/main/java/org/alfresco/repo/content/transform/LocalTransformer.java new file mode 100644 index 0000000000..abf6672cf3 --- /dev/null +++ b/src/main/java/org/alfresco/repo/content/transform/LocalTransformer.java @@ -0,0 +1,42 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 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.transform; + +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; + +import java.util.Map; + +/** + * Interface of a local transformer using flat transform options. + */ +public interface LocalTransformer +{ + void transform(ContentReader reader, ContentWriter writer, Map transformOptions, + String renditionName, NodeRef sourceNodeRef) + throws Exception; +} diff --git a/src/main/java/org/alfresco/repo/content/transform/LocalTransformerImpl.java b/src/main/java/org/alfresco/repo/content/transform/LocalTransformerImpl.java new file mode 100644 index 0000000000..62824fb9b5 --- /dev/null +++ b/src/main/java/org/alfresco/repo/content/transform/LocalTransformerImpl.java @@ -0,0 +1,163 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 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.transform; + +import org.alfresco.repo.rendition2.RenditionDefinition2; +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.NodeRef; +import org.alfresco.util.Pair; + +import java.util.Map; +import java.util.Set; + +/** + * A local transformer using flat transform options. + * + * Instances are automatically created for transformers identified by alfresco/transform json files and returned from + * T-Engines which are themselves identified by global properties or system properties the match the pattern + * localTransformer.<name>.url. The transforms take place in a separate process (typically a Docker container). + */ +public class LocalTransformerImpl extends AbstractLocalTransformer +{ + private RemoteTransformerClient remoteTransformerClient; + + private boolean available = false; + + public LocalTransformerImpl(String name, TransformerDebug transformerDebug, + MimetypeService mimetypeService, boolean strictMimeTypeCheck, + Map> strictMimetypeExceptions, + boolean retryTransformOnDifferentMimeType, + LocalTransformServiceRegistry localTransformServiceRegistry, String baseUrl, + int startupRetryPeriodSeconds) + { + super(name, transformerDebug, mimetypeService, strictMimeTypeCheck, strictMimetypeExceptions, + retryTransformOnDifferentMimeType, localTransformServiceRegistry); + remoteTransformerClient = new RemoteTransformerClient(name, baseUrl); + remoteTransformerClient.setStartupRetryPeriodSeconds(startupRetryPeriodSeconds); + + checkAvailability(); + } + + private boolean remoteTransformerClientConfigured() + { + return remoteTransformerClient.getBaseUrl() != null; + } + + @Override + public boolean isAvailable() + { + if (remoteTransformerClientConfigured() && !remoteTransformerClient.isAvailable()) + { + checkAvailability(); + } + + return available; + } + + private void setAvailable(boolean available) + { + this.available = available; + } + + private void checkAvailability() + { + // check availability + if (remoteTransformerClientConfigured()) + { + try + { + Pair result = remoteTransformerClient.check(log); + Boolean isAvailable = result.getFirst(); + String msg = result.getSecond() == null ? "" : result.getSecond(); + if (isAvailable != null && isAvailable) + { + setAvailable(true); + log.info("Using local transformer " + name + ": " + msg); + } + else + { + setAvailable(false); + String message = "Local transformer " + name + " is not available. " + msg; + if (isAvailable == null) + { + log.debug(message); + } + else + { + log.error(message); + } + } + } + catch (Throwable e) + { + setAvailable(false); + log.error("Local transformer " + name + " is not available: " + (e.getMessage() != null ? e.getMessage() : "")); + log.debug(e); + } + } + else + { + setAvailable(false); + } + } + + @Override + protected void transformImpl(ContentReader reader, + ContentWriter writer, Map transformOptions, + String sourceMimetype, String targetMimetype, + String sourceExtension, String targetExtension, + String targetEncoding, String renditionName, NodeRef sourceNodeRef) throws Exception + { + // Build an array of option names and values and extract the timeout. + long timeoutMs = 0; + int nonOptions = transformOptions.containsKey(RenditionDefinition2.TIMEOUT) ? 1 : 0; + int size = (transformOptions.size() - nonOptions) * 2; + String[] args = new String[size]; + int i = 0; + for (Map.Entry option : transformOptions.entrySet()) + { + String name = option.getKey(); + String value = option.getValue(); + if (RenditionDefinition2.TIMEOUT.equals(name)) + { + if (value != null) + { + timeoutMs = Long.parseLong(value); + } + } + else + { + args[i++] = name; + args[i++] = value; + } + } + + remoteTransformerClient.request(reader, writer, sourceMimetype, sourceExtension, targetExtension, + timeoutMs, log, args); + } +} 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 c6b0c20e94..70903c7ed5 100644 --- a/src/main/java/org/alfresco/repo/content/transform/RemoteTransformerClient.java +++ b/src/main/java/org/alfresco/repo/content/transform/RemoteTransformerClient.java @@ -53,10 +53,7 @@ import java.util.StringJoiner; * saved in a ContentWriter. In the event of an error an Exception is thrown. * * @since 6.0 - * - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. */ -@Deprecated public class RemoteTransformerClient { private final String name; @@ -137,7 +134,7 @@ public class RemoteTransformerClient if (logger.isDebugEnabled()) { - logger.debug("Remote "+name+' '+sourceExtension+' '+targetExtension+' '+url+' '+args); + logger.debug(name+' '+sourceExtension+' '+targetExtension+' '+url+' '+args); } try @@ -149,7 +146,7 @@ public class RemoteTransformerClient StatusLine statusLine = response.getStatusLine(); if (statusLine == null) { - throw new AlfrescoRuntimeException("Remote "+name+" returned no status " + url + ' ' + args); + throw new AlfrescoRuntimeException(name+" returned no status " + url + ' ' + args); } HttpEntity resEntity = response.getEntity(); if (resEntity != null) @@ -164,7 +161,7 @@ public class RemoteTransformerClient long responseContentLength = resEntity.getContentLength(); Header responseContentEncoding = resEntity.getContentEncoding(); Header responseContentType = resEntity.getContentType(); - logger.debug("Remote " + name + ' ' + sourceExtension + ' ' + targetExtension + + logger.debug(name + ' ' + sourceExtension + ' ' + targetExtension + " returned. length=" + responseContentLength + " type=" + responseContentType + " encoding=" + responseContentEncoding); @@ -175,13 +172,13 @@ public class RemoteTransformerClient } catch (IOException e) { - throw new AlfrescoRuntimeException("Remote " + name + " failed to read the returned content", e); + throw new AlfrescoRuntimeException(name + " failed to read the returned content", e); } } else { String message = getErrorMessage(resEntity); - String msg = ("Remote " + name + " returned a " + statusCode + " status " + message + + String msg = (name + " returned a " + statusCode + " status " + message + ' ' + url + ' ' + args).trim(); if (statusCode == 401) { @@ -199,7 +196,7 @@ public class RemoteTransformerClient } else { - throw new AlfrescoRuntimeException("Remote " + name + " did not return an entity " + url); + throw new AlfrescoRuntimeException(name + " did not return an entity " + url); } } catch (IOException e) @@ -207,12 +204,12 @@ public class RemoteTransformerClient // In the case of transform requests, unlike version checks, it is only the failure to connect that // forces a wait before trying again. connectionFailed(); - throw new AlfrescoRuntimeException("Remote " + name + " failed to connect or to read the response", e); + throw new AlfrescoRuntimeException(name + " failed to connect or to read the response", e); } } catch (IOException e) { - throw new AlfrescoRuntimeException("Remote " + name + " failed to create an HttpClient", e); + throw new AlfrescoRuntimeException(name + " failed to create an HttpClient", e); } } catch (AlfrescoRuntimeException e) @@ -235,7 +232,7 @@ public class RemoteTransformerClient { if (!isTimeToCheckAvailability()) { - logger.debug("Remote "+name+' '+" too early to check availability"); + logger.debug(name+' '+" too early to check availability"); Pair result = getCheckResult(); return result; } @@ -245,7 +242,7 @@ public class RemoteTransformerClient if (logger.isDebugEnabled()) { - logger.debug("Remote "+name+' '+" check" +url); + logger.debug(name+' '+" check" +url); } try @@ -257,7 +254,7 @@ public class RemoteTransformerClient StatusLine statusLine = response.getStatusLine(); if (statusLine == null) { - throw new AlfrescoRuntimeException("Remote "+name+" check returned no status " + url); + throw new AlfrescoRuntimeException(name+" check returned no status " + url); } HttpEntity resEntity = response.getEntity(); if (resEntity != null) @@ -274,7 +271,7 @@ public class RemoteTransformerClient long responseContentLength = resEntity.getContentLength(); Header responseContentType = resEntity.getContentType(); Header responseContentEncoding = resEntity.getContentEncoding(); - logger.debug("Remote " + name + + logger.debug(name + " check returned. length=" + responseContentLength + " type=" + responseContentType + " encoding=" + responseContentEncoding+ @@ -289,28 +286,28 @@ public class RemoteTransformerClient } catch (IOException e) { - throw new AlfrescoRuntimeException("Remote " + name + " check failed to read the returned content", e); + throw new AlfrescoRuntimeException(name + " check failed to read the returned content", e); } } else { String message = getErrorMessage(resEntity); - throw new AlfrescoRuntimeException("Remote " + name + " check returned a " + statusCode + " status " + message + ' ' + url); + throw new AlfrescoRuntimeException(name + " check returned a " + statusCode + " status " + message + ' ' + url); } } else { - throw new AlfrescoRuntimeException("Remote " + name + " check did not return an entity " + url); + throw new AlfrescoRuntimeException(name + " check did not return an entity " + url); } } catch (IOException e) { - throw new AlfrescoRuntimeException("Remote " + name + " check failed to connect or to read the response", e); + throw new AlfrescoRuntimeException(name + " check failed to connect or to read the response", e); } } catch (IOException e) { - throw new AlfrescoRuntimeException("Remote " + name + " check failed to create an HttpClient", e); + throw new AlfrescoRuntimeException(name + " check failed to create an HttpClient", e); } } catch (AlfrescoRuntimeException e) 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 6ffabb4cc9..c7377eb382 100644 --- a/src/main/java/org/alfresco/repo/content/transform/TransformerDebug.java +++ b/src/main/java/org/alfresco/repo/content/transform/TransformerDebug.java @@ -25,8 +25,37 @@ */ package org.alfresco.repo.content.transform; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.content.filestore.FileContentWriter; +import org.alfresco.repo.model.Repository; +import org.alfresco.repo.rendition2.RenditionDefinition2; +import org.alfresco.repo.rendition2.RenditionDefinition2Impl; +import org.alfresco.repo.rendition2.RenditionDefinitionRegistry2Impl; +import org.alfresco.repo.rendition2.TransformClient; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.MimetypeService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.TransformationOptions; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.EqualsHelper; +import org.alfresco.util.LogTee; +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.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + import java.io.File; -import java.io.IOException; +import java.io.Serializable; +import java.net.URI; import java.net.URL; import java.util.ArrayDeque; import java.util.ArrayList; @@ -34,8 +63,11 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Deque; +import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; @@ -43,24 +75,7 @@ import java.util.TreeSet; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Pattern; -import org.alfresco.api.AlfrescoPublicApi; -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.model.ContentModel; -import org.alfresco.repo.content.filestore.FileContentReader; -import org.alfresco.repo.content.filestore.FileContentWriter; -import org.alfresco.service.cmr.repository.ContentReader; -import org.alfresco.service.cmr.repository.ContentService; -import org.alfresco.service.cmr.repository.ContentWriter; -import org.alfresco.service.cmr.repository.MimetypeService; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.TransformationOptions; -import org.alfresco.util.EqualsHelper; -import org.alfresco.util.LogTee; -import org.alfresco.util.TempFileProvider; -import org.apache.commons.io.FileUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import static org.alfresco.repo.rendition2.RenditionService2Impl.SOURCE_HAS_NO_CONTENT; /** * Debugs transformers selection and activity.

@@ -79,21 +94,26 @@ import org.apache.commons.logging.LogFactory; * transformers) and {@link #popAvailable} are called.

* * @author Alan Davis - * - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. */ -@Deprecated -@AlfrescoPublicApi -public class TransformerDebug +public class TransformerDebug implements ApplicationContextAware { private static final String FINISHED_IN = "Finished in "; private static final String NO_TRANSFORMERS = "No transformers"; - private final Log logger; - private final Log info; + private Log info; + private Log logger; + private NodeService nodeService; + private MimetypeService mimetypeService; + private ContentTransformerRegistry transformerRegistry; + private TransformerConfig transformerConfig; + + private ApplicationContext applicationContext; + private ContentService contentService; + private TransformClient transformClient; + private Repository repositoryHelper; + private TransactionService transactionService; + private RenditionDefinitionRegistry2Impl renditionDefinitionRegistry2; - @Deprecated - @AlfrescoPublicApi private enum Call { AVAILABLE, @@ -101,8 +121,6 @@ public class TransformerDebug AVAILABLE_AND_TRANSFORM }; - @Deprecated - @AlfrescoPublicApi private static class ThreadInfo { private static final ThreadLocal threadInfo = new ThreadLocal() @@ -153,8 +171,6 @@ public class TransformerDebug } } - @Deprecated - @AlfrescoPublicApi private static class Frame { private static final AtomicInteger uniqueId = new AtomicInteger(0); @@ -163,7 +179,8 @@ public class TransformerDebug private final String fromUrl; private final String sourceMimetype; private final String targetMimetype; - private final TransformationOptions options; + private final NodeRef sourceNodeRef; + private final String renditionName; private final boolean origDebugOutput; private long start; @@ -176,7 +193,7 @@ public class TransformerDebug private String transformerName; private Frame(Frame parent, String transformerName, String fromUrl, String sourceMimetype, String targetMimetype, - long sourceSize, TransformationOptions options, Call pushCall, boolean origDebugOutput) + long sourceSize, String renditionName, NodeRef sourceNodeRef, Call pushCall, boolean origDebugOutput) { this.id = -1; this.parent = parent; @@ -185,7 +202,8 @@ public class TransformerDebug this.sourceMimetype = sourceMimetype; this.targetMimetype = targetMimetype; this.sourceSize = sourceSize; - this.options = options; + this.renditionName = renditionName; + this.sourceNodeRef = sourceNodeRef; this.callType = pushCall; this.origDebugOutput = origDebugOutput; start = System.currentTimeMillis(); @@ -232,7 +250,6 @@ public class TransformerDebug } @Deprecated - @AlfrescoPublicApi private class UnavailableTransformer implements Comparable { private final String name; @@ -282,27 +299,50 @@ public class TransformerDebug return name.compareTo(o.name); } } - - private final NodeService nodeService; - private final MimetypeService mimetypeService; - private final ContentTransformerRegistry transformerRegistry; - private final TransformerConfig transformerConfig; - private ContentService contentService; - - /** - * Constructor - */ - public TransformerDebug(NodeService nodeService, MimetypeService mimetypeService, - ContentTransformerRegistry transformerRegistry, TransformerConfig transformerConfig, - Log transformerLog, Log transformerDebugLog) + + public void setTransformerLog(Log transformerLog) + { + info = new LogTee(LogFactory.getLog(TransformerLog.class), transformerLog); + } + + public void setTransformerDebugLog(Log transformerDebugLog) + { + logger = new LogTee(LogFactory.getLog(TransformerDebug.class), transformerDebugLog); + } + + public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; + } + + public void setMimetypeService(MimetypeService mimetypeService) + { this.mimetypeService = mimetypeService; + } + + public void setTransformerRegistry(ContentTransformerRegistry transformerRegistry) + { this.transformerRegistry = transformerRegistry; + } + + public void setTransformerConfig(TransformerConfig transformerConfig) + { this.transformerConfig = transformerConfig; - - logger = new LogTee(LogFactory.getLog(TransformerDebug.class), transformerDebugLog); - info = new LogTee(LogFactory.getLog(TransformerLog.class), transformerLog); + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException + { + this.applicationContext = applicationContext; + } + + private ContentService getContentService() + { + if (contentService == null) + { + contentService = (ContentService) applicationContext.getBean("contentService"); + } + return contentService; } public void setContentService(ContentService contentService) @@ -310,40 +350,138 @@ public class TransformerDebug this.contentService = contentService; } + private TransformClient getTransformClient() + { + if (transformClient == null) + { + transformClient = (TransformClient) applicationContext.getBean("transformClient"); + } + return transformClient; + } + + public void setTransformClient(TransformClient transformClient) + { + this.transformClient = transformClient; + } + + public Repository getRepositoryHelper() + { + if (repositoryHelper == null) + { + repositoryHelper = (Repository) applicationContext.getBean("repositoryHelper"); + } + return repositoryHelper; + } + + public void setRepositoryHelper(Repository repositoryHelper) + { + this.repositoryHelper = repositoryHelper; + } + + public TransactionService getTransactionService() + { + if (transactionService == null) + { + transactionService = (TransactionService) applicationContext.getBean("transactionService"); + } + return transactionService; + } + + public void setTransactionService(TransactionService transactionService) + { + this.transactionService = transactionService; + } + + public RenditionDefinitionRegistry2Impl getRenditionDefinitionRegistry2Impl() + { + if (renditionDefinitionRegistry2 == null) + { + renditionDefinitionRegistry2 = (RenditionDefinitionRegistry2Impl) applicationContext.getBean("renditionDefinitionRegistry2"); + } + return renditionDefinitionRegistry2; + } + + public void setRenditionDefinitionRegistry2(RenditionDefinitionRegistry2Impl renditionDefinitionRegistry2) + { + this.renditionDefinitionRegistry2 = renditionDefinitionRegistry2; + } + + public void afterPropertiesSet() throws Exception + { + PropertyCheck.mandatory(this, "transformerLog", info); + PropertyCheck.mandatory(this, "transformerDebugLog", logger); + PropertyCheck.mandatory(this, "nodeService", nodeService); + PropertyCheck.mandatory(this, "mimetypeService", mimetypeService); + PropertyCheck.mandatory(this, "transformerRegistry", transformerRegistry); + PropertyCheck.mandatory(this, "transformerConfig", transformerConfig); + } + + @Deprecated + public void pushAvailable(String fromUrl, String sourceMimetype, String targetMimetype, + TransformationOptions options) + { + String renditionName = options == null ? null : options.getUse(); + NodeRef sourceNodeRef = options == null ? null : options.getSourceNodeRef(); + pushAvailable(fromUrl, sourceMimetype, targetMimetype, renditionName, sourceNodeRef); + } + /** * Called prior to working out what transformers are available. */ + @Deprecated public void pushAvailable(String fromUrl, String sourceMimetype, String targetMimetype, - TransformationOptions options) + String renditionName, NodeRef sourceNodeRef) { if (isEnabled()) { - push(null, fromUrl, sourceMimetype, targetMimetype, -1, options, Call.AVAILABLE); + push(null, fromUrl, sourceMimetype, targetMimetype, -1, renditionName, + sourceNodeRef, Call.AVAILABLE); } } /** * Called when a transformer has been ignored because of a blacklist entry. */ + @Deprecated public void blacklistTransform(ContentTransformer transformer, String sourceMimetype, String targetMimetype, TransformationOptions options) { log("Blacklist "+getName(transformer)+" "+getMimetypeExt(sourceMimetype)+getMimetypeExt(targetMimetype)); } - + + @Deprecated + public void pushTransform(ContentTransformer transformer, String fromUrl, String sourceMimetype, + String targetMimetype, long sourceSize, TransformationOptions options) + { + String renditionName = options == null ? null : options.getUse(); + NodeRef sourceNodeRef = options == null ? null : options.getSourceNodeRef(); + pushTransform(transformer, fromUrl, sourceMimetype, targetMimetype, sourceSize, renditionName, sourceNodeRef); + } + /** * Called prior to performing a transform. */ + @Deprecated public void pushTransform(ContentTransformer transformer, String fromUrl, String sourceMimetype, - String targetMimetype, long sourceSize, TransformationOptions options) + String targetMimetype, long sourceSize, String renditionName, NodeRef sourceNodeRef) { if (isEnabled()) { push(getName(transformer), fromUrl, sourceMimetype, targetMimetype, sourceSize, - options, Call.TRANSFORM); + renditionName, sourceNodeRef, Call.TRANSFORM); } } - + + public void pushTransform(String transformerName, String fromUrl, String sourceMimetype, + String targetMimetype, long sourceSize, String renditionName, NodeRef sourceNodeRef) + { + if (isEnabled()) + { + push(transformerName, fromUrl, sourceMimetype, targetMimetype, sourceSize, + renditionName, sourceNodeRef, Call.TRANSFORM); + } + } + /** * Adds a new level to the stack to get a new request number or nesting number. * Called prior to working out what transformers are active @@ -353,13 +491,14 @@ public class TransformerDebug { if (isEnabled()) { - push(null, null, null, null, -1, null, Call.AVAILABLE); + push(null, null, null, null, -1, null, null, Call.AVAILABLE); } } /** * Called prior to calling a nested isTransformable. */ + @Deprecated public void pushIsTransformableSize(ContentTransformer transformer) { if (isEnabled()) @@ -369,7 +508,7 @@ public class TransformerDebug } private void push(String transformerName, String fromUrl, String sourceMimetype, String targetMimetype, - long sourceSize, TransformationOptions options, Call callType) + long sourceSize, String renditionName, NodeRef sourceNodeRef, Call callType) { Deque ourStack = ThreadInfo.getStack(); Frame frame = ourStack.peek(); @@ -383,13 +522,14 @@ public class TransformerDebug // Create a new frame. Logging level is set to trace if the file size is 0 boolean origDebugOutput = ThreadInfo.setDebugOutput(ThreadInfo.getDebugOutput() && sourceSize != 0); - frame = new Frame(frame, transformerName, fromUrl, sourceMimetype, targetMimetype, sourceSize, options, callType, origDebugOutput); + frame = new Frame(frame, transformerName, fromUrl, sourceMimetype, targetMimetype, sourceSize, renditionName, + sourceNodeRef, callType, origDebugOutput); ourStack.push(frame); if (callType == Call.TRANSFORM) { // Log the basic info about this transformation - logBasicDetails(frame, sourceSize, options.getUse(), transformerName, (ourStack.size() == 1)); + logBasicDetails(frame, sourceSize, renditionName, transformerName, (ourStack.size() == 1)); } } @@ -397,6 +537,7 @@ public class TransformerDebug * Called to identify a transformer that cannot be used during working out * available transformers. */ + @Deprecated public void unavailableTransformer(ContentTransformer transformer, String sourceMimetype, String targetMimetype, long maxSourceSizeKBytes) { if (isEnabled()) @@ -421,11 +562,21 @@ public class TransformerDebug } } + @Deprecated + public void availableTransformers(List transformers, long sourceSize, + TransformationOptions options, String calledFrom) + { + String renditionName = options == null ? null : options.getUse(); + NodeRef sourceNodeRef = options == null ? null : options.getSourceNodeRef(); + availableTransformers(transformers, sourceSize, renditionName, sourceNodeRef, calledFrom); + } + /** * Called once all available transformers have been identified. */ + @Deprecated public void availableTransformers(List transformers, long sourceSize, - TransformationOptions options, String calledFrom) + String renditionName, NodeRef sourceNodeRef, String calledFrom) { if (isEnabled()) { @@ -447,7 +598,7 @@ public class TransformerDebug frame.setSourceSize(sourceSize); // Log the basic info about this transformation - logBasicDetails(frame, sourceSize, options.getUse(), + logBasicDetails(frame, sourceSize, renditionName, calledFrom + ((transformers.size() == 0) ? " NO transformers" : ""), firstLevel); // Report available and unavailable transformers @@ -457,7 +608,11 @@ public class TransformerDebug { String name = getName(trans); int padName = longestNameLength - name.length() + 1; - long maxSourceSizeKBytes = trans.getMaxSourceSizeKBytes(frame.sourceMimetype, frame.targetMimetype, frame.options); + // TODO replace with call to RenditionService2 or leave as a deprecated method using ContentService. + TransformationOptions options = new TransformationOptions(); + options.setUse(frame.renditionName); + options.setSourceNodeRef(frame.sourceNodeRef); + long maxSourceSizeKBytes = trans.getMaxSourceSizeKBytes(frame.sourceMimetype, frame.targetMimetype, options); String size = maxSourceSizeKBytes > 0 ? "< "+fileSize(maxSourceSizeKBytes*1024) : ""; int padSize = 10 - size.length(); String priority = gePriority(trans, frame.sourceMimetype, frame.targetMimetype); @@ -491,11 +646,13 @@ public class TransformerDebug return priority; } + @Deprecated public void inactiveTransformer(ContentTransformer transformer) { log(getName(transformer)+' '+ms(transformer.getTransformationTime(null, null))+" INACTIVE"); } + @Deprecated public void activeTransformer(int mimetypePairCount, ContentTransformer transformer, String sourceMimetype, String targetMimetype, long maxSourceSizeKBytes, boolean firstMimetypePair) { @@ -510,8 +667,9 @@ public class TransformerDebug ' '+fileSize((maxSourceSizeKBytes > 0) ? maxSourceSizeKBytes*1024 : maxSourceSizeKBytes)+ (maxSourceSizeKBytes == 0 ? " disabled" : "")); } - - public void activeTransformer(String sourceMimetype, String targetMimetype, + + @Deprecated + public void activeTransformer(String sourceMimetype, String targetMimetype, int transformerCount, ContentTransformer transformer, long maxSourceSizeKBytes, boolean firstTransformer) { @@ -548,7 +706,7 @@ public class TransformerDebug return longestNameLength; } - private void logBasicDetails(Frame frame, long sourceSize, String use, String message, boolean firstLevel) + private void logBasicDetails(Frame frame, long sourceSize, String renditionName, String message, boolean firstLevel) { // Log the source URL, but there is no point if the parent has logged it if (frame.fromUrl != null && (firstLevel || frame.id != 1)) @@ -557,14 +715,14 @@ public class TransformerDebug } log(frame.sourceMimetype+' '+frame.targetMimetype, false); - String fileName = getFileName(frame.options, firstLevel, sourceSize); + String fileName = getFileName(frame.sourceNodeRef, firstLevel, sourceSize); log(getMimetypeExt(frame.sourceMimetype)+getMimetypeExt(frame.targetMimetype) + ((fileName != null) ? fileName+' ' : "")+ ((sourceSize >= 0) ? fileSize(sourceSize)+' ' : "") + - (firstLevel && use != null ? "-- "+use+" -- " : "") + message); + (firstLevel && renditionName != null ? "-- "+renditionName+" -- " : "") + message); if (firstLevel) { - String nodeRef = getNodeRef(frame.options, firstLevel, sourceSize); + String nodeRef = getNodeRef(frame.sourceNodeRef, firstLevel, sourceSize); if (!nodeRef.isEmpty()) { log(nodeRef); @@ -659,7 +817,7 @@ public class TransformerDebug boolean firstLevel = size == 1; String sourceExt = getMimetypeExt(frame.sourceMimetype); String targetExt = getMimetypeExt(frame.targetMimetype); - String fileName = getFileName(frame.options, firstLevel, frame.sourceSize); + String fileName = getFileName(frame.sourceNodeRef, firstLevel, frame.sourceSize); long sourceSize = frame.getSourceSize(); String transformerName = frame.getTransformerName(); String level = null; @@ -893,9 +1051,11 @@ public class TransformerDebug * @param toString indicates that a String value should be returned in addition to any debug. * @param format42 indicates the old 4.1.4 format should be used which did not order the transformers * and only included top level transformers. - * @param use to which the transformation will be put (such as "Index", "Preview", null). + * @param renditionName to which the transformation will be put (such as "Index", "Preview", null). + * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. */ - public String transformationsByTransformer(String transformerName, boolean toString, boolean format42, String use) + @Deprecated + public String transformationsByTransformer(String transformerName, boolean toString, boolean format42, String renditionName) { // Do not generate this type of debug if already generating other debug to a StringBuilder // (for example a test transform). @@ -915,7 +1075,7 @@ public class TransformerDebug : mimetypeService.getMimetypes(); TransformationOptions options = new TransformationOptions(); - options.setUse(use); + options.setUse(renditionName); StringBuilder sb = null; try { @@ -977,10 +1137,12 @@ public class TransformerDebug * level transformers. * @param onlyNonDeterministic if true only report transformations where there is more than * one transformer available with the same priority. - * @param use to which the transformation will be put (such as "Index", "Preview", null). + * @param renditionName to which the transformation will be put (such as "Index", "Preview", null). + * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. */ + @Deprecated public String transformationsByExtension(String sourceExtension, String targetExtension, boolean toString, - boolean format42, boolean onlyNonDeterministic, String use) + boolean format42, boolean onlyNonDeterministic, String renditionName) { // Do not generate this type of debug if already generating other debug to a StringBuilder // (for example a test transform). @@ -1000,7 +1162,7 @@ public class TransformerDebug : mimetypeService.getMimetypes(); TransformationOptions options = new TransformationOptions(); - options.setUse(use); + options.setUse(renditionName); StringBuilder sb = null; try { @@ -1248,24 +1410,30 @@ public class TransformerDebug return !transformerRegistry.getTransformers().contains(transformer); } + @Deprecated public String getFileName(TransformationOptions options, boolean firstLevel, long sourceSize) { - return getFileNameOrNodeRef(options, firstLevel, sourceSize, true); + NodeRef sourceNodeRef = options == null ? null : options.getSourceNodeRef(); + return getFileName(sourceNodeRef, firstLevel, sourceSize); } - - private String getNodeRef(TransformationOptions options, boolean firstLevel, long sourceSize) + + public String getFileName(NodeRef sourceNodeRef, boolean firstLevel, long sourceSize) { - return getFileNameOrNodeRef(options, firstLevel, sourceSize, false); + return getFileNameOrNodeRef(sourceNodeRef, firstLevel, sourceSize, true); + } + + private String getNodeRef(NodeRef sourceNodeRef, boolean firstLevel, long sourceSize) + { + return getFileNameOrNodeRef(sourceNodeRef, firstLevel, sourceSize, false); } - private String getFileNameOrNodeRef(TransformationOptions options, boolean firstLevel, long sourceSize, boolean getName) + private String getFileNameOrNodeRef(NodeRef sourceNodeRef, boolean firstLevel, long sourceSize, boolean getName) { String result = getName ? null : ""; - if (options != null) + if (sourceNodeRef != null) { try { - NodeRef sourceNodeRef = options.getSourceNodeRef(); result = getName ? (String)nodeService.getProperty(sourceNodeRef, ContentModel.PROP_NAME) : sourceNodeRef.toString()+" "; @@ -1368,7 +1536,9 @@ public class TransformerDebug * @param transformerName to restrict the collection to one entry * @return a new Collection of sorted transformers * @throws IllegalArgumentException if transformerName is not found. + * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. */ + @Deprecated public Collection sortTransformersByName(String transformerName) { Collection transformers = (transformerName != null) @@ -1389,13 +1559,14 @@ public class TransformerDebug * Debugs a request to the Transform Service */ public int debugTransformServiceRequest(String sourceMimetype, long sourceSize, NodeRef sourceNodeRef, - int contentHashcode, String fileName, String targetMimetype, String use) + int contentHashcode, String fileName, String targetMimetype, + String renditionName) { pushMisc(); debug(getMimetypeExt(sourceMimetype)+getMimetypeExt(targetMimetype) + ((fileName != null) ? fileName+' ' : "")+ ((sourceSize >= 0) ? fileSize(sourceSize)+' ' : "") + - (use != null ? "-- "+use+" -- " : "") + " RenditionService2"); + (renditionName != null ? "-- "+renditionName+" -- " : "") + " RenditionService2"); debug(sourceNodeRef.toString() + ' ' +contentHashcode); debug(" **a) [01] TransformService"); return pop(Call.AVAILABLE, true, false); @@ -1426,35 +1597,19 @@ public class TransformerDebug pop(Call.AVAILABLE, suppressFinish, true); } - public String testTransform(String sourceExtension, String targetExtension, String use) + public String testTransform(String sourceExtension, String targetExtension, String renditionName) { - return new TestTransform() - { - protected void transform(ContentReader reader, ContentWriter writer, TransformationOptions options) - { - contentService.transform(reader, writer, options); - } - }.run(sourceExtension, targetExtension, use); + return new TestTransform().run(sourceExtension, targetExtension, renditionName); } - - public String testTransform(final String transformerName, String sourceExtension, - String targetExtension, String use) - { - final ContentTransformer transformer = transformerRegistry.getTransformer(transformerName); - return new TestTransform() - { - protected String isTransformable(String sourceMimetype, long sourceSize, String targetMimetype, TransformationOptions options) - { - return transformer.isTransformable(sourceMimetype, sourceSize, targetMimetype, options) - ? null - : transformerName+" does not support this transformation."; - } - protected void transform(ContentReader reader, ContentWriter writer, TransformationOptions options) - { - transformer.transform(reader, writer, options); - } - }.run(sourceExtension, targetExtension, use); + /** + * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. + */ + @Deprecated + public String testTransform(final String transformerName, String sourceExtension, + String targetExtension, String renditionName) + { + return "The testTransform operation for a specific transformer is no longer supported."; } public String[] getTestFileExtensionsAndMimetypes() @@ -1499,68 +1654,58 @@ public class TransformerDebug } @Deprecated - @AlfrescoPublicApi - private abstract class TestTransform + private class TestTransform { - String run(String sourceExtension, String targetExtension, String use) + protected LinkedList nodesToDeleteAfterTest = new LinkedList(); + + String run(String sourceExtension, String targetExtension, String renditionName) { - String debug; - + RenditionDefinitionRegistry2Impl renditionDefinitionRegistry2 = getRenditionDefinitionRegistry2Impl(); + String targetMimetype = getMimetype(targetExtension, false); String sourceMimetype = getMimetype(sourceExtension, true); - URL sourceURL = loadQuickTestFile(sourceExtension); - if (sourceURL == null) - { - throw new IllegalArgumentException("There is no test file with a "+sourceExtension+" extension."); - } - - // This URL may point to a file on the filesystem or to an entry in a jar. - // To use the transform method below, we need the content to be accessible from a ContentReader. - // This is possible for a File but not a (non-file) URL. - // - // Therefore, we'll always copy the URL content to a temporary local file. - final File sourceFile = TempFileProvider.createTempFile(TransformerDebug.class.getSimpleName() + "-tmp-", ""); - - try { FileUtils.copyURLToFile(sourceURL, sourceFile); } - catch (IOException shouldNeverHappen) - { - // The sourceURL should always be readable as we're reading data that Alfresco is distributing. - // But just in case... - throw new IllegalArgumentException("Cannot read content of test file with a " + - sourceExtension + " extension.", shouldNeverHappen); - } - - ContentReader reader = new FileContentReader(sourceFile); - reader.setMimetype(sourceMimetype); File tempFile = TempFileProvider.createTempFile( "TestTransform_" + sourceExtension + "_", "." + targetExtension); ContentWriter writer = new FileContentWriter(tempFile); writer.setMimetype(targetMimetype); - long sourceSize = reader.getSize(); - TransformationOptions options = new TransformationOptions(); - options.setUse(use); - - debug = isTransformable(sourceMimetype, sourceSize, targetMimetype, options); - if (debug == null) + String testRenditionName = "testTransform"+System.currentTimeMillis(); + NodeRef sourceNodeRef = null; + StringBuilder sb = new StringBuilder(); + try { - StringBuilder sb = new StringBuilder(); - try + setStringBuilder(sb); + RenditionDefinition2 renditionDefinition = new RenditionDefinition2Impl(testRenditionName, targetMimetype, + Collections.emptyMap(), renditionDefinitionRegistry2); + + sourceNodeRef = createSourceNode(sourceExtension, sourceMimetype); + ContentData contentData = (ContentData) nodeService.getProperty(sourceNodeRef, ContentModel.PROP_CONTENT); + if (contentData != null) { - setStringBuilder(sb); - transform(reader, writer, options); + String contentUrl = contentData.getContentUrl(); + if (contentUrl != null) + { + long size = contentData.getSize(); + int sourceContentHashCode = SOURCE_HAS_NO_CONTENT; + String contentString = contentData.getContentUrl()+contentData.getMimetype(); + if (contentString != null) + { + sourceContentHashCode = contentString.hashCode(); + } + + TransformClient transformClient = getTransformClient(); + transformClient.checkSupported(sourceNodeRef, renditionDefinition, sourceMimetype, size, contentUrl); + transformClient.transform(sourceNodeRef, renditionDefinition, "testTransform", sourceContentHashCode); + } } - catch (AlfrescoRuntimeException e) - { - sb.append(e.getMessage()); - } - finally - { - setStringBuilder(null); - } - debug = sb.toString(); } - return debug; + finally + { + setStringBuilder(null); + renditionDefinitionRegistry2.unregister(testRenditionName); + deleteSourceNode(sourceNodeRef); + } + return sb.toString(); } private String getMimetype(String extension, boolean isSource) @@ -1581,11 +1726,57 @@ public class TransformerDebug return mimetype; } - protected String isTransformable(String sourceMimetype, long sourceSize, String targetMimetype, TransformationOptions options) + public NodeRef createSourceNode(String extension, String sourceMimetype) { - return null; + // Create a content node which will serve as test data for our transformations. + RetryingTransactionHelper.RetryingTransactionCallback makeNodeCallback = new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + // Create a source node loaded with a quick file. + URL url = loadQuickTestFile(extension); + URI uri = url.toURI(); + File sourceFile = new File(uri); + + final NodeRef companyHome = getRepositoryHelper().getCompanyHome(); + + Map props = new HashMap(); + String localName = "TestTransform." + extension; + props.put(ContentModel.PROP_NAME, localName); + NodeRef node = nodeService.createNode( + companyHome, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, localName), + ContentModel.TYPE_CONTENT, + props).getChildRef(); + + ContentWriter writer = getContentService().getWriter(node, ContentModel.PROP_CONTENT, true); + writer.setMimetype(sourceMimetype); + writer.setEncoding("UTF-8"); + writer.putContent(sourceFile); + + return node; + } + }; + NodeRef contentNodeRef = getTransactionService().getRetryingTransactionHelper().doInTransaction(makeNodeCallback); + this.nodesToDeleteAfterTest.add(contentNodeRef); + return contentNodeRef; + } + + public void deleteSourceNode(NodeRef sourceNodeRef) + { + if (sourceNodeRef != null) + { + getTransactionService().getRetryingTransactionHelper().doInTransaction( + (RetryingTransactionHelper.RetryingTransactionCallback) () -> + { + if (nodeService.exists(sourceNodeRef)) + { + nodeService.deleteNode(sourceNodeRef); + } + return null; + }); + } } - - protected abstract void transform(ContentReader reader, ContentWriter writer, TransformationOptions options); } } diff --git a/src/main/java/org/alfresco/repo/content/transform/UnsupportedTransformationException.java b/src/main/java/org/alfresco/repo/content/transform/UnsupportedTransformationException.java index 6fcb7b2e2d..cf614de863 100644 --- a/src/main/java/org/alfresco/repo/content/transform/UnsupportedTransformationException.java +++ b/src/main/java/org/alfresco/repo/content/transform/UnsupportedTransformationException.java @@ -30,16 +30,12 @@ import org.alfresco.error.AlfrescoRuntimeException; /** * Exception indicates that a transformer is unable to transform a requested - * transformation. Normally the transformer is a component of a complex (compound) transformer - * and has been asked to transform a file that is too large (see transformation limits) as the + * transformation. Normally the transformer is a component of a pipeline transformer + * and has been asked to transform a file that is too large as the * size of the intermediate file is unknown at the start. * * @author Alan Davis - * - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. */ -@Deprecated -@AlfrescoPublicApi public class UnsupportedTransformationException extends AlfrescoRuntimeException { private static final long serialVersionUID = 9039331287661301086L; diff --git a/src/main/java/org/alfresco/repo/content/transform/magick/ImageMagickContentTransformerWorker.java b/src/main/java/org/alfresco/repo/content/transform/magick/ImageMagickContentTransformerWorker.java index b9779b16da..3a16537a78 100644 --- a/src/main/java/org/alfresco/repo/content/transform/magick/ImageMagickContentTransformerWorker.java +++ b/src/main/java/org/alfresco/repo/content/transform/magick/ImageMagickContentTransformerWorker.java @@ -81,12 +81,14 @@ public class ImageMagickContentTransformerWorker extends AbstractImageMagickCont /** the system command executer */ private RuntimeExec executer; + private boolean enabled = true; + /** the check command executer */ private RuntimeExec checkCommand; - + /** the output from the check command */ private String versionString; - + /** * Default constructor */ @@ -116,6 +118,11 @@ public class ImageMagickContentTransformerWorker extends AbstractImageMagickCont this.executer = executer; } + public void setEnabled(boolean enabled) + { + this.enabled = enabled; + } + /** * Sets the command that must be executed in order to retrieve version information from the converting executable * and thus test that the executable itself is present. @@ -127,7 +134,7 @@ public class ImageMagickContentTransformerWorker extends AbstractImageMagickCont { this.checkCommand = checkCommand; } - + /** * Gets the version string captured from the check command. * @@ -146,54 +153,57 @@ public class ImageMagickContentTransformerWorker extends AbstractImageMagickCont @Override public void afterPropertiesSet() { - if (!remoteTransformerClientConfigured() && executer == null) + if (enabled) { - throw new AlfrescoRuntimeException("System runtime executer not set"); - } - super.afterPropertiesSet(); - if (!remoteTransformerClientConfigured()) - { - if (isAvailable()) + if (!remoteTransformerClientConfigured() && executer == null) { - try - { - // On some platforms / versions, the -version command seems to return an error code whilst still - // returning output, so let's not worry about the exit code! - ExecutionResult result = this.checkCommand.execute(); - this.versionString = result.getStdOut().trim(); - } - catch (Throwable e) - { - setAvailable(false); - logger.error(getClass().getSimpleName() + " not available: " - + (e.getMessage() != null ? e.getMessage() : "")); - // debug so that we can trace the issue if required - logger.debug(e); - } + throw new AlfrescoRuntimeException("System runtime executer not set"); } - } - else - { - Pair result = remoteTransformerClient.check(logger); - Boolean isAvailable = result.getFirst(); - if (isAvailable != null && isAvailable) + super.afterPropertiesSet(); + if (!remoteTransformerClientConfigured()) { - setAvailable(true); - versionString = result.getSecond().trim(); - logger.info("Using remote ImageMagick: "+versionString); + if (isAvailable()) + { + try + { + // On some platforms / versions, the -version command seems to return an error code whilst still + // returning output, so let's not worry about the exit code! + ExecutionResult result = this.checkCommand.execute(); + this.versionString = result.getStdOut().trim(); + } + catch (Throwable e) + { + setAvailable(false); + logger.error(getClass().getSimpleName() + " not available: " + + (e.getMessage() != null ? e.getMessage() : "")); + // debug so that we can trace the issue if required + logger.debug(e); + } + } } else { - setAvailable(false); - versionString = "unknown"; - String message = "Remote ImageMagick is not available for transformations. " + result.getSecond(); - if (isAvailable == null) + Pair result = remoteTransformerClient.check(logger); + Boolean isAvailable = result.getFirst(); + if (isAvailable != null && isAvailable) { - logger.debug(message); + setAvailable(true); + versionString = result.getSecond().trim(); + logger.info("Using legacy local ImageMagick: " + versionString); } else { - logger.error(message); + setAvailable(false); + versionString = "unknown"; + String message = "Leacy remote ImageMagick is not available for transformations. " + result.getSecond(); + if (isAvailable == null) + { + logger.debug(message); + } + else + { + logger.error(message); + } } } } diff --git a/src/main/java/org/alfresco/repo/content/transform/pdfrenderer/AlfrescoPdfRendererContentTransformerWorker.java b/src/main/java/org/alfresco/repo/content/transform/pdfrenderer/AlfrescoPdfRendererContentTransformerWorker.java index 4c97cc2a9e..a722bb53b8 100644 --- a/src/main/java/org/alfresco/repo/content/transform/pdfrenderer/AlfrescoPdfRendererContentTransformerWorker.java +++ b/src/main/java/org/alfresco/repo/content/transform/pdfrenderer/AlfrescoPdfRendererContentTransformerWorker.java @@ -77,6 +77,8 @@ public class AlfrescoPdfRendererContentTransformerWorker extends ContentTransfor /** the system command executer */ private RuntimeExec executer; + private boolean enabled = true; + /** the check command executer */ private RuntimeExec checkCommand; @@ -110,6 +112,11 @@ public class AlfrescoPdfRendererContentTransformerWorker extends ContentTransfor this.executer = executer; } + public void setEnabled(boolean enabled) + { + this.enabled = enabled; + } + /** * Sets the optional remote transformer client which will be used in preference to a local command if available. * @@ -141,41 +148,44 @@ public class AlfrescoPdfRendererContentTransformerWorker extends ContentTransfor @Override public void afterPropertiesSet() { - PropertyCheck.mandatory(this, "executer", executer); - PropertyCheck.mandatory(this, "isAvailable", checkCommand); - // check availability - try + if (enabled) { - Pair result = remoteTransformerClientConfigured() - ? remoteTransformerClient.check(logger) - : remoteTransformerClient.check(checkCommand); - Boolean isAvailable = result.getFirst(); - if (isAvailable != null && isAvailable) + PropertyCheck.mandatory(this, "executer", executer); + PropertyCheck.mandatory(this, "isAvailable", checkCommand); + // check availability + try { - versionString = result.getSecond(); - setAvailable(true); - logger.info("Using remote Alfresco PDF Renderer: "+versionString); - } - else - { - setAvailable(false); - String message = "Remote Alfresco PDF Renderer is not available for transformations. " + result.getSecond(); - if (isAvailable == null) + Pair result = remoteTransformerClientConfigured() + ? remoteTransformerClient.check(logger) + : remoteTransformerClient.check(checkCommand); + Boolean isAvailable = result.getFirst(); + if (isAvailable != null && isAvailable) { - logger.debug(message); + versionString = result.getSecond(); + setAvailable(true); + logger.info("Using legacy local Alfresco PDF Renderer: " + versionString); } else { - logger.error(message); + setAvailable(false); + String message = "Legacy local Alfresco PDF Renderer is not available for transformations. " + result.getSecond(); + if (isAvailable == null) + { + logger.debug(message); + } + else + { + logger.error(message); + } } } - } - catch (Throwable e) - { - setAvailable(false); - logger.error("Remote Alfresco PDF Renderer is not available: " + (e.getMessage() != null ? e.getMessage() : "")); - // debug so that we can trace the issue if required - logger.debug(e); + catch (Throwable e) + { + setAvailable(false); + logger.error("Remote Alfresco PDF Renderer is not available: " + (e.getMessage() != null ? e.getMessage() : "")); + // debug so that we can trace the issue if required + logger.debug(e); + } } } diff --git a/src/main/java/org/alfresco/repo/rendition2/LegacyLocalTransformClient.java b/src/main/java/org/alfresco/repo/rendition2/LegacyTransformClient.java similarity index 94% rename from src/main/java/org/alfresco/repo/rendition2/LegacyLocalTransformClient.java rename to src/main/java/org/alfresco/repo/rendition2/LegacyTransformClient.java index dccfa89c70..c3bc3dafed 100644 --- a/src/main/java/org/alfresco/repo/rendition2/LegacyLocalTransformClient.java +++ b/src/main/java/org/alfresco/repo/rendition2/LegacyTransformClient.java @@ -37,6 +37,7 @@ import org.alfresco.service.transaction.TransactionService; 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.io.InputStream; import java.util.Map; @@ -52,9 +53,9 @@ import java.util.concurrent.Executors; * @author adavis */ @Deprecated -public class LegacyLocalTransformClient extends AbstractTransformClient implements TransformClient +public class LegacyTransformClient implements TransformClient, InitializingBean { - private static Log logger = LogFactory.getLog(LegacyLocalTransformClient.class); + private static Log logger = LogFactory.getLog(LegacyTransformClient.class); private TransactionService transactionService; @@ -94,7 +95,7 @@ public class LegacyLocalTransformClient extends AbstractTransformClient implemen @Override public void afterPropertiesSet() throws Exception { - super.afterPropertiesSet(); + PropertyCheck.mandatory(this, "transactionService", transactionService); PropertyCheck.mandatory(this, "contentService", contentService); PropertyCheck.mandatory(this, "renditionService2", renditionService2); PropertyCheck.mandatory(this, "converter", converter); @@ -144,7 +145,7 @@ public class LegacyLocalTransformClient extends AbstractTransformClient implemen TransformationOptions transformationOptions = converter.getTransformationOptions(renditionName, options); transformationOptions.setSourceNodeRef(sourceNodeRef); - ContentReader reader = LegacyLocalTransformClient.this.contentService.getReader(sourceNodeRef, ContentModel.PROP_CONTENT); + ContentReader reader = LegacyTransformClient.this.contentService.getReader(sourceNodeRef, ContentModel.PROP_CONTENT); if (null == reader || !reader.exists()) { throw new IllegalArgumentException("The supplied sourceNodeRef "+sourceNodeRef+" has no content."); diff --git a/src/main/java/org/alfresco/repo/rendition2/LegacyLocalTransformServiceRegistry.java b/src/main/java/org/alfresco/repo/rendition2/LegacyTransformServiceRegistry.java similarity index 82% rename from src/main/java/org/alfresco/repo/rendition2/LegacyLocalTransformServiceRegistry.java rename to src/main/java/org/alfresco/repo/rendition2/LegacyTransformServiceRegistry.java index 0ddd2e9993..18aaffcb9a 100644 --- a/src/main/java/org/alfresco/repo/rendition2/LegacyLocalTransformServiceRegistry.java +++ b/src/main/java/org/alfresco/repo/rendition2/LegacyTransformServiceRegistry.java @@ -41,7 +41,7 @@ import java.util.Map; * @author adavis */ @Deprecated -public class LegacyLocalTransformServiceRegistry extends AbstractTransformServiceRegistry implements InitializingBean +public class LegacyTransformServiceRegistry extends AbstractTransformServiceRegistry implements InitializingBean { private ContentService contentService; private TransformationOptionsConverter converter; @@ -85,14 +85,21 @@ public class LegacyLocalTransformServiceRegistry extends AbstractTransformServic if (firstTime) { firstTime = false; - transformerDebug.debug("Local legacy transformers are " + (enabled ? "enabled" : "disabled")); + transformerDebug.debug("Legacy transforms are " + (enabled ? "enabled" : "disabled")); } long maxSize = 0; if (enabled) { - TransformationOptions transformationOptions = converter.getTransformationOptions(renditionName, options); - maxSize = contentService.getMaxSourceSizeBytes(sourceMimetype, targetMimetype, transformationOptions); + try + { + TransformationOptions transformationOptions = converter.getTransformationOptions(renditionName, options); + maxSize = contentService.getMaxSourceSizeBytes(sourceMimetype, targetMimetype, transformationOptions); + } + catch (IllegalArgumentException ignore) + { + // Typically if the mimetype is invalid. + } } return maxSize; } diff --git a/src/main/java/org/alfresco/repo/rendition2/LocalTransformClient.java b/src/main/java/org/alfresco/repo/rendition2/LocalTransformClient.java new file mode 100644 index 0000000000..1e7a81b96f --- /dev/null +++ b/src/main/java/org/alfresco/repo/rendition2/LocalTransformClient.java @@ -0,0 +1,177 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 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.model.ContentModel; +import org.alfresco.repo.content.transform.LocalTransformer; +import org.alfresco.repo.content.transform.LocalTransformServiceRegistry; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +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.transaction.TransactionService; +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.io.InputStream; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * Requests rendition transforms take place using transforms available on the local machine (based on + * {@link LocalTransformer}. The transform and consumption of the + * resulting content is linked into a single operation that will take place at some point in the future on the local + * machine. + * + * @author adavis + */ +public class LocalTransformClient implements TransformClient, InitializingBean +{ + private static Log logger = LogFactory.getLog(LocalTransformClient.class); + + private LocalTransformServiceRegistry localTransformServiceRegistry; + private TransactionService transactionService; + private ContentService contentService; + private RenditionService2Impl renditionService2; + + private ExecutorService executorService; + + public void setLocalTransformServiceRegistry(LocalTransformServiceRegistry localTransformServiceRegistry) + { + this.localTransformServiceRegistry = localTransformServiceRegistry; + } + + public void setTransactionService(TransactionService transactionService) + { + this.transactionService = transactionService; + } + + public void setContentService(ContentService contentService) + { + this.contentService = contentService; + } + + public void setRenditionService2(RenditionService2Impl renditionService2) + { + this.renditionService2 = renditionService2; + } + + public void setExecutorService(ExecutorService executorService) + { + this.executorService = executorService; + } + + @Override + public void afterPropertiesSet() throws Exception + { + PropertyCheck.mandatory(this, "localTransformServiceRegistry", localTransformServiceRegistry); + PropertyCheck.mandatory(this, "transactionService", transactionService); + PropertyCheck.mandatory(this, "contentService", contentService); + PropertyCheck.mandatory(this, "renditionService2", renditionService2); + if (executorService == null) + { + executorService = Executors.newCachedThreadPool(); + } + } + + @Override + public void checkSupported(NodeRef sourceNodeRef, RenditionDefinition2 renditionDefinition, String sourceMimetype, long size, String contentUrl) + { + String targetMimetype = renditionDefinition.getTargetMimetype(); + String renditionName = renditionDefinition.getRenditionName(); + + Map options = renditionDefinition.getTransformOptions(); + if (!localTransformServiceRegistry.isSupported(sourceMimetype, size, targetMimetype, options, renditionName)) + { + String message = "Unsupported rendition " + renditionName + " from " + sourceMimetype + " size: " + size; + logger.debug(message); + throw new UnsupportedOperationException(message); + } + + if (logger.isDebugEnabled()) + { + logger.debug("Rendition of " + renditionName + " from " + sourceMimetype + " will use local transforms"); + } + } + + @Override + public void transform(NodeRef sourceNodeRef, RenditionDefinition2 renditionDefinition, String user, int sourceContentHashCode) + { + executorService.submit(() -> + { + AuthenticationUtil.runAs((AuthenticationUtil.RunAsWork) () -> + transactionService.getRetryingTransactionHelper().doInTransaction(() -> + { + try + { + String targetMimetype = renditionDefinition.getTargetMimetype(); + String renditionName = renditionDefinition.getRenditionName(); + Map options = renditionDefinition.getTransformOptions(); + + ContentReader reader = contentService.getReader(sourceNodeRef, ContentModel.PROP_CONTENT); + if (null == reader || !reader.exists()) + { + throw new IllegalArgumentException("The supplied sourceNodeRef "+sourceNodeRef+" has no content."); + } + + ContentWriter writer = contentService.getTempWriter(); + writer.setMimetype(targetMimetype); + if (logger.isDebugEnabled()) + { + logger.debug("Local transform requested for rendition of " + renditionDefinition.getRenditionName()); + } + localTransformServiceRegistry.transform(reader, writer, options, renditionName, sourceNodeRef); + + InputStream inputStream = writer.getReader().getContentInputStream(); + if (logger.isDebugEnabled()) + { + logger.debug("Local transform to be consumed for rendition of " + renditionDefinition.getRenditionName()); + } + renditionService2.consume(sourceNodeRef, inputStream, renditionDefinition, sourceContentHashCode); + if (logger.isDebugEnabled()) + { + logger.debug("Local transform consumed for rendition of " + renditionDefinition.getRenditionName()); + } + } + catch (Exception e) + { + if (logger.isDebugEnabled()) + { + String renditionName = renditionDefinition.getRenditionName(); + logger.error("Rendition of "+renditionName+" failed", e); + } + renditionService2.failure(sourceNodeRef, renditionDefinition, sourceContentHashCode); + throw e; + } + return null; + }), user); + }); + } +} diff --git a/src/main/java/org/alfresco/repo/rendition2/RenditionDefinitionRegistry2Impl.java b/src/main/java/org/alfresco/repo/rendition2/RenditionDefinitionRegistry2Impl.java index 12e9f92f18..5890440539 100644 --- a/src/main/java/org/alfresco/repo/rendition2/RenditionDefinitionRegistry2Impl.java +++ b/src/main/java/org/alfresco/repo/rendition2/RenditionDefinitionRegistry2Impl.java @@ -49,16 +49,18 @@ public class RenditionDefinitionRegistry2Impl implements RenditionDefinitionRegi public void setTransformServiceRegistry(TransformServiceRegistry transformServiceRegistry) { this.transformServiceRegistry = transformServiceRegistry; + renditionsFor.clear(); } /** * Obtains a {@link RenditionDefinition2} by name. * @param renditionName to be returned * @return the {@link RenditionDefinition2} or null if not registered. + * @deprecated use {@link #getRenditionDefinition(String)} */ public RenditionDefinition2 getDefinition(String renditionName) { - return renditionDefinitions.get(renditionName); + return getRenditionDefinition(renditionName); } public void register(RenditionDefinition2 renditionDefinition) diff --git a/src/main/java/org/alfresco/repo/rendition2/SwitchingTransformClient.java b/src/main/java/org/alfresco/repo/rendition2/SwitchingTransformClient.java index d1f66aba76..65038a476a 100644 --- a/src/main/java/org/alfresco/repo/rendition2/SwitchingTransformClient.java +++ b/src/main/java/org/alfresco/repo/rendition2/SwitchingTransformClient.java @@ -38,7 +38,7 @@ public class SwitchingTransformClient implements TransformClient { private final TransformClient primary; private final TransformClient secondary; - private ThreadLocal usePrimary = new ThreadLocal<>(); + private ThreadLocal usePrimary = ThreadLocal.withInitial(()->Boolean.FALSE); public SwitchingTransformClient(TransformClient primary, TransformClient secondary) { diff --git a/src/main/java/org/alfresco/repo/thumbnail/ThumbnailRegistry.java b/src/main/java/org/alfresco/repo/thumbnail/ThumbnailRegistry.java index 7e6f2a610f..1c712cf0ee 100644 --- a/src/main/java/org/alfresco/repo/thumbnail/ThumbnailRegistry.java +++ b/src/main/java/org/alfresco/repo/thumbnail/ThumbnailRegistry.java @@ -166,6 +166,7 @@ public class ThumbnailRegistry implements ApplicationContextAware, ApplicationLi public void setTransformServiceRegistry(TransformServiceRegistry transformServiceRegistry) { this.transformServiceRegistry = transformServiceRegistry; + mimetypeMap.clear(); } public void setRenditionDefinitionRegistry2(RenditionDefinitionRegistry2 renditionDefinitionRegistry2) diff --git a/src/main/java/org/alfresco/transform/client/model/config/ExtensionMap.java b/src/main/java/org/alfresco/transform/client/model/config/AbstractTransformOption.java similarity index 72% rename from src/main/java/org/alfresco/transform/client/model/config/ExtensionMap.java rename to src/main/java/org/alfresco/transform/client/model/config/AbstractTransformOption.java index a8f91d0c00..5566128979 100644 --- a/src/main/java/org/alfresco/transform/client/model/config/ExtensionMap.java +++ b/src/main/java/org/alfresco/transform/client/model/config/AbstractTransformOption.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 @@ -26,10 +26,21 @@ package org.alfresco.transform.client.model.config; /** - * Helper class to be supplied by the client to map file extensions to mimetypes, so the json description of what - * is supported by the Transform Service includes file extensions rather than mmetypes, so is more readable. + * Abstract implementation of TransformOption. */ -public interface ExtensionMap +public abstract class AbstractTransformOption implements TransformOption { - String toMimetype(String extension); + private boolean required; + + @Override + public boolean isRequired() + { + return required; + } + + @Override + public void setRequired(boolean required) + { + this.required = required; + } } diff --git a/src/main/java/org/alfresco/transform/client/model/config/JsonConverter.java b/src/main/java/org/alfresco/transform/client/model/config/JsonConverter.java new file mode 100644 index 0000000000..465b353971 --- /dev/null +++ b/src/main/java/org/alfresco/transform/client/model/config/JsonConverter.java @@ -0,0 +1,273 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 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.transform.client.model.config; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.apache.commons.logging.Log; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +/** + * This class recreates the json format used in ACS 6.1 where we just had an array of transformers and each + * transformer has a list of transform options. The idea of this code is that it replaces the references with the + * actual transform options that have been separated out into their own section.

+ * + * The T-Router and T-Engines return the format with the transform option separated into their own section. Pipeline + * definitions used by the LocalTransformServiceRegistry may use transform reference options defined in the json + * returned by T-Engines. with the actual definitions from the transform options + * reference section. It also combines multiple json sources into a single jsonNode structure that can be parsed as + * before. + */ +public class JsonConverter +{ + public static final String NAME = "name"; + private final Log log; + + private static final String TRANSFORM_OPTIONS = "transformOptions"; + private static final String GROUP = "group"; + private static final String TRANSFORMERS = "transformers"; + + private Map allTransformOptions = new HashMap<>(); + private List allTransforms = new ArrayList<>(); + private ObjectMapper jsonObjectMapper = new ObjectMapper(); + + JsonConverter(Log log) + { + this.log = log; + } + + void addJsonSource(String path) throws IOException + { + final File jarFile = new File(getClass().getProtectionDomain().getCodeSource().getLocation().getPath()); + if (jarFile.isFile()) + { + JarFile jar = new JarFile(jarFile); + Enumeration entries = jar.entries(); //gives ALL entries in jar + String prefix = path + "/"; + List names = new ArrayList<>(); + while (entries.hasMoreElements()) + { + final String name = entries.nextElement().getName(); + if (name.startsWith(prefix) && name.length() > prefix.length()) + { + names.add(name); + } + } + Collections.sort(names); + for (String name : names) + { + log.debug("Reading resource "+name); + addJsonSource(new InputStreamReader(getResourceAsStream(name))); + } + + jar.close(); + } + else + { + URL url = getClass().getClassLoader().getResource(path); + if (url != null) + { + File root = new File(url.getPath()); + if (root.isDirectory()) + { + File[] files = root.listFiles(); + Arrays.sort(files, (file1, file2) -> file1.getName().compareTo(file2.getName())); + for (File file: files) + { + log.debug("Reading dir file "+file.getPath()); + addJsonSource(new FileReader(file)); + } + } + else + { + log.debug("Reading file "+root.getPath()); + addJsonSource(new FileReader(root)); + } + } + } + } + + private InputStream getResourceAsStream(String resource) + { + final InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource); + return in == null ? getClass().getResourceAsStream(resource) : in; + } + + private void addJsonSource(Reader reader) throws IOException + { + JsonNode jsonNode = jsonObjectMapper.readValue(reader, new TypeReference() {}); + + JsonNode transformOptions = jsonNode.get(TRANSFORM_OPTIONS); + if (transformOptions != null && transformOptions.isObject()) + { + Iterator> iterator = transformOptions.fields(); + while (iterator.hasNext()) + { + Map.Entry entry = iterator.next(); + + JsonNode options = entry.getValue(); + if (options.isArray()) + { + String optionsName = entry.getKey(); + allTransformOptions.put(optionsName, (ArrayNode)options); + } + } + } + + JsonNode transformers = jsonNode.get(TRANSFORMERS); + if (transformers != null && transformers.isArray()) + { + for (JsonNode transformer : transformers) + { + if (transformer.isObject()) + { + allTransforms.add((ObjectNode)transformer); + } + } + } + } + + public List getTransformers() throws IOException + { + List transformers = new ArrayList<>(); + + // After all json input has been loaded build the output with the options in place. + ArrayNode transformersNode = jsonObjectMapper.createArrayNode(); + for (ObjectNode transform : allTransforms) + { + try + { + ArrayNode transformOptions = (ArrayNode) transform.get(TRANSFORM_OPTIONS); + if (transformOptions != null) + { + + ArrayNode options; + int size = transformOptions.size(); + if (size == 1) + { + // If there is a single transform option reference, we can just use it. + int i = 0; + options = getTransformOptions(transformOptions, i, transform); + } + else + { + // If there are many transform option references (typically in a pipeline), then each element + // has a group for each set of transform options. + options = jsonObjectMapper.createArrayNode(); + for (int i = size - 1; i >= 0; i--) + { + JsonNode referencedTransformOptions = getTransformOptions(transformOptions, i, transform); + if (referencedTransformOptions != null) + { + ObjectNode element = jsonObjectMapper.createObjectNode(); + options.add(element); + + ObjectNode group = jsonObjectMapper.createObjectNode(); + group.set(TRANSFORM_OPTIONS, referencedTransformOptions); + element.set(GROUP, group); + } + } + } + if (options == null || options.size() == 0) + { + transform.remove(TRANSFORM_OPTIONS); + } + else + { + transform.set(TRANSFORM_OPTIONS, options); + } + } + + try + { + Transformer transformer = jsonObjectMapper.convertValue(transform, Transformer.class); + transformers.add(transformer); + } + catch (IllegalArgumentException e) + { + log.error("Invalid transformer "+getTransformName(transform)+" "+e.getMessage()); + } + transformersNode.add(transform); + } + catch (IllegalArgumentException e) + { + String transformString = jsonObjectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(transform); + log.error(e.getMessage()+"\n"+ transformString); + } + } + + return transformers; + } + + private ArrayNode getTransformOptions(ArrayNode transformOptions, int i, ObjectNode transform) + { + ArrayNode options = null; + JsonNode optionName = transformOptions.get(i); + if (optionName.isTextual()) + { + String name = optionName.asText(); + options = allTransformOptions.get(name); + if (options == null) + { + String message = "Reference to \"transformOptions\": \"" + name + "\" not found. Transformer " + + getTransformName(transform) + " ignored."; + throw new IllegalArgumentException(message); + } + } + return options; + } + + private String getTransformName(ObjectNode transform) + { + String name = "Unknown"; + JsonNode nameNode = transform.get(NAME); + if (nameNode.isTextual()) + { + name = '"'+nameNode.asText()+'"'; + } + return name; + } +} diff --git a/src/main/java/org/alfresco/transform/client/model/config/SupportedSourceAndTarget.java b/src/main/java/org/alfresco/transform/client/model/config/SupportedSourceAndTarget.java index e27c29124d..2f9860bea9 100644 --- a/src/main/java/org/alfresco/transform/client/model/config/SupportedSourceAndTarget.java +++ b/src/main/java/org/alfresco/transform/client/model/config/SupportedSourceAndTarget.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 @@ -31,29 +31,36 @@ package org.alfresco.transform.client.model.config; */ public class SupportedSourceAndTarget { - private String sourceExt; + private String sourceMediaType; private long maxSourceSizeBytes = -1; - private String targetExt; + private String targetMediaType; + private int priority = 50; public SupportedSourceAndTarget() { } - public SupportedSourceAndTarget(String sourceExt, String targetExt, long maxSourceSizeBytes) + public SupportedSourceAndTarget(String sourceMediaType, String targetMediaType, long maxSourceSizeBytes) { - setSourceExt(sourceExt); + this(sourceMediaType, targetMediaType, maxSourceSizeBytes, 50); + } + + public SupportedSourceAndTarget(String sourceMediaType, String targetMediaType, long maxSourceSizeBytes, int priority) + { + setSourceMediaType(sourceMediaType); setMaxSourceSizeBytes(maxSourceSizeBytes); - setTargetExt(targetExt); + setTargetMediaType(targetMediaType); + setPriority(priority); } - public String getSourceExt() + public String getSourceMediaType() { - return sourceExt; + return sourceMediaType; } - public void setSourceExt(String sourceExt) + public void setSourceMediaType(String sourceMediaType) { - this.sourceExt = sourceExt; + this.sourceMediaType = sourceMediaType; } public long getMaxSourceSizeBytes() @@ -66,13 +73,23 @@ public class SupportedSourceAndTarget this.maxSourceSizeBytes = maxSourceSizeBytes; } - public String getTargetExt() + public String getTargetMediaType() { - return targetExt; + return targetMediaType; } - public void setTargetExt(String targetExt) + public void setTargetMediaType(String targetMediaType) { - this.targetExt = targetExt; + this.targetMediaType = targetMediaType; + } + + public int getPriority() + { + return priority; + } + + public void setPriority(int priority) + { + this.priority = priority; } } diff --git a/src/main/java/org/alfresco/transform/client/model/config/TransformOption.java b/src/main/java/org/alfresco/transform/client/model/config/TransformOption.java index 85dba88f06..dc73be1551 100644 --- a/src/main/java/org/alfresco/transform/client/model/config/TransformOption.java +++ b/src/main/java/org/alfresco/transform/client/model/config/TransformOption.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 diff --git a/src/main/java/org/alfresco/transform/client/model/config/TransformOptionGroup.java b/src/main/java/org/alfresco/transform/client/model/config/TransformOptionGroup.java index 83919c3ec0..a0ad82c951 100644 --- a/src/main/java/org/alfresco/transform/client/model/config/TransformOptionGroup.java +++ b/src/main/java/org/alfresco/transform/client/model/config/TransformOptionGroup.java @@ -30,11 +30,12 @@ import java.util.List; /** * Represents a group of one or more options. If the group is optional, child options that are marked as required are * only required if any child in the group is supplied by the client. If the group is required, child options are - * optional or required based on their own setting alone. The top + * optional or required based on their own setting alone. + * + * In a pipeline transformation, a group of options */ -public class TransformOptionGroup implements TransformOption +public class TransformOptionGroup extends AbstractTransformOption { - private boolean required; List transformOptions; public TransformOptionGroup() @@ -47,18 +48,6 @@ public class TransformOptionGroup implements TransformOption setTransformOptions(transformOptions); } - @Override - public boolean isRequired() - { - return required; - } - - @Override - public void setRequired(boolean required) - { - this.required = required; - } - public List getTransformOptions() { return transformOptions; diff --git a/src/main/java/org/alfresco/transform/client/model/config/TransformOptionValue.java b/src/main/java/org/alfresco/transform/client/model/config/TransformOptionValue.java index 6f40377109..6962162175 100644 --- a/src/main/java/org/alfresco/transform/client/model/config/TransformOptionValue.java +++ b/src/main/java/org/alfresco/transform/client/model/config/TransformOptionValue.java @@ -28,9 +28,8 @@ package org.alfresco.transform.client.model.config; /** * Represents a single transformation option. */ -public class TransformOptionValue implements TransformOption +public class TransformOptionValue extends AbstractTransformOption { - private boolean required; private String name; public TransformOptionValue() @@ -43,18 +42,6 @@ public class TransformOptionValue implements TransformOption setName(name); } - @Override - public boolean isRequired() - { - return required; - } - - @Override - public void setRequired(boolean required) - { - this.required = required; - } - public String getName() { return name; diff --git a/src/main/java/org/alfresco/transform/client/model/config/TransformServiceRegistryImpl.java b/src/main/java/org/alfresco/transform/client/model/config/TransformServiceRegistryImpl.java index b76f548fe0..8584394cd1 100644 --- a/src/main/java/org/alfresco/transform/client/model/config/TransformServiceRegistryImpl.java +++ b/src/main/java/org/alfresco/transform/client/model/config/TransformServiceRegistryImpl.java @@ -27,6 +27,7 @@ package org.alfresco.transform.client.model.config; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.logging.Log; import org.springframework.beans.factory.InitializingBean; import java.io.IOException; @@ -44,38 +45,36 @@ import static org.alfresco.repo.rendition2.RenditionDefinition2.TIMEOUT; /** * Used by clients to work out if a transformation is supported by the Transform Service. */ -public class TransformServiceRegistryImpl implements TransformServiceRegistry, InitializingBean +public abstract class TransformServiceRegistryImpl implements TransformServiceRegistry, InitializingBean { class SupportedTransform { TransformOptionGroup transformOptions; - private long maxSourceSizeBytes; + long maxSourceSizeBytes; + private String name; + private int priority; - public SupportedTransform(List transformOptions, long maxSourceSizeBytes) + public SupportedTransform(String name, List transformOptions, long maxSourceSizeBytes, int priority) { // Logically the top level TransformOptionGroup is required, so that child options are optional or required // based on their own setting. this.transformOptions = new TransformOptionGroup(true, transformOptions); this.maxSourceSizeBytes = maxSourceSizeBytes; + this.name = name; + this.priority = priority; } } private ObjectMapper jsonObjectMapper; - private ExtensionMap extensionMap; ConcurrentMap>> transformers = new ConcurrentHashMap<>(); - ConcurrentMap> cachedMaxSizes = new ConcurrentHashMap<>(); + ConcurrentMap>> cachedSupportedTransformList = new ConcurrentHashMap<>(); public void setJsonObjectMapper(ObjectMapper jsonObjectMapper) { this.jsonObjectMapper = jsonObjectMapper; } - public void setExtensionMap(ExtensionMap extensionMap) - { - this.extensionMap = extensionMap; - } - @Override public void afterPropertiesSet() throws Exception { @@ -83,30 +82,21 @@ public class TransformServiceRegistryImpl implements TransformServiceRegistry, I { throw new IllegalStateException("jsonObjectMapper has not been set"); } - if (extensionMap == null) - { - throw new IllegalStateException("extensionMap has not been set"); - } + cachedSupportedTransformList.clear(); + transformers.clear(); } - private String toMimetype(String ext) - { - String mimetype = extensionMap.toMimetype(ext); - if (mimetype == null) - { - throw new IllegalArgumentException("The mimetype for the file extension "+ext+" cannot be looked up by: "+ - extensionMap.getClass().getName()); - } - return mimetype; - } + protected abstract Log getLog(); - public void register(Transformer transformer) + public void register(String path) throws IOException { - transformer.getSupportedSourceAndTargetList().forEach( - e -> transformers.computeIfAbsent(toMimetype(e.getSourceExt()), - k -> new ConcurrentHashMap<>()).computeIfAbsent(toMimetype(e.getTargetExt()), - k -> new ArrayList<>()).add( - new SupportedTransform(transformer.getTransformOptions(), e.getMaxSourceSizeBytes()))); + JsonConverter jsonConverter = new JsonConverter(getLog()); + jsonConverter.addJsonSource(path); + List transformers = jsonConverter.getTransformers(); + for (Transformer transformer : transformers) + { + register(transformer); + } } public void register(Reader reader) throws IOException @@ -115,31 +105,78 @@ public class TransformServiceRegistryImpl implements TransformServiceRegistry, I transformers.forEach(t -> register(t)); } + public void register(Transformer transformer) + { + transformer.getSupportedSourceAndTargetList().forEach( + e -> transformers.computeIfAbsent(e.getSourceMediaType(), + k -> new ConcurrentHashMap<>()).computeIfAbsent(e.getTargetMediaType(), + k -> new ArrayList<>()).add( + new SupportedTransform(transformer.getTransformerName(), + transformer.getTransformOptions(), e.getMaxSourceSizeBytes(), e.getPriority()))); + } + @Override public boolean isSupported(String sourceMimetype, long sourceSizeInBytes, String targetMimetype, - Map actualOptions, String transformName) + Map actualOptions, String renditionName) { - long maxSize = getMaxSize(sourceMimetype, targetMimetype, actualOptions, transformName); + long maxSize = getMaxSize(sourceMimetype, targetMimetype, actualOptions, renditionName); return maxSize != 0 && (maxSize == -1L || maxSize >= sourceSizeInBytes); } + /** + * Works out the name of the transformer (might not map to an actual transformer) that will be used 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 {@Transform} objects registered with this class. + * @param sourceMimetype the mimetype of the source content + * @param sourceSizeInBytes the size in bytes of the source content. Ignored if negative. + * @param targetMimetype the mimetype of the target + * @param actualOptions 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. + */ + protected String getTransformerName(String sourceMimetype, long sourceSizeInBytes, String targetMimetype, Map actualOptions, String renditionName) + { + List supportedTransforms = getTransformListBySize(sourceMimetype, targetMimetype, actualOptions, renditionName); + for (SupportedTransform supportedTransform : supportedTransforms) + { + if (supportedTransform.maxSourceSizeBytes == -1 || supportedTransform.maxSourceSizeBytes >= sourceSizeInBytes) + { + return supportedTransform.name; + } + } + + return null; + } + @Override public long getMaxSize(String sourceMimetype, String targetMimetype, - Map actualOptions, String transformName) + Map actualOptions, String renditionName) + { + List supportedTransforms = getTransformListBySize(sourceMimetype, targetMimetype, actualOptions, renditionName); + return supportedTransforms.isEmpty() ? 0 : supportedTransforms.get(supportedTransforms.size()-1).maxSourceSizeBytes; + } + + // Returns transformers in increasing supported size order, where lower priority transformers for the same size have + // been discarded. + private List getTransformListBySize(String sourceMimetype, String targetMimetype, + Map actualOptions, String renditionName) { if (actualOptions == null) { actualOptions = Collections.EMPTY_MAP; } - if (transformName != null && transformName.trim().isEmpty()) + if (renditionName != null && renditionName.trim().isEmpty()) { - transformName = null; + renditionName = null; } - Long maxSize = transformName == null ? null : cachedMaxSizes.computeIfAbsent(transformName, k -> new ConcurrentHashMap<>()).get(sourceMimetype); - if (maxSize != null) + List transformListBySize = renditionName == null ? null + : cachedSupportedTransformList.computeIfAbsent(renditionName, k -> new ConcurrentHashMap<>()).get(sourceMimetype); + if (transformListBySize != null) { - return maxSize.longValue(); + return transformListBySize; } // Remove the "timeout" property from the actualOptions as it is not used to select a transformer. @@ -149,7 +186,7 @@ public class TransformServiceRegistryImpl implements TransformServiceRegistry, I actualOptions.remove(TIMEOUT); } - long calculatedMaxSize = 0; + transformListBySize = new ArrayList<>(); ConcurrentMap> targetMap = transformers.get(sourceMimetype); if (targetMap != null) { @@ -163,24 +200,66 @@ public class TransformServiceRegistryImpl implements TransformServiceRegistry, I addToPossibleTransformOptions(possibleTransformOptions, transformOptions, true, actualOptions); if (isSupported(possibleTransformOptions, actualOptions)) { - if (supportedTransform.maxSourceSizeBytes < 0) - { - calculatedMaxSize = -1; - break; - } - - calculatedMaxSize = Math.max(calculatedMaxSize, supportedTransform.maxSourceSizeBytes); + addToSupportedTransformList(transformListBySize, supportedTransform); } } } } - if (transformName != null) + if (renditionName != null) { - cachedMaxSizes.get(transformName).put(sourceMimetype, calculatedMaxSize); + cachedSupportedTransformList.get(renditionName).put(sourceMimetype, transformListBySize); } - return calculatedMaxSize; + return transformListBySize; + } + + // Add newTransform to the transformListBySize in increasing size order and discards lower priority (numerically + // higher) transforms with a smaller or equal size. + private void addToSupportedTransformList(List transformListBySize, SupportedTransform newTransform) + { + for (int i=0; i < transformListBySize.size(); i++) + { + SupportedTransform existingTransform = transformListBySize.get(i); + int added = -1; + int compare = compare(newTransform.maxSourceSizeBytes, existingTransform.maxSourceSizeBytes); + if (compare < 0) + { + transformListBySize.add(i, newTransform); + added = i; + } + else if (compare == 0) + { + if (newTransform.priority < existingTransform.priority) + { + transformListBySize.set(i, newTransform); + added = i; + } + } + if (added == i) + { + for (i--; i >= 0; i--) + { + existingTransform = transformListBySize.get(i); + if (newTransform.priority <= existingTransform.priority) + { + transformListBySize.remove(i); + } + } + return; + } + } + transformListBySize.add(newTransform); + } + + // compare where -1 is unlimited. + private int compare(long a, long b) + { + return a == -1 + ? b == -1 ? 0 : 1 + : b == -1 ? -1 + : a == b ? 0 + : a > b ? 1 : -1; } /** diff --git a/src/main/java/org/alfresco/transform/client/model/config/TransformStep.java b/src/main/java/org/alfresco/transform/client/model/config/TransformStep.java new file mode 100644 index 0000000000..cd2a543174 --- /dev/null +++ b/src/main/java/org/alfresco/transform/client/model/config/TransformStep.java @@ -0,0 +1,66 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 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.transform.client.model.config; + +/** + * Represents a single transform step in a transform pipeline. The last step in the pipeline does not specify the + * target type as that is based on the supported types and what has been requested. + */ +public class TransformStep +{ + private String transformerName; + private String targetMediaType; + + public TransformStep() + { + } + + public TransformStep(String transformerName, String targetMediaType) + { + setTransformerName(transformerName); + setTargetMediaType(targetMediaType); + } + + public String getTransformerName() + { + return transformerName; + } + + public void setTransformerName(String transformerName) + { + this.transformerName = transformerName; + } + + public String getTargetMediaType() + { + return targetMediaType; + } + + public void setTargetMediaType(String targetMediaType) + { + this.targetMediaType = targetMediaType; + } +} diff --git a/src/main/java/org/alfresco/transform/client/model/config/Transformer.java b/src/main/java/org/alfresco/transform/client/model/config/Transformer.java index 3f18a8bf8f..f707d1cc95 100644 --- a/src/main/java/org/alfresco/transform/client/model/config/Transformer.java +++ b/src/main/java/org/alfresco/transform/client/model/config/Transformer.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 @@ -26,28 +26,34 @@ package org.alfresco.transform.client.model.config; import java.util.List; -import java.util.Objects; +import org.alfresco.transform.client.model.config.TransformServiceRegistry; /** - * Represents a set of transformations supported by the Transform Service that share the same transform options. Each - * may be an actual transformer or the amalgamation of multiple transformers. It is possible that more than one - * transformer may able to perform a transformation from one mimetype to another. The actual selection of transformer - * is up to the Transform Service to decide. Clients may use {@link TransformServiceRegistry#isSupported} to decide + * Represents a set of transformations supported by the Transform Service or Local Transform Service Registry that + * share the same transform options. Each may be an actual transformer or a pipeline of multiple transformers. It is + * possible that more than one transformer may able to perform a transformation from one mimetype to another. The actual + * selection of transformer is up to the Transform Service or Local Transform Service Registry to decide. Clients may + * use {@link TransformServiceRegistry#isSupported(String, long, String, java.util.Map, String)} to decide * if they should send a request to the Transform Service. As a result clients have a simple generic view of - * transformations which allows new transformations to be added without the need change client data structures other + * transformations which allows new transformations to be added without the need to change client data structures other * than to define new name value pairs. For this to work the Transform Service defines unique names for each option. *

    - *
  • name - is unique. The client should infer nothing from the name as it is simply a label.
  • - *
  • version - of the transformer. The client should infer nothing from the value and should only use it - * in messages. There should only be one version supplied to the client for each name.
  • + *
  • transformerName - is optional but if supplied should be unique. The client should infer nothing from the name + * as it is simply a label, but the Local Transform Service Registry will use the name in pipelines.
  • *
  • transformOptions - a grouping of individual transformer transformOptions. The group may be optional and may * contain nested transformOptions.
  • *
+ * For local transforms, this structure is extended when defining a pipeline transform. + *
    + *
  • transformerPipeline - an array of pairs of transformer name and target extension for each transformer in the + * pipeline. The last one should not have an extension as that is defined by the request and should be in the + * supported list.
  • + *
*/ public class Transformer { - private String name; - private String version; + private String transformerName; + private List transformerPipeline; private List transformOptions; private List supportedSourceAndTargetList; @@ -55,32 +61,37 @@ public class Transformer { } - public Transformer(String name, String version, List transformOptions, List supportedSourceAndTargetList) + public Transformer(String transformerName, List transformOptions, List supportedSourceAndTargetList) { - setName(name); - setVersion(version); + setTransformerName(transformerName); setTransformOptions(transformOptions); setSupportedSourceAndTargetList(supportedSourceAndTargetList); } - public String getName() + public Transformer(String transformerName, List transformerPipeline, List transformOptions, List supportedSourceAndTargetList) { - return name; + this(transformerName, transformOptions, supportedSourceAndTargetList); + setTransformerPipeline(transformerPipeline); } - public void setName(String name) + public String getTransformerName() { - this.name = name; + return transformerName; } - public String getVersion() + public void setTransformerName(String transformerName) { - return version; + this.transformerName = transformerName; } - public void setVersion(String version) + public List getTransformerPipeline() { - this.version = version; + return transformerPipeline; + } + + public void setTransformerPipeline(List transformerPipeline) + { + this.transformerPipeline = transformerPipeline; } public List getTransformOptions() diff --git a/src/main/resources/alfresco/content-services-context.xml b/src/main/resources/alfresco/content-services-context.xml index 0be62cb8e1..086fe5e1d1 100644 --- a/src/main/resources/alfresco/content-services-context.xml +++ b/src/main/resources/alfresco/content-services-context.xml @@ -328,33 +328,18 @@ - + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + @@ -489,6 +474,7 @@ class="org.alfresco.repo.content.transform.TikaPoweredContentTransformer" abstract="true" parent="baseContentTransformer"> + diff --git a/src/main/resources/alfresco/rendition-services2-context.xml b/src/main/resources/alfresco/rendition-services2-context.xml index b1fd36d65c..19cb463faa 100644 --- a/src/main/resources/alfresco/rendition-services2-context.xml +++ b/src/main/resources/alfresco/rendition-services2-context.xml @@ -71,9 +71,20 @@ - + + + + + + + + + + + + + - @@ -91,10 +102,28 @@ - + + + + + + + + + + + + + + + + + + + - + diff --git a/src/main/resources/alfresco/repository.properties b/src/main/resources/alfresco/repository.properties index 03287403bc..632b6a3cfa 100644 --- a/src/main/resources/alfresco/repository.properties +++ b/src/main/resources/alfresco/repository.properties @@ -562,7 +562,7 @@ img.root=./ImageMagick img.dyn=${img.root}/lib img.exe=${img.root}/bin/convert -# Remote server (or docker container) url used to service imagemagick requests. +# Legacy imageMagick transformer url to T-Engine to service transform requests via http. Disabled by default. img.url= # When img.url is set, this value indicates the amount of time to wait after a connection failure @@ -611,13 +611,26 @@ system.thumbnail.redeployStaticDefsOnStartup=true # The default timeout for metadata mapping extracters content.metadataExtracter.default.timeoutMs=20000 -# Remote server (or docker container) url used to service tika requests. +# Legacy tika transformer url to T-Engines to service transform requests via http. Disabled by default. tika.url= -# When tika.url is set, this value indicates the amount of time to wait after a connection failure +# When the legacy tika transformer .url is set, this value indicates the amount of time to wait after a connection failure # before retrying the connection to allow a docker container to (re)start. tika.startupRetryPeriodSeconds=60 +# Local transformer urls to T-engines to service transform requests via http. Enabled by default. +localTransformer.pdfrenderer.url=http://localhost:8090/ +localTransformer.imagemagick.url=http://localhost:8091/ +localTransformer.libreoffice.url=http://localhost:8092/ +localTransformer.tika.url=http://localhost:8093/ + +# When a local transformer .url is set, this value indicates the amount of time to wait after a connection failure +# before retrying the connection to allow a docker container to (re)start. +localTransformer.pdfrenderer.startupRetryPeriodSeconds=60 +localTransformer.imagemagick.startupRetryPeriodSeconds=60 +localTransformer.libreoffice.startupRetryPeriodSeconds=60 +localTransformer.tika.startupRetryPeriodSeconds=60 + # content.metadataExtracter.pdf.maxDocumentSizeMB=10 content.metadataExtracter.pdf.maxConcurrentExtractionsCount=5 @@ -1065,6 +1078,9 @@ system.cronJob.startDelayMilliseconds=60000 # Used to disable transforms locally. local.transform.service.enabled=true +# Used to disable transforms that extend AbstractContentTransformer2 +legacy.transform.service.enabled=true + # # Check that the declared mimetype (of the Node) is the same as the derived # mimetype of the content (via Tika) before a transformation takes place. @@ -1074,13 +1090,15 @@ local.transform.service.enabled=true # # There are a few issues with the Tika mimetype detection. So that transformations # still take place where the detected mimetype is not the same as the declared mimetype, -# another property (transformer.strict.mimetype.check.whitelist.mimetypes in transformers.properties -# so can be changed via JMX) contains pairs of declared and detected mimetypes that should -# be allowed. This parameter value is a sequence of ; separated pairs. The declared and -# derived mimetypes are also ; separated. +# another property (transformer.strict.mimetype.check.whitelist.mimetypes) contains pairs +# of declared and detected mimetypes that should be allowed. This parameter value is a +# sequence of ; separated pairs. The declared and derived mimetypes are also ; separated. # transformer.strict.mimetype.check=true +# A white list of declared and detected mimetypes, that don't match, but should still be transformed. +transformer.strict.mimetype.check.whitelist.mimetypes=application/eps;application/postscript;application/illustrator;application/pdf;application/x-tar;application/x-gtar;application/acp;application/zip;application/vnd.stardivision.math;application/x-tika-msoffice + # # Enable transformation retrying if the file has MIME type differ than file extension. # Ignored if transformer.strict.mimetype.check is true as these transformations @@ -1257,5 +1275,4 @@ messaging.subsystem.autoStart=true # Raw events -acs.repo.rendition.events.endpoint=jms:acs-repo-rendition-events?jmsMessageType=Text - +acs.repo.rendition.events.endpoint=jms:acs-repo-rendition-events?jmsMessageType=Text \ No newline at end of file diff --git a/src/main/resources/alfresco/subsystems/OOoJodconverter/default/jodconverter-context.xml b/src/main/resources/alfresco/subsystems/OOoJodconverter/default/jodconverter-context.xml index ef73c93b5e..ac1235ce85 100644 --- a/src/main/resources/alfresco/subsystems/OOoJodconverter/default/jodconverter-context.xml +++ b/src/main/resources/alfresco/subsystems/OOoJodconverter/default/jodconverter-context.xml @@ -53,6 +53,7 @@ + diff --git a/src/main/resources/alfresco/subsystems/thirdparty/default/imagemagick-transform-context.xml b/src/main/resources/alfresco/subsystems/thirdparty/default/imagemagick-transform-context.xml index 30aaa7baa4..9e98320965 100644 --- a/src/main/resources/alfresco/subsystems/thirdparty/default/imagemagick-transform-context.xml +++ b/src/main/resources/alfresco/subsystems/thirdparty/default/imagemagick-transform-context.xml @@ -61,6 +61,7 @@ + diff --git a/src/main/resources/alfresco/transform-service-config.json b/src/main/resources/alfresco/transform-service-config.json index b3cfc347d4..0f9c392de7 100644 --- a/src/main/resources/alfresco/transform-service-config.json +++ b/src/main/resources/alfresco/transform-service-config.json @@ -1,58 +1,20 @@ -[ - { - "name": "libreOffice", - "version": "1", - "supportedSourceAndTargetList": [ - {"sourceExt": "doc", "targetExt": "doc" }, - {"sourceExt": "docx", "targetExt": "doc" }, - {"sourceExt": "doc", "maxSourceSizeBytes": 10485760, "targetExt": "pdf" }, - {"sourceExt": "xls", "maxSourceSizeBytes": 10485760, "targetExt": "pdf"}, - {"sourceExt": "ppt", "maxSourceSizeBytes": 6291456, "targetExt": "pdf" }, - {"sourceExt": "docx", "maxSourceSizeBytes": 786432, "targetExt": "pdf" }, - {"sourceExt": "xlsx", "maxSourceSizeBytes": 1572864, "targetExt": "pdf"}, - {"sourceExt": "pptx", "maxSourceSizeBytes": 4194304, "targetExt": "pdf" }, - {"sourceExt": "msg", "targetExt": "pdf"} - ] - }, - { - "name": "tika", - "version": "1", - "transformOptions": [ +{ + "transformOptions": { + "tikaOptions": [ {"value": {"name": "transform"}}, {"value": {"name": "includeContents"}}, {"value": {"name": "notExtractBookmarksText"}}, {"value": {"name": "targetMimetype"}}, {"value": {"name": "targetEncoding"}} ], - "supportedSourceAndTargetList": [ - {"sourceExt": "pdf", "maxSourceSizeBytes": 26214400, "targetExt": "txt" }, - {"sourceExt": "doc", "targetExt": "txt"}, - {"sourceExt": "xls", "targetExt": "txt" }, - {"sourceExt": "ppt", "targetExt": "txt" }, - {"sourceExt": "docx", "targetExt": "txt"}, - {"sourceExt": "xlsx", "targetExt": "txt" }, - {"sourceExt": "pptx", "targetExt": "txt" }, - {"sourceExt": "msg", "targetExt": "txt"} - ] - }, - { - "name": "pdfRenderer", - "version": "1", - "transformOptions": [ + "pdfrendererOptions": [ {"value": {"name": "page"}}, {"value": {"name": "width"}}, {"value": {"name": "height"}}, {"value": {"name": "allowPdfEnlargement"}}, {"value": {"name": "maintainPdfAspectRatio"}} ], - "supportedSourceAndTargetList": [ - {"sourceExt": "pdf", "targetExt": "png" } - ] - }, - { - "name": "imageMagick", - "version": "1", - "transformOptions": [ + "imagemagickOptions": [ {"value": {"name": "alphaRemove"}}, {"value": {"name": "autoOrient"}}, {"value": {"name": "startPage"}}, @@ -73,103 +35,110 @@ {"value": {"name": "allowEnlargement"}}, {"value": {"name": "maintainAspectRatio"}} ]}} - ], - "supportedSourceAndTargetList": [ - {"sourceExt": "gif", "targetExt": "gif" }, - {"sourceExt": "gif", "targetExt": "jpeg"}, - {"sourceExt": "gif", "targetExt": "png" }, - {"sourceExt": "gif", "targetExt": "tiff"}, - - {"sourceExt": "jpeg", "targetExt": "gif" }, - {"sourceExt": "jpeg", "targetExt": "jpeg"}, - {"sourceExt": "jpeg", "targetExt": "png" }, - {"sourceExt": "jpeg", "targetExt": "tiff"}, - - {"sourceExt": "png", "targetExt": "gif" }, - {"sourceExt": "png", "targetExt": "jpeg"}, - {"sourceExt": "png", "targetExt": "png" }, - {"sourceExt": "png", "targetExt": "tiff"}, - - {"sourceExt": "tiff", "targetExt": "gif" }, - {"sourceExt": "tiff", "targetExt": "tiff"} ] }, - { - "name": "officeToImageViaPdf", - "version": "1", - "transformOptions": [ - { - "group": { - "required": true, - "transformOptions": [ - {"value": {"name": "alphaRemove"}}, - {"value": {"name": "autoOrient"}}, - {"value": {"name": "startPage"}}, - {"value": {"name": "endPage"}}, - {"group": {"transformOptions": [ - {"value": {"name": "cropGravity"}}, - {"value": {"name": "cropWidth"}}, - {"value": {"name": "cropHeight"}}, - {"value": {"name": "cropPercentage"}}, - {"value": {"name": "cropXOffset"}}, - {"value": {"name": "cropYOffset"}} - ]}}, - {"group": {"transformOptions": [ - {"value": {"name": "thumbnail"}}, - {"value": {"name": "resizeHeight"}}, - {"value": {"name": "resizeWidth"}}, - {"value": {"name": "resizePercentage"}}, - {"value": {"name": "allowEnlargement"}}, - {"value": {"name": "maintainAspectRatio"}} - ]}} - ]}},{ - "group": { - "required": false, - "transformOptions": [ - {"value": {"name": "page"}}, - {"value": {"name": "width"}}, - {"value": {"name": "height"}}, - {"value": {"name": "allowPdfEnlargement"}}, - {"value": {"name": "maintainPdfAspectRatio"}} - ] - } - } - ], - "supportedSourceAndTargetList": [ - {"sourceExt": "doc", "targetExt": "gif" }, - {"sourceExt": "doc", "targetExt": "jpeg"}, - {"sourceExt": "doc", "targetExt": "png" }, - {"sourceExt": "doc", "targetExt": "tiff"}, + "transformers": [ + { + "supportedSourceAndTargetList": [ + {"sourceMediaType": "application/msword", "targetMediaType": "application/msword" }, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "targetMediaType": "application/msword" }, + {"sourceMediaType": "application/msword", "maxSourceSizeBytes": 10485760, "targetMediaType": "application/pdf" }, + {"sourceMediaType": "application/vnd.ms-excel", "maxSourceSizeBytes": 10485760, "targetMediaType": "application/pdf"}, + {"sourceMediaType": "application/vnd.ms-powerpoint", "maxSourceSizeBytes": 6291456, "targetMediaType": "application/pdf" }, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "maxSourceSizeBytes": 786432, "targetMediaType": "application/pdf" }, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "maxSourceSizeBytes": 1572864, "targetMediaType": "application/pdf"}, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.presentationml.presentation", "maxSourceSizeBytes": 4194304, "targetMediaType": "application/pdf" }, + {"sourceMediaType": "application/vnd.ms-outlook", "targetMediaType": "application/pdf"} + ] + }, + { + "supportedSourceAndTargetList": [ + {"sourceMediaType": "application/pdf", "maxSourceSizeBytes": 26214400, "targetMediaType": "text/plain" }, + {"sourceMediaType": "application/msword", "targetMediaType": "text/plain"}, + {"sourceMediaType": "application/vnd.ms-excel", "targetMediaType": "text/plain" }, + {"sourceMediaType": "application/vnd.ms-powerpoint", "targetMediaType": "text/plain" }, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "targetMediaType": "text/plain"}, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "targetMediaType": "text/plain" }, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.presentationml.presentation", "targetMediaType": "text/plain" }, + {"sourceMediaType": "application/vnd.ms-outlook", "targetMediaType": "text/plain"} + ], + "transformOptions": [ + "tikaOptions" + ] + }, + { + "supportedSourceAndTargetList": [ + {"sourceMediaType": "application/pdf", "targetMediaType": "image/png" } + ], + "transformOptions": [ + "pdfrendererOptions" + ] + }, + { + "supportedSourceAndTargetList": [ + {"sourceMediaType": "image/gif", "targetMediaType": "image/gif" }, + {"sourceMediaType": "image/gif", "targetMediaType": "image/jpeg"}, + {"sourceMediaType": "image/gif", "targetMediaType": "image/png" }, + {"sourceMediaType": "image/gif", "targetMediaType": "image/tiff"}, - {"sourceExt": "xls", "targetExt": "gif" }, - {"sourceExt": "xls", "targetExt": "jpeg"}, - {"sourceExt": "xls", "targetExt": "png" }, - {"sourceExt": "xls", "targetExt": "tiff"}, + {"sourceMediaType": "image/jpeg", "targetMediaType": "image/gif" }, + {"sourceMediaType": "image/jpeg", "targetMediaType": "image/jpeg"}, + {"sourceMediaType": "image/jpeg", "targetMediaType": "image/png" }, + {"sourceMediaType": "image/jpeg", "targetMediaType": "image/tiff"}, - {"sourceExt": "ppt", "targetExt": "gif" }, - {"sourceExt": "ppt", "targetExt": "jpeg"}, - {"sourceExt": "ppt", "targetExt": "png" }, - {"sourceExt": "ppt", "targetExt": "tiff"}, + {"sourceMediaType": "image/png", "targetMediaType": "image/gif" }, + {"sourceMediaType": "image/png", "targetMediaType": "image/jpeg"}, + {"sourceMediaType": "image/png", "targetMediaType": "image/png" }, + {"sourceMediaType": "image/png", "targetMediaType": "image/tiff"}, - {"sourceExt": "docx", "targetExt": "gif" }, - {"sourceExt": "docx", "targetExt": "jpeg"}, - {"sourceExt": "docx", "targetExt": "png" }, - {"sourceExt": "docx", "targetExt": "tiff"}, + {"sourceMediaType": "image/tiff", "targetMediaType": "image/gif" }, + {"sourceMediaType": "image/tiff", "targetMediaType": "image/tiff"} + ], + "transformOptions": [ + "imagemagickOptions" + ] + }, + { + "supportedSourceAndTargetList": [ + {"sourceMediaType": "application/msword", "targetMediaType": "image/gif" }, + {"sourceMediaType": "application/msword", "targetMediaType": "image/jpeg"}, + {"sourceMediaType": "application/msword", "targetMediaType": "image/png" }, + {"sourceMediaType": "application/msword", "targetMediaType": "image/tiff"}, - {"sourceExt": "xlsx", "targetExt": "gif" }, - {"sourceExt": "xlsx", "targetExt": "jpeg"}, - {"sourceExt": "xlsx", "targetExt": "png" }, - {"sourceExt": "xlsx", "targetExt": "tiff"}, + {"sourceMediaType": "application/vnd.ms-excel", "targetMediaType": "image/gif" }, + {"sourceMediaType": "application/vnd.ms-excel", "targetMediaType": "image/jpeg"}, + {"sourceMediaType": "application/vnd.ms-excel", "targetMediaType": "image/png" }, + {"sourceMediaType": "application/vnd.ms-excel", "targetMediaType": "image/tiff"}, - {"sourceExt": "pptx", "targetExt": "gif" }, - {"sourceExt": "pptx", "targetExt": "jpeg"}, - {"sourceExt": "pptx", "targetExt": "png" }, - {"sourceExt": "pptx", "targetExt": "tiff"}, + {"sourceMediaType": "application/vnd.ms-powerpoint", "targetMediaType": "image/gif" }, + {"sourceMediaType": "application/vnd.ms-powerpoint", "targetMediaType": "image/jpeg"}, + {"sourceMediaType": "application/vnd.ms-powerpoint", "targetMediaType": "image/png" }, + {"sourceMediaType": "application/vnd.ms-powerpoint", "targetMediaType": "image/tiff"}, - {"sourceExt": "msg", "targetExt": "gif" }, - {"sourceExt": "msg", "targetExt": "jpeg"}, - {"sourceExt": "msg", "targetExt": "png" }, - {"sourceExt": "msg", "targetExt": "tiff"} - ] - } -] \ No newline at end of file + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "targetMediaType": "image/gif" }, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "targetMediaType": "image/jpeg"}, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "targetMediaType": "image/png" }, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "targetMediaType": "image/tiff"}, + + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "targetMediaType": "image/gif" }, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "targetMediaType": "image/jpeg"}, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "targetMediaType": "image/png" }, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "targetMediaType": "image/tiff"}, + + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.presentationml.presentation", "targetMediaType": "image/gif" }, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.presentationml.presentation", "targetMediaType": "image/jpeg"}, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.presentationml.presentation", "targetMediaType": "image/png" }, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.presentationml.presentation", "targetMediaType": "image/tiff"}, + + {"sourceMediaType": "application/vnd.ms-outlook", "targetMediaType": "image/gif" }, + {"sourceMediaType": "application/vnd.ms-outlook", "targetMediaType": "image/jpeg"}, + {"sourceMediaType": "application/vnd.ms-outlook", "targetMediaType": "image/png" }, + {"sourceMediaType": "application/vnd.ms-outlook", "targetMediaType": "image/tiff"} + ], + "transformOptions": [ + "pdfrendererOptions", + "imagemagickOptions" + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/alfresco/transformers/0100-imagemagick.json b/src/main/resources/alfresco/transformers/0100-imagemagick.json new file mode 100644 index 0000000000..930211fa10 --- /dev/null +++ b/src/main/resources/alfresco/transformers/0100-imagemagick.json @@ -0,0 +1,53 @@ +{ + "transformOptions": { + "imagemagickOptions": [ + {"value": {"name": "alphaRemove"}}, + {"value": {"name": "autoOrient"}}, + {"value": {"name": "startPage"}}, + {"value": {"name": "endPage"}}, + {"group": {"transformOptions": [ + {"value": {"name": "cropGravity"}}, + {"value": {"name": "cropWidth"}}, + {"value": {"name": "cropHeight"}}, + {"value": {"name": "cropPercentage"}}, + {"value": {"name": "cropXOffset"}}, + {"value": {"name": "cropYOffset"}} + ]}}, + {"group": {"transformOptions": [ + {"value": {"name": "thumbnail"}}, + {"value": {"name": "resizeHeight"}}, + {"value": {"name": "resizeWidth"}}, + {"value": {"name": "resizePercentage"}}, + {"value": {"name": "allowEnlargement"}}, + {"value": {"name": "maintainAspectRatio"}} + ]}} + ] + }, + "transformers": [ + { + "transformerName": "imagemagick", + "supportedSourceAndTargetList": [ + {"sourceMediaType": "image/gif", "targetMediaType": "image/gif" }, + {"sourceMediaType": "image/gif", "targetMediaType": "image/jpeg"}, + {"sourceMediaType": "image/gif", "targetMediaType": "image/png" }, + {"sourceMediaType": "image/gif", "targetMediaType": "image/tiff"}, + + {"sourceMediaType": "image/jpeg", "targetMediaType": "image/gif" }, + {"sourceMediaType": "image/jpeg", "targetMediaType": "image/jpeg"}, + {"sourceMediaType": "image/jpeg", "targetMediaType": "image/png" }, + {"sourceMediaType": "image/jpeg", "targetMediaType": "image/tiff"}, + + {"sourceMediaType": "image/png", "targetMediaType": "image/gif" }, + {"sourceMediaType": "image/png", "targetMediaType": "image/jpeg"}, + {"sourceMediaType": "image/png", "targetMediaType": "image/png" }, + {"sourceMediaType": "image/png", "targetMediaType": "image/tiff"}, + + {"sourceMediaType": "image/tiff", "targetMediaType": "image/gif" }, + {"sourceMediaType": "image/tiff", "targetMediaType": "image/tiff"} + ], + "transformOptions": [ + "imagemagickOptions" + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/alfresco/transformers/0110-tika.json b/src/main/resources/alfresco/transformers/0110-tika.json new file mode 100644 index 0000000000..4ce9924777 --- /dev/null +++ b/src/main/resources/alfresco/transformers/0110-tika.json @@ -0,0 +1,29 @@ +{ + "transformOptions": { + "tikaOptions": [ + {"value": {"name": "transform"}}, + {"value": {"name": "includeContents"}}, + {"value": {"name": "notExtractBookmarksText"}}, + {"value": {"name": "targetMimetype"}}, + {"value": {"name": "targetEncoding"}} + ] + }, + "transformers": [ + { + "transformerName": "tika", + "supportedSourceAndTargetList": [ + {"sourceMediaType": "application/pdf", "maxSourceSizeBytes": 26214400, "targetMediaType": "text/plain" }, + {"sourceMediaType": "application/msword", "targetMediaType": "text/plain"}, + {"sourceMediaType": "application/vnd.ms-excel", "targetMediaType": "text/plain" }, + {"sourceMediaType": "application/vnd.ms-powerpoint", "targetMediaType": "text/plain" }, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "targetMediaType": "text/plain"}, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "targetMediaType": "text/plain" }, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.presentationml.presentation", "targetMediaType": "text/plain" }, + {"sourceMediaType": "application/vnd.ms-outlook", "targetMediaType": "text/plain"} + ], + "transformOptions": [ + "tikaOptions" + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/alfresco/transformers/0120-pdfrenderer.json b/src/main/resources/alfresco/transformers/0120-pdfrenderer.json new file mode 100644 index 0000000000..c9a25f9326 --- /dev/null +++ b/src/main/resources/alfresco/transformers/0120-pdfrenderer.json @@ -0,0 +1,22 @@ +{ + "transformOptions": { + "pdfrendererOptions": [ + {"value": {"name": "page"}}, + {"value": {"name": "width"}}, + {"value": {"name": "height"}}, + {"value": {"name": "allowPdfEnlargement"}}, + {"value": {"name": "maintainPdfAspectRatio"}} + ] + }, + "transformers": [ + { + "transformerName": "pdfrenderer", + "supportedSourceAndTargetList": [ + {"sourceMediaType": "application/pdf", "targetMediaType": "image/png" } + ], + "transformOptions": [ + "pdfrendererOptions" + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/alfresco/transformers/0130-libreoffice.json b/src/main/resources/alfresco/transformers/0130-libreoffice.json new file mode 100644 index 0000000000..7af46de6c2 --- /dev/null +++ b/src/main/resources/alfresco/transformers/0130-libreoffice.json @@ -0,0 +1,18 @@ +{ + "transformers": [ + { + "transformerName": "libreoffice", + "supportedSourceAndTargetList": [ + {"sourceMediaType": "application/msword", "targetMediaType": "application/msword" }, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "targetMediaType": "application/msword" }, + {"sourceMediaType": "application/msword", "maxSourceSizeBytes": 10485760, "targetMediaType": "application/pdf" }, + {"sourceMediaType": "application/vnd.ms-excel", "maxSourceSizeBytes": 10485760, "targetMediaType": "application/pdf"}, + {"sourceMediaType": "application/vnd.ms-powerpoint", "maxSourceSizeBytes": 6291456, "targetMediaType": "application/pdf" }, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "maxSourceSizeBytes": 786432, "targetMediaType": "application/pdf" }, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "maxSourceSizeBytes": 1572864, "targetMediaType": "application/pdf"}, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.presentationml.presentation", "maxSourceSizeBytes": 4194304, "targetMediaType": "application/pdf" }, + {"sourceMediaType": "application/vnd.ms-outlook", "targetMediaType": "application/pdf"} + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/alfresco/transformers/0150-officeToImageViaPdf.json b/src/main/resources/alfresco/transformers/0150-officeToImageViaPdf.json new file mode 100644 index 0000000000..c1ada0fcdc --- /dev/null +++ b/src/main/resources/alfresco/transformers/0150-officeToImageViaPdf.json @@ -0,0 +1,52 @@ +{ + "transformers": [ + { + "transformerName": "officeToImageViaPdf", + "transformerPipeline" : [ + {"transformerName": "libreoffice", "targetMediaType": "application/pdf"}, + {"transformerName": "pdfrenderer", "targetMediaType": "image/png"}, + {"transformerName": "imagemagick"} + ], + "supportedSourceAndTargetList": [ + {"sourceMediaType": "application/msword", "targetMediaType": "image/gif" }, + {"sourceMediaType": "application/msword", "targetMediaType": "image/jpeg"}, + {"sourceMediaType": "application/msword", "targetMediaType": "image/png" }, + {"sourceMediaType": "application/msword", "targetMediaType": "image/tiff"}, + + {"sourceMediaType": "application/vnd.ms-excel", "targetMediaType": "image/gif" }, + {"sourceMediaType": "application/vnd.ms-excel", "targetMediaType": "image/jpeg"}, + {"sourceMediaType": "application/vnd.ms-excel", "targetMediaType": "image/png" }, + {"sourceMediaType": "application/vnd.ms-excel", "targetMediaType": "image/tiff"}, + + {"sourceMediaType": "application/vnd.ms-powerpoint", "targetMediaType": "image/gif" }, + {"sourceMediaType": "application/vnd.ms-powerpoint", "targetMediaType": "image/jpeg"}, + {"sourceMediaType": "application/vnd.ms-powerpoint", "targetMediaType": "image/png" }, + {"sourceMediaType": "application/vnd.ms-powerpoint", "targetMediaType": "image/tiff"}, + + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "targetMediaType": "image/gif" }, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "targetMediaType": "image/jpeg"}, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "targetMediaType": "image/png" }, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "targetMediaType": "image/tiff"}, + + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "targetMediaType": "image/gif" }, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "targetMediaType": "image/jpeg"}, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "targetMediaType": "image/png" }, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "targetMediaType": "image/tiff"}, + + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.presentationml.presentation", "targetMediaType": "image/gif" }, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.presentationml.presentation", "targetMediaType": "image/jpeg"}, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.presentationml.presentation", "targetMediaType": "image/png" }, + {"sourceMediaType": "application/vnd.openxmlformats-officedocument.presentationml.presentation", "targetMediaType": "image/tiff"}, + + {"sourceMediaType": "application/vnd.ms-outlook", "targetMediaType": "image/gif" }, + {"sourceMediaType": "application/vnd.ms-outlook", "targetMediaType": "image/jpeg"}, + {"sourceMediaType": "application/vnd.ms-outlook", "targetMediaType": "image/png" }, + {"sourceMediaType": "application/vnd.ms-outlook", "targetMediaType": "image/tiff"} + ], + "transformOptions": [ + "pdfrendererOptions", + "imagemagickOptions" + ] + } + ] +} \ No newline at end of file diff --git a/src/test/java/org/alfresco/AllUnitTestsSuite.java b/src/test/java/org/alfresco/AllUnitTestsSuite.java index cf541db439..3049cccc6d 100644 --- a/src/test/java/org/alfresco/AllUnitTestsSuite.java +++ b/src/test/java/org/alfresco/AllUnitTestsSuite.java @@ -200,7 +200,8 @@ import org.junit.runners.Suite; org.alfresco.util.bean.HierarchicalBeanLoaderTest.class, org.alfresco.util.resource.HierarchicalResourceLoaderTest.class, org.alfresco.repo.events.ClientUtilTest.class, - org.alfresco.repo.rendition2.RenditionService2Test.class + org.alfresco.repo.rendition2.RenditionService2Test.class, + org.alfresco.transform.client.model.config.TransformServiceRegistryImplTest.class }) public class AllUnitTestsSuite { diff --git a/src/test/java/org/alfresco/AppContext06TestSuite.java b/src/test/java/org/alfresco/AppContext06TestSuite.java index 3e520f569e..2412c1222b 100644 --- a/src/test/java/org/alfresco/AppContext06TestSuite.java +++ b/src/test/java/org/alfresco/AppContext06TestSuite.java @@ -47,11 +47,18 @@ import org.junit.runners.Suite; // Requires a running ActiveMQ org.alfresco.repo.rawevents.EventBehaviourTest.class, org.alfresco.repo.rawevents.TransactionAwareEventProducerTest.class, + // Requires running transformers org.alfresco.repo.rendition2.RenditionService2IntegrationTest.class, - org.alfresco.repo.rendition2.LegacyLocalTransformClientIntegrationTest.class, - org.alfresco.repo.rendition2.LegacyLocalTransformServiceRegistryTest.class, - org.alfresco.repo.rendition2.RenditionTest.class, + org.alfresco.repo.rendition2.LocalTransformServiceRegistryTest.class, + org.alfresco.repo.rendition2.LocalTransformClientIntegrationTest.class, + org.alfresco.repo.rendition2.LegacyTransformServiceRegistryTest.class, + org.alfresco.repo.rendition2.LegacyTransformClientIntegrationTest.class, + org.alfresco.repo.rendition2.LocalRenditionTest.class, + org.alfresco.repo.rendition2.LegacyRenditionTest.class, + org.alfresco.repo.rendition2.LegacyLocalRenditionTest.class, + org.alfresco.repo.rendition2.NoneRenditionTest.class, + org.alfresco.repo.solr.SOLRTrackingComponentTest.class, org.alfresco.repo.tagging.TaggingServiceImplTest.class, org.alfresco.repo.transaction.AlfrescoTransactionSupportTest.class, @@ -64,6 +71,7 @@ import org.junit.runners.Suite; org.alfresco.repo.tenant.MultiTServiceImplTest.class, org.alfresco.repo.search.SearcherComponentTest.class, org.alfresco.repo.action.scheduled.ScheduledPersistedActionServiceTest.class, + org.alfresco.repo.rendition2.RenditionDefinitionTest.class }) public class AppContext06TestSuite diff --git a/src/test/java/org/alfresco/repo/content/transform/TransformerDebugLogTest.java b/src/test/java/org/alfresco/repo/content/transform/TransformerDebugLogTest.java index 79a88920a5..8a24feeef9 100644 --- a/src/test/java/org/alfresco/repo/content/transform/TransformerDebugLogTest.java +++ b/src/test/java/org/alfresco/repo/content/transform/TransformerDebugLogTest.java @@ -41,10 +41,7 @@ import org.mockito.MockitoAnnotations; * Test class for TransformerDebugLog. * * @author Alan Davis - * - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. */ -@Deprecated public class TransformerDebugLogTest { @Mock 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 fc9b7cb7fb..9df45538e7 100644 --- a/src/test/java/org/alfresco/repo/content/transform/TransformerDebugTest.java +++ b/src/test/java/org/alfresco/repo/content/transform/TransformerDebugTest.java @@ -25,9 +25,9 @@ */ package org.alfresco.repo.content.transform; +import org.alfresco.repo.rendition2.TransformClient; import org.alfresco.service.cmr.repository.MimetypeService; import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.TransformationOptions; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; @@ -49,10 +49,7 @@ import static org.mockito.Mockito.when; * Test class for TransformerDebug. * * @author Alan Davis - * - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. */ -@Deprecated public class TransformerDebugTest { @Mock @@ -68,7 +65,7 @@ public class TransformerDebugTest private TransformerConfig transformerConfig; @Mock - private TransformationOptions options; + private TransformClient transformClient; @Mock private AbstractContentTransformerLimits transformer1; @@ -108,7 +105,14 @@ public class TransformerDebugTest when(transformer3.getName()).thenReturn("transformer3"); when(transformer4.getName()).thenReturn("transformer4"); - transformerDebug = new TransformerDebug(nodeService, mimetypeService, transformerRegistry, transformerConfig, log, debug); + transformerDebug = new TransformerDebug(); + transformerDebug.setNodeService(nodeService); + transformerDebug.setMimetypeService(mimetypeService); + transformerDebug.setTransformerRegistry(transformerRegistry); + transformerDebug.setTransformerConfig(transformerConfig); + transformerDebug.setTransformerLog(log); + transformerDebug.setTransformerDebugLog(debug); + transformerDebug.setTransformClient(transformClient); log.setTransformerDebug(transformerDebug); log.setTransformerConfig(transformerConfig); @@ -151,7 +155,7 @@ public class TransformerDebugTest { long sourceSize = 1024*1024*3/2; - transformerDebug.pushAvailable("sourceUrl", "application/pdf", "text/plain", options); + transformerDebug.pushAvailable("sourceUrl", "application/pdf", "text/plain", null, null); transformerDebug.unavailableTransformer(transformer1, "application/pdf", "text/plain", 50); transformerDebug.unavailableTransformer(transformer2, "application/pdf", "text/plain", 0); @@ -160,7 +164,7 @@ public class TransformerDebugTest List transformers = Arrays.asList(new ContentTransformer[] {}); - transformerDebug.availableTransformers(transformers, sourceSize, options, "ContentService.transform(...)"); + transformerDebug.availableTransformers(transformers, sourceSize, null, null, "ContentService.transform(...)"); transformerDebug.popAvailable(); diff --git a/src/test/java/org/alfresco/repo/content/transform/TransformerLogTest.java b/src/test/java/org/alfresco/repo/content/transform/TransformerLogTest.java index 9281a593c4..339e8fba22 100644 --- a/src/test/java/org/alfresco/repo/content/transform/TransformerLogTest.java +++ b/src/test/java/org/alfresco/repo/content/transform/TransformerLogTest.java @@ -39,10 +39,7 @@ import org.mockito.MockitoAnnotations; * Test class for TransformerLog. * * @author Alan Davis - * - * @deprecated The transformations code is being moved out of the codebase and replaced by the new async RenditionService2 or other external libraries. */ -@Deprecated public class TransformerLogTest { @Mock diff --git a/src/test/java/org/alfresco/repo/rawevents/TransactionAwareEventProducerTest.java b/src/test/java/org/alfresco/repo/rawevents/TransactionAwareEventProducerTest.java index 70d88acb7c..17ded38875 100644 --- a/src/test/java/org/alfresco/repo/rawevents/TransactionAwareEventProducerTest.java +++ b/src/test/java/org/alfresco/repo/rawevents/TransactionAwareEventProducerTest.java @@ -36,6 +36,7 @@ import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.beans.factory.annotation.Qualifier; /** * Provides a base set of tests for {@link TransactionAwareEventProducer} @@ -51,6 +52,7 @@ public class TransactionAwareEventProducerTest extends BaseSpringTest @Autowired private TransactionAwareEventProducer eventProducer; @Autowired + @Qualifier("alfrescoEventObjectMapper") private ObjectMapper messagingObjectMapper; @Test diff --git a/src/test/java/org/alfresco/repo/rendition2/AbstractRenditionIntegrationTest.java b/src/test/java/org/alfresco/repo/rendition2/AbstractRenditionIntegrationTest.java index 34a9821fad..6442177075 100644 --- a/src/test/java/org/alfresco/repo/rendition2/AbstractRenditionIntegrationTest.java +++ b/src/test/java/org/alfresco/repo/rendition2/AbstractRenditionIntegrationTest.java @@ -28,6 +28,7 @@ package org.alfresco.repo.rendition2; import junit.framework.AssertionFailedError; import org.alfresco.model.ContentModel; import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.content.transform.LocalTransformServiceRegistry; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.thumbnail.ThumbnailRegistry; import org.alfresco.repo.transaction.RetryingTransactionHelper; @@ -44,7 +45,7 @@ import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.namespace.QName; import org.alfresco.service.transaction.TransactionService; -import org.alfresco.util.ApplicationContextHelper; +import org.alfresco.transform.client.model.config.TransformServiceRegistry; import org.alfresco.util.BaseSpringTest; import org.alfresco.util.GUID; import org.alfresco.util.PropertyMap; @@ -72,10 +73,7 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest protected RenditionService2Impl renditionService2; @Autowired - protected RenditionDefinitionRegistry2 renditionDefinitionRegistry2; - - @Autowired - protected TransformClient transformClient; + protected RenditionDefinitionRegistry2Impl renditionDefinitionRegistry2; @Autowired protected RenditionService renditionService; @@ -107,6 +105,15 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest @Autowired protected PermissionService permissionService; + @Autowired + protected TransformServiceRegistry transformServiceRegistry; + + @Autowired + protected LocalTransformServiceRegistry localTransformServiceRegistry; + + @Autowired + protected LegacyTransformServiceRegistry legacyTransformServiceRegistry; + static String PASSWORD = "password"; protected static final String ADMIN = "admin"; @@ -115,17 +122,74 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest @BeforeClass public static void before() { - // Use the docker images for transforms + // Use the docker images for transforms (legacy) System.setProperty("alfresco-pdf-renderer.url", "http://localhost:8090/"); System.setProperty("img.url", "http://localhost:8091"); System.setProperty("jodconverter.url", "http://localhost:8092/"); System.setProperty("tika.url", "http://localhost:8093/"); + + // Use the docker images for transforms (local) + System.setProperty("localTransformer.pdfrenderer.url", "http://localhost:8090/"); + System.setProperty("localTransformer.imagemagick.url", "http://localhost:8091"); + System.setProperty("localTransformer.libreoffice.url", "http://localhost:8092/"); + System.setProperty("localTransformer.tika.url", "http://localhost:8093/"); + } + + protected static void none() + { + System.setProperty("transform.service.enabled", "false"); + System.setProperty("local.transform.service.enabled", "false"); + System.setProperty("legacy.transform.service.enabled", "false"); + } + + protected static void legacy() + { + System.setProperty("transform.service.enabled", "false"); + System.setProperty("local.transform.service.enabled", "false"); + System.setProperty("legacy.transform.service.enabled", "true"); + } + + protected static void local() + { + System.setProperty("transform.service.enabled", "false"); + System.setProperty("local.transform.service.enabled", "true"); + System.setProperty("legacy.transform.service.enabled", "false"); + } + + protected static void service() + { + System.setProperty("transform.service.enabled", "true"); + System.setProperty("local.transform.service.enabled", "false"); + System.setProperty("legacy.transform.service.enabled", "false"); + } + + protected static void legacyLocal() + { + System.setProperty("transform.service.enabled", "false"); + System.setProperty("local.transform.service.enabled", "true"); + System.setProperty("legacy.transform.service.enabled", "true"); + } + + protected static void legacyLocalService() + { + System.setProperty("transform.service.enabled", "true"); + System.setProperty("local.transform.service.enabled", "true"); + System.setProperty("legacy.transform.service.enabled", "true"); } @Before public void setUp() throws Exception { assertTrue("The RenditionService2 needs to be enabled", renditionService2.isEnabled()); + + legacyTransformServiceRegistry.setEnabled(Boolean.parseBoolean(System.getProperty("legacy.transform.service.enabled"))); + legacyTransformServiceRegistry.afterPropertiesSet(); + + localTransformServiceRegistry.setEnabled(Boolean.parseBoolean(System.getProperty("local.transform.service.enabled"))); + localTransformServiceRegistry.afterPropertiesSet(); + + renditionDefinitionRegistry2.setTransformServiceRegistry(transformServiceRegistry); + thumbnailRegistry.setTransformServiceRegistry(transformServiceRegistry); } @After @@ -141,6 +205,15 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest System.clearProperty("img.url"); System.clearProperty("jodconverter.url"); System.clearProperty("tika.url"); + + System.clearProperty("localTransformer.pdfrenderer.url"); + System.clearProperty("localTransformer.imagemagick.url"); + System.clearProperty("localTransformer.libreoffice.url"); + System.clearProperty("localTransformer.tika.url"); + + System.clearProperty("transform.service.enabled"); + System.clearProperty("local.transform.service.enabled"); + System.clearProperty("legacy.transform.service.enabled"); } protected void checkRendition(String testFileName, String renditionName, boolean expectedToPass) diff --git a/src/test/java/org/alfresco/repo/rendition2/RenditionTest.java b/src/test/java/org/alfresco/repo/rendition2/AbstractRenditionTest.java similarity index 78% rename from src/test/java/org/alfresco/repo/rendition2/RenditionTest.java rename to src/test/java/org/alfresco/repo/rendition2/AbstractRenditionTest.java index 8b1dec11fc..2c1073ec03 100644 --- a/src/test/java/org/alfresco/repo/rendition2/RenditionTest.java +++ b/src/test/java/org/alfresco/repo/rendition2/AbstractRenditionTest.java @@ -28,7 +28,6 @@ package org.alfresco.repo.rendition2; import junit.framework.AssertionFailedError; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.thumbnail.ThumbnailDefinition; -import org.alfresco.transform.client.model.config.TransformServiceRegistry; import org.alfresco.util.testing.category.DebugTests; import org.junit.Before; import org.junit.Test; @@ -45,11 +44,12 @@ import java.util.Set; import java.util.StringJoiner; /** - * Test it is possible to create renditions from the quick files. + * Abstract test class to check it is possible to create renditions from the quick files using combinations of + * local transforms, legacy transforms and the Transform Service. * * @author adavis */ -public class RenditionTest extends AbstractRenditionIntegrationTest +public abstract class AbstractRenditionTest extends AbstractRenditionIntegrationTest { // This is the same order as produced by MimetypeMap public static final List TAS_REST_API_SOURCE_EXTENSIONS = Arrays.asList( @@ -89,9 +89,7 @@ public class RenditionTest extends AbstractRenditionIntegrationTest "wpd png avatar32", "wpd jpg imgpreview"); - @Autowired - private TransformServiceRegistry transformServiceRegistry; - + @Override @Before public void setUp() throws Exception { @@ -148,17 +146,15 @@ public class RenditionTest extends AbstractRenditionIntegrationTest } else { - String task = sourceExtension + " " + targetExtension + " " + renditionName; - try { checkRendition(testFileName, renditionName, !expectedToFail.contains(sourceTragetRendition)); - successes.add(task); + successes.add(sourceTragetRendition); successCount++; } catch (AssertionFailedError e) { - failures.add(task + " " + e.getMessage()); + failures.add(sourceTragetRendition + " " + e.getMessage()); failedCount++; } } @@ -217,48 +213,6 @@ public class RenditionTest extends AbstractRenditionIntegrationTest Collections.emptyList(), expectedRenditionCount, expectedFailedCount); } - @Category(DebugTests.class) - @Test - public void testTransformServiceConfig() throws Exception - { - internalTestTransformServiceConfig(57, 0); - } - - // Tests all renditions supported by the TransformServiceRegistry (in the case of Transform Service, see - // transform-service-config.json and the LegacyLocalTransformServiceRegistry see the original ACS config). - protected void internalTestTransformServiceConfig(int expectedRenditionCount, int expectedFailedCount) throws Exception - { - List sourceExtensions = getAllSourceMimetypes(); - List excludeList = new ArrayList<>(); - - for (String sourceExtension : sourceExtensions) - { - String sourceMimetype = mimetypeMap.getMimetype(sourceExtension); - String testFileName = getTestFileName(sourceMimetype); - if (testFileName != null) - { - Set renditionNames = renditionDefinitionRegistry2.getRenditionNamesFrom(sourceMimetype, -1); - for (String renditionName : renditionNames) - { - RenditionDefinition2 renditionDefinition = renditionDefinitionRegistry2.getRenditionDefinition(renditionName); - String targetMimetype = renditionDefinition.getTargetMimetype(); - String targetExtension = mimetypeMap.getExtension(targetMimetype); - - String sourceTragetRendition = sourceExtension + ' ' + targetExtension + ' ' + renditionName; - Map actualOptions = renditionDefinition.getTransformOptions(); - if (!transformServiceRegistry.isSupported(sourceMimetype, -1L, targetMimetype, - actualOptions, null)) - { - excludeList.add(sourceTragetRendition); - } - } - } - } - - assertRenditionsOkayFromSourceExtension(sourceExtensions, excludeList, - Collections.emptyList(), expectedRenditionCount, expectedFailedCount); - } - private List getAllSourceMimetypes() { List sourceExtensions = new ArrayList<>(); diff --git a/src/main/java/org/alfresco/repo/rendition2/AbstractTransformClient.java b/src/test/java/org/alfresco/repo/rendition2/LegacyLocalRenditionTest.java similarity index 61% rename from src/main/java/org/alfresco/repo/rendition2/AbstractTransformClient.java rename to src/test/java/org/alfresco/repo/rendition2/LegacyLocalRenditionTest.java index f8ce1d6f6c..0b0d6dc0bf 100644 --- a/src/main/java/org/alfresco/repo/rendition2/AbstractTransformClient.java +++ b/src/test/java/org/alfresco/repo/rendition2/LegacyLocalRenditionTest.java @@ -25,30 +25,30 @@ */ package org.alfresco.repo.rendition2; -import org.alfresco.model.ContentModel; -import org.alfresco.service.cmr.repository.ContentData; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.util.PropertyCheck; -import org.springframework.beans.factory.InitializingBean; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; /** - * Contains common code used in TransformClients. + * Repeats quick file rendition tests with local transforms and legacy transformers enabled. + * The Transform Service does not exist for the Community edition. + * Should be the same result as with legacy or local transforms on their own. * * @author adavis */ -public abstract class AbstractTransformClient implements InitializingBean +@Deprecated +public class LegacyLocalRenditionTest extends AbstractRenditionTest { - protected NodeService nodeService; - - public void setNodeService(NodeService nodeService) + @BeforeClass + public static void before() { - this.nodeService = nodeService; + AbstractRenditionIntegrationTest.before(); + legacyLocal(); } - @Override - public void afterPropertiesSet() throws Exception + @AfterClass + public static void after() { - PropertyCheck.mandatory(this, "nodeService", nodeService); + AbstractRenditionIntegrationTest.after(); } } diff --git a/src/test/java/org/alfresco/repo/rendition2/LegacyRenditionTest.java b/src/test/java/org/alfresco/repo/rendition2/LegacyRenditionTest.java new file mode 100644 index 0000000000..0b82424607 --- /dev/null +++ b/src/test/java/org/alfresco/repo/rendition2/LegacyRenditionTest.java @@ -0,0 +1,52 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2018 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.junit.AfterClass; +import org.junit.BeforeClass; + +/** + * Repeats quick file rendition tests with local transforms disabled but legacy transformers enabled. + * The Transform Service does not exist for the Community edition. + * Should be the same result as with local transforms. + * + * @author adavis + */ +public class LegacyRenditionTest extends AbstractRenditionTest +{ + @BeforeClass + public static void before() + { + AbstractRenditionIntegrationTest.before(); + legacy(); + } + + @AfterClass + public static void after() + { + AbstractRenditionIntegrationTest.after(); + } +} diff --git a/src/test/java/org/alfresco/repo/rendition2/LegacyTransformClientIntegrationTest.java b/src/test/java/org/alfresco/repo/rendition2/LegacyTransformClientIntegrationTest.java new file mode 100644 index 0000000000..86ead44420 --- /dev/null +++ b/src/test/java/org/alfresco/repo/rendition2/LegacyTransformClientIntegrationTest.java @@ -0,0 +1,69 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2018 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.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import static org.alfresco.model.ContentModel.PROP_CONTENT; + +/** + * Integration tests for {@link LegacyTransformClient} + */ +@Deprecated +public class LegacyTransformClientIntegrationTest extends LocalTransformClientIntegrationTest +{ + @Autowired + protected TransformClient legacyTransformClient; + + @BeforeClass + public static void before() + { + AbstractRenditionIntegrationTest.before(); + legacy(); + } + + @AfterClass + public static void after() + { + AbstractRenditionIntegrationTest.after(); + } + + @Before + public void setUp() throws Exception + { + super.setUp(); + transformClient = legacyTransformClient; + } +} diff --git a/src/test/java/org/alfresco/repo/rendition2/LegacyTransformServiceRegistryTest.java b/src/test/java/org/alfresco/repo/rendition2/LegacyTransformServiceRegistryTest.java new file mode 100644 index 0000000000..d1cd37c608 --- /dev/null +++ b/src/test/java/org/alfresco/repo/rendition2/LegacyTransformServiceRegistryTest.java @@ -0,0 +1,75 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2018 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.transform.client.model.config.TransformServiceRegistry; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.HashMap; +import java.util.Map; + +import static org.alfresco.repo.content.MimetypeMap.MIMETYPE_OPENXML_WORDPROCESSING; +import static org.alfresco.repo.content.MimetypeMap.MIMETYPE_PDF; + +/** + * Integration tests for {@link LegacyTransformServiceRegistry} + */ +@Deprecated +public class LegacyTransformServiceRegistryTest extends LocalTransformServiceRegistryTest +{ + @Autowired + private LegacyTransformServiceRegistry legacyTransformServiceRegistry; + + @Before + public void setUp() throws Exception + { + super.setUp(); + transformServiceRegistry = legacyTransformServiceRegistry; + } + + @BeforeClass + public static void before() + { + AbstractRenditionIntegrationTest.before(); + legacy(); + } + + @AfterClass + public static void after() + { + AbstractRenditionIntegrationTest.after(); + } + + protected void setEnabled(boolean enabled) + { + legacyTransformServiceRegistry.setEnabled(enabled); + } +} diff --git a/src/test/java/org/alfresco/repo/rendition2/LocalRenditionTest.java b/src/test/java/org/alfresco/repo/rendition2/LocalRenditionTest.java new file mode 100644 index 0000000000..6337e6832e --- /dev/null +++ b/src/test/java/org/alfresco/repo/rendition2/LocalRenditionTest.java @@ -0,0 +1,64 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2018 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.util.testing.category.DebugTests; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +/** + * Repeats quick file rendition tests with local transforms enabled but legacy transformers disabled. + * The Transform Service does not exist for the Community edition. + * Should be the same result as with legacy transforms only. + * + * @author adavis + */ +public class LocalRenditionTest extends AbstractRenditionTest +{ + @BeforeClass + public static void before() + { + AbstractRenditionIntegrationTest.before(); + local(); + } + + @AfterClass + public static void after() + { + AbstractRenditionIntegrationTest.after(); + } + + // TODO this method will be removed when Local transformers support all 196 renditions supported by legacy + @Override + @Category(DebugTests.class) + @Test + public void testAllSourceExtensions() throws Exception + { + internalTestAllSourceExtensions(57, 0); + } +} diff --git a/src/test/java/org/alfresco/repo/rendition2/LegacyLocalTransformClientIntegrationTest.java b/src/test/java/org/alfresco/repo/rendition2/LocalTransformClientIntegrationTest.java similarity index 67% rename from src/test/java/org/alfresco/repo/rendition2/LegacyLocalTransformClientIntegrationTest.java rename to src/test/java/org/alfresco/repo/rendition2/LocalTransformClientIntegrationTest.java index fa64ea0788..ae26cfe61f 100644 --- a/src/test/java/org/alfresco/repo/rendition2/LegacyLocalTransformClientIntegrationTest.java +++ b/src/test/java/org/alfresco/repo/rendition2/LocalTransformClientIntegrationTest.java @@ -30,107 +30,89 @@ import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.junit.AfterClass; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import static org.alfresco.model.ContentModel.PROP_CONTENT; /** - * Integration tests for {@link LegacyLocalTransformClient} + * Integration tests for {@link LocalTransformClient} */ -public class LegacyLocalTransformClientIntegrationTest extends AbstractRenditionIntegrationTest +public class LocalTransformClientIntegrationTest extends AbstractRenditionIntegrationTest { + @Autowired + protected TransformClient localTransformClient; + + protected TransformClient transformClient; + + @BeforeClass + public static void before() + { + AbstractRenditionIntegrationTest.before(); + local(); + } + + @AfterClass + public static void after() + { + AbstractRenditionIntegrationTest.after(); + } + @Before public void setUp() throws Exception { super.setUp(); AuthenticationUtil.setRunAsUser(AuthenticationUtil.getAdminUserName()); + transformClient = localTransformClient; } - // PDF transformation - - @Test - public void testLocalRenderPdfToJpegMedium() throws Exception - { - localCheckRendition("quick.pdf", "medium", true); - } - - @Test - public void testLocalRenderPdfToDoclib() throws Exception - { - localCheckRendition("quick.pdf", "doclib", true); - } - - @Test - public void testLocalRenderPdfJpegImgpreview() throws Exception - { - localCheckRendition("quick.pdf", "imgpreview", true); - } - - @Test - public void testLocalRenderPdfPngAvatar() throws Exception - { - localCheckRendition("quick.pdf", "avatar", true); - } - - @Test - public void testLocalRenderPdfPngAvatar32() throws Exception - { - localCheckRendition("quick.pdf", "avatar32", true); - } - - @Test - public void testLocalRenderPdfFlashWebpreview() throws Exception - { - localCheckRendition("quick.pdf", "webpreview", false); - } - - // DOCX transformation @Test public void testLocalRenderDocxJpegMedium() throws Exception { - localCheckRendition("quick.docx", "medium", true); + checkClientRendition("quick.docx", "medium", true); } @Test public void testLocalRenderDocxDoclib() throws Exception { - localCheckRendition("quick.docx", "doclib", true); + checkClientRendition("quick.docx", "doclib", true); } @Test public void testLocalRenderDocxJpegImgpreview() throws Exception { - localCheckRendition("quick.docx", "imgpreview", true); + checkClientRendition("quick.docx", "imgpreview", true); } @Test public void testLocalRenderDocxPngAvatar() throws Exception { - localCheckRendition("quick.docx", "avatar", true); + checkClientRendition("quick.docx", "avatar", true); } @Test public void testLocalRenderDocxPngAvatar32() throws Exception { - localCheckRendition("quick.docx", "avatar32", true); + checkClientRendition("quick.docx", "avatar32", true); } @Test public void testLocalRenderDocxFlashWebpreview() throws Exception { - localCheckRendition("quick.docx", "webpreview", false); + checkClientRendition("quick.docx", "webpreview", false); } @Test public void testLocalRenderDocxPdf() throws Exception { - localCheckRendition("quick.docx", "pdf", false); + checkClientRendition("quick.docx", "pdf", false); } - private void localCheckRendition(String testFileName, String renditionDefinitionName, boolean expectedToPass) throws InterruptedException + protected void checkClientRendition(String testFileName, String renditionDefinitionName, boolean expectedToPass) throws InterruptedException { if (expectedToPass) { @@ -143,11 +125,12 @@ public class LegacyLocalTransformClientIntegrationTest extends AbstractRendition { RenditionDefinition2 renditionDefinition = renditionDefinitionRegistry2.getRenditionDefinition(renditionDefinitionName); - transformClient.transform( - sourceNode, - renditionDefinition, - AuthenticationUtil.getAdminUserName(), - sourceContentHashCode); + String contentUrl = contentData.getContentUrl(); + String sourceMimetype = contentData.getMimetype(); + long size = contentData.getSize(); + String adminUserName = AuthenticationUtil.getAdminUserName(); + transformClient.checkSupported(sourceNode, renditionDefinition, sourceMimetype, size, contentUrl); + transformClient.transform(sourceNode, renditionDefinition, adminUserName, sourceContentHashCode); return null; }); ChildAssociationRef childAssociationRef = null; diff --git a/src/test/java/org/alfresco/repo/rendition2/LegacyLocalTransformServiceRegistryTest.java b/src/test/java/org/alfresco/repo/rendition2/LocalTransformServiceRegistryTest.java similarity index 78% rename from src/test/java/org/alfresco/repo/rendition2/LegacyLocalTransformServiceRegistryTest.java rename to src/test/java/org/alfresco/repo/rendition2/LocalTransformServiceRegistryTest.java index bbded0270e..aa929cffaf 100644 --- a/src/test/java/org/alfresco/repo/rendition2/LegacyLocalTransformServiceRegistryTest.java +++ b/src/test/java/org/alfresco/repo/rendition2/LocalTransformServiceRegistryTest.java @@ -25,9 +25,12 @@ */ package org.alfresco.repo.rendition2; -import com.sun.star.auth.InvalidArgumentException; +import org.alfresco.repo.content.transform.LocalTransformServiceRegistry; +import org.alfresco.transform.client.model.config.TransformServiceRegistry; +import org.junit.AfterClass; import org.junit.Assert; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -38,28 +41,49 @@ import static org.alfresco.repo.content.MimetypeMap.MIMETYPE_OPENXML_WORDPROCESS import static org.alfresco.repo.content.MimetypeMap.MIMETYPE_PDF; /** - * Integration tests for {@link LegacyLocalTransformServiceRegistry} + * Integration tests for {@link LocalTransformServiceRegistry} */ -public class LegacyLocalTransformServiceRegistryTest extends AbstractRenditionIntegrationTest +public class LocalTransformServiceRegistryTest extends AbstractRenditionIntegrationTest { - private static final String RENDITION_NAME = "pdf"; - @Autowired - private LegacyLocalTransformServiceRegistry transformServiceRegistry; + private LocalTransformServiceRegistry localTransformServiceRegistry; - @Autowired - private RenditionDefinitionRegistry2 renditionDefinitionRegistry2; + protected TransformServiceRegistry transformServiceRegistry; private Map options; + @BeforeClass + public static void before() + { + AbstractRenditionIntegrationTest.before(); + local(); + } + + @AfterClass + public static void after() + { + AbstractRenditionIntegrationTest.after(); + } + @Before public void setUp() throws Exception { super.setUp(); + transformServiceRegistry = localTransformServiceRegistry; + RenditionDefinition2 definition2 = renditionDefinitionRegistry2.getRenditionDefinition(RENDITION_NAME); options = definition2.getTransformOptions(); } + protected void setEnabled(boolean enabled) + { + localTransformServiceRegistry.setEnabled(enabled); + } + private static final String RENDITION_NAME = "pdf"; + + @Autowired + private LegacyTransformServiceRegistry legacyTransformServiceRegistry; + @Test public void testIsSupported() { @@ -71,7 +95,7 @@ public class LegacyLocalTransformServiceRegistryTest extends AbstractRenditionIn // Bad Source Assert.assertFalse(transformServiceRegistry.isSupported("docxBad", 1234, MIMETYPE_PDF, options, RENDITION_NAME)); // Bad Target - Assert.assertFalse(transformServiceRegistry.isSupported(MIMETYPE_OPENXML_WORDPROCESSING, 1234, "pdfBad", options, RENDITION_NAME)); + Assert.assertFalse(transformServiceRegistry.isSupported(MIMETYPE_OPENXML_WORDPROCESSING, 1234, "pdfBad", options, "pdfBad")); // Good MaxSize docx max size is 768K Assert.assertTrue(transformServiceRegistry.isSupported(MIMETYPE_OPENXML_WORDPROCESSING, 768L*1024, MIMETYPE_PDF, options, RENDITION_NAME)); @@ -89,7 +113,7 @@ public class LegacyLocalTransformServiceRegistryTest extends AbstractRenditionIn Assert.assertTrue(transformServiceRegistry.isSupported(MIMETYPE_OPENXML_WORDPROCESSING, 1234, MIMETYPE_PDF, options, "custom")); } - @Test(expected = IllegalArgumentException.class) + @Test public void testBadOptions() { // Source, Target and Props are in dictionary.properties @@ -105,12 +129,12 @@ public class LegacyLocalTransformServiceRegistryTest extends AbstractRenditionIn Assert.assertTrue(transformServiceRegistry.isSupported(MIMETYPE_OPENXML_WORDPROCESSING, 1234, MIMETYPE_PDF, options, RENDITION_NAME)); try { - transformServiceRegistry.setEnabled(false); + setEnabled(false); Assert.assertFalse(transformServiceRegistry.isSupported(MIMETYPE_OPENXML_WORDPROCESSING, 1234, MIMETYPE_PDF, options, RENDITION_NAME)); } finally { - transformServiceRegistry.setEnabled(true); + setEnabled(true); } Assert.assertTrue(transformServiceRegistry.isSupported(MIMETYPE_OPENXML_WORDPROCESSING, 1234, MIMETYPE_PDF, options, RENDITION_NAME)); } diff --git a/src/test/java/org/alfresco/repo/rendition2/NoLocalTransformRenditionTest.java b/src/test/java/org/alfresco/repo/rendition2/NoneRenditionTest.java similarity index 88% rename from src/test/java/org/alfresco/repo/rendition2/NoLocalTransformRenditionTest.java rename to src/test/java/org/alfresco/repo/rendition2/NoneRenditionTest.java index 5954f46658..e6541fe950 100644 --- a/src/test/java/org/alfresco/repo/rendition2/NoLocalTransformRenditionTest.java +++ b/src/test/java/org/alfresco/repo/rendition2/NoneRenditionTest.java @@ -32,27 +32,26 @@ import org.junit.Test; import org.junit.experimental.categories.Category; /** - * Disables local transform and repeats the RenditionTests + * Repeats quick file rendition tests with local transforms and legacy transformers disabled. + * The Transform Service does not exist for the Community edition. * * @author adavis */ -public class NoLocalTransformRenditionTest extends RenditionTest +public class NoneRenditionTest extends AbstractRenditionTest { @BeforeClass public static void before() { AbstractRenditionIntegrationTest.before(); - System.setProperty("local.transform.service.enabled", "false"); + none(); } @AfterClass public static void after() { AbstractRenditionIntegrationTest.after(); - System.clearProperty("local.transform.service.enabled"); } - @Test @Override public void testTasRestApiRenditions() throws Exception diff --git a/src/test/java/org/alfresco/repo/rendition2/RenditionService2IntegrationTest.java b/src/test/java/org/alfresco/repo/rendition2/RenditionService2IntegrationTest.java index 8b538bd47d..016f099a08 100644 --- a/src/test/java/org/alfresco/repo/rendition2/RenditionService2IntegrationTest.java +++ b/src/test/java/org/alfresco/repo/rendition2/RenditionService2IntegrationTest.java @@ -36,6 +36,9 @@ import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import java.util.List; @@ -48,6 +51,13 @@ import static org.junit.Assert.assertNotEquals; */ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrationTest { + @BeforeClass + public static void before() + { + AbstractRenditionIntegrationTest.before(); + legacyLocal(); + } + // PDF transformation @Test diff --git a/src/test/java/org/alfresco/transform/client/model/config/LocalTransformServiceRegistryTest.java b/src/test/java/org/alfresco/transform/client/model/config/LocalTransformServiceRegistryTest.java new file mode 100644 index 0000000000..b554bc908f --- /dev/null +++ b/src/test/java/org/alfresco/transform/client/model/config/LocalTransformServiceRegistryTest.java @@ -0,0 +1,99 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 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.transform.client.model.config; + +import org.alfresco.repo.content.transform.TransformerDebug; +import org.alfresco.repo.content.transform.LocalTransformServiceRegistry; +import org.junit.After; +import org.junit.Before; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Properties; + +/** + * Extends the {@link TransformServiceRegistryImplTest} (used to test the config received from the Transform Service) + * so that configuration for the local transformations may be tested. This includes pipelines and options specific + * transform steps. + */ +public class LocalTransformServiceRegistryTest extends TransformServiceRegistryImplTest +{ + protected LocalTransformServiceRegistry registry; + private Properties properties; + @Mock + private TransformerDebug transformerDebug; + + public static final String TRANSFORM_SERVICE_CONFIG = "alfresco/transformers"; + + @Before + public void setUp() throws Exception + { + MockitoAnnotations.initMocks(this); + properties = new Properties(); + properties.setProperty( "name.url", "dummy"); + properties.setProperty("transformer1.url", "dummy"); + properties.setProperty("transformer2.url", "dummy"); + properties.setProperty("transformer3.url", "dummy"); + properties.setProperty("transformer4.url", "dummy"); + properties.setProperty("transformer5.url", "dummy"); + properties.setProperty( "libreoffice.url", "dummy"); + properties.setProperty( "tika.url", "dummy"); + properties.setProperty( "pdfrenderer.url", "dummy"); + properties.setProperty( "imagemagick.url", "dummy"); + + super.setUp(); + } + + protected TransformServiceRegistryImpl buildTransformServiceRegistryImpl() + { + registry = new LocalTransformServiceRegistry(); + registry.setJsonObjectMapper(JSON_OBJECT_MAPPER); + registry.setProperties(properties); + registry.setTransformerDebug(transformerDebug); + return registry; + } + + @After + public void tearDown() + { + // shut down + } + + protected String getTransformServiceConfig() + { + return TRANSFORM_SERVICE_CONFIG; + } + + // TODO test pipeline + + // TODO test strict mimetype check + + // TODO test retry transform on different mimetype + + // TODO test using system properties and alfresco-global.properties + + // TODO test the JsonConverter +} diff --git a/src/main/java/org/alfresco/transform/client/model/config/TransformBuilder.java b/src/test/java/org/alfresco/transform/client/model/config/TransformBuilder.java similarity index 67% rename from src/main/java/org/alfresco/transform/client/model/config/TransformBuilder.java rename to src/test/java/org/alfresco/transform/client/model/config/TransformBuilder.java index 024620fd88..a9d9316df9 100644 --- a/src/main/java/org/alfresco/transform/client/model/config/TransformBuilder.java +++ b/src/test/java/org/alfresco/transform/client/model/config/TransformBuilder.java @@ -29,12 +29,12 @@ import java.util.ArrayList; import java.util.List; /** - * Helper class that builds a {@link Transformer} given the source and target extensions and a pipeline of Transformers - * for creating intermediary content, or a set of failover transformers. + * Helper class that builds a {@link Transformer} given the source and target types and a pipeline of Transformers + * for creating intermediary content. */ public class TransformBuilder { - public Transformer buildPipeLine(String name, String version, List sourceAndTargetList, + public Transformer buildPipeLine(String name, List sourceAndTargetList, List transformerList) { List options = new ArrayList<>(transformerList.size()); @@ -47,13 +47,6 @@ public class TransformBuilder options.add(new TransformOptionGroup(t.isRequired(), t.getTransformer().getTransformOptions())); } }); - return new Transformer(name, version, options, sourceAndTargetList); + return new Transformer(name, options, sourceAndTargetList); } - - // TODO Commented out for now as it is unclear what the Transform service will support in terms of failover transformations. - // Note: The use of a list of Transformers rather than ChildTransformers, as the supplied actual options would have - // to match one or more of the transformer's options. Matching one or more options is not currently - // implemented by the TransformServiceRegistry - // public Transformer buildFailover(String name, String version, List sourceAndTargetList, - // List transformerList) } diff --git a/src/test/java/org/alfresco/transform/client/model/config/TransformServiceRegistryImplTest.java b/src/test/java/org/alfresco/transform/client/model/config/TransformServiceRegistryImplTest.java index febf322d00..6446fa1762 100644 --- a/src/test/java/org/alfresco/transform/client/model/config/TransformServiceRegistryImplTest.java +++ b/src/test/java/org/alfresco/transform/client/model/config/TransformServiceRegistryImplTest.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 @@ -26,6 +26,8 @@ package org.alfresco.transform.client.model.config; import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -35,7 +37,6 @@ import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; -import java.io.InputStreamReader; import java.io.Reader; import java.util.Arrays; import java.util.Collections; @@ -50,83 +51,48 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +/** + * Test the config received from the Transform Service about what it supports. + */ public class TransformServiceRegistryImplTest { - public static final String GIF = "gif"; - public static final String JPEG = "jpeg"; - public static final String PNG = "png"; - public static final String TIFF = "tiff"; - public static final String PDF = "pdf"; - public static final String DOC = "doc"; - public static final String XLS = "xls"; - public static final String PPT = "ppt"; - public static final String DOCX = "docx"; - public static final String XLSX = "xlsx"; - public static final String PPTX = "pptx"; - public static final String MSG = "msg"; - public static final String TXT = "txt"; + private static Log log = LogFactory.getLog(TransformServiceRegistryImplTest.class); - public static final String GIF_MIMETYPE = "image/gif"; - public static final String JPEG_MIMETYPE = "image/jpeg"; - public static final String PNG_MIMETYPE = "image/png"; - public static final String TIFF_MIMETYPE = "image/tiff"; - public static final String PDF_MIMETYPE = "application/pdf"; - public static final String DOC_MIMETYPE = "application/msword"; - public static final String XLS_MIMETYPE = "application/vnd.ms-excel"; - public static final String PPT_MIMETYPE = "application/vnd.ms-powerpoint"; - public static final String DOCX_MIMETYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; - public static final String XLSX_MIMETYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; - public static final String PPTX_MIMETYPE = "application/vnd.openxmlformats-officedocument.presentationml.presentation"; - public static final String MSG_MIMETYPE = "application/vnd.ms-outlook"; - public static final String TXT_MIMETYPE = "text/plain"; + public static final String GIF = "image/gif"; + public static final String JPEG = "image/jpeg"; + public static final String PNG = "image/png"; + public static final String TIFF = "image/tiff"; + public static final String PDF = "application/pdf"; + public static final String DOC = "application/msword"; + public static final String XLS = "application/vnd.ms-excel"; + public static final String PPT = "application/vnd.ms-powerpoint"; + public static final String MSG = "application/vnd.ms-outlook"; + public static final String TXT = "text/plain"; public static final String TRANSFORM_SERVICE_CONFIG = "alfresco/transform-service-config.json"; public static final ObjectMapper JSON_OBJECT_MAPPER = new ObjectMapper(); private TransformServiceRegistryImpl registry; - private TransformBuilder builder; - private Transformer transformer; - private ExtensionMap extensionMap; + protected TransformBuilder builder; + protected Transformer transformer; @Before public void setUp() throws Exception { - extensionMap = new ExtensionMap() - { - private Map map = new HashMap<>(); - - { - map.put(GIF, GIF_MIMETYPE); - map.put(JPEG, JPEG_MIMETYPE); - map.put(PNG, PNG_MIMETYPE); - map.put(TIFF, TIFF_MIMETYPE); - map.put(PDF, PDF_MIMETYPE); - map.put(DOC, DOC_MIMETYPE); - map.put(XLS, XLS_MIMETYPE); - map.put(PPT, PPT_MIMETYPE); - map.put(DOCX, DOCX_MIMETYPE); - map.put(XLSX, XLSX_MIMETYPE); - map.put(PPTX, PPTX_MIMETYPE); - map.put(MSG, MSG_MIMETYPE); - map.put(TXT, TXT_MIMETYPE); - } - - @Override - public String toMimetype(String extension) - { - return map.get(extension); - } - }; - registry = buildTransformServiceRegistryImpl(); - builder = new TransformBuilder(); } - private TransformServiceRegistryImpl buildTransformServiceRegistryImpl() + protected TransformServiceRegistryImpl buildTransformServiceRegistryImpl() { - TransformServiceRegistryImpl registry = new TransformServiceRegistryImpl(); - registry.setExtensionMap(extensionMap); + TransformServiceRegistryImpl registry = new TransformServiceRegistryImpl() + { + @Override + protected Log getLog() + { + return log; + } + }; registry.setJsonObjectMapper(JSON_OBJECT_MAPPER); return registry; } @@ -137,6 +103,11 @@ public class TransformServiceRegistryImplTest // shut down } + protected String getTransformServiceConfig() + { + return TRANSFORM_SERVICE_CONFIG; + } + private void assertAddToPossibleOptions(TransformOptionGroup transformOptionGroup, String actualOptionNames, String expectedNames, String expectedRequired) { Map actualOptions = buildActualOptions(actualOptionNames); @@ -188,7 +159,7 @@ public class TransformServiceRegistryImplTest private void assertTransformOptions(List transformOptions) { - transformer = new Transformer("name", "1", + transformer = new Transformer("name", transformOptions, Arrays.asList( new SupportedSourceAndTarget(DOC, TXT, -1), @@ -197,44 +168,56 @@ public class TransformServiceRegistryImplTest registry = buildTransformServiceRegistryImpl(); registry.register(transformer); - assertTrue(registry.isSupported(XLS_MIMETYPE, 1024, TXT_MIMETYPE, Collections.emptyMap(), null)); - assertTrue(registry.isSupported(XLS_MIMETYPE, 1024000, TXT_MIMETYPE, null, null)); - assertFalse(registry.isSupported(XLS_MIMETYPE, 1024001, TXT_MIMETYPE, Collections.emptyMap(), null)); - assertTrue(registry.isSupported(DOC_MIMETYPE, 1024001, TXT_MIMETYPE, null, null)); + assertTrue(registry.isSupported(XLS, 1024, TXT, Collections.emptyMap(), null)); + assertTrue(registry.isSupported(XLS, 1024000, TXT, null, null)); + assertFalse(registry.isSupported(XLS, 1024001, TXT, Collections.emptyMap(), null)); + assertTrue(registry.isSupported(DOC, 1024001, TXT, null, null)); } - private void assertSupported(String sourceExt, long sourceSizeInBytes, String targetExt, + private void assertTransformerName(String sourceMimetype, long sourceSizeInBytes, String targetMimetype, + Map actualOptions, String expectedTransformerName, + Transformer... transformers) + { + buildAndPopulateRegistry(transformers); + String transformerName = registry.getTransformerName(sourceMimetype, sourceSizeInBytes, targetMimetype, actualOptions, null); + assertEquals(sourceMimetype+" to "+targetMimetype+" should have returned "+expectedTransformerName, expectedTransformerName, transformerName); + } + + private void assertSupported(String sourceMimetype, long sourceSizeInBytes, String targetMimetype, Map actualOptions, String unsupportedMsg) { - assertSupported(sourceExt, sourceSizeInBytes, targetExt, actualOptions, unsupportedMsg, transformer); + assertSupported(sourceMimetype, sourceSizeInBytes, targetMimetype, actualOptions, unsupportedMsg, transformer); } - private void assertSupported(String sourceExt, long sourceSizeInBytes, String targetExt, + private void assertSupported(String sourceMimetype, long sourceSizeInBytes, String targetMimetype, Map actualOptions, String unsupportedMsg, Transformer... transformers) + { + buildAndPopulateRegistry(transformers); + assertSupported(sourceMimetype, sourceSizeInBytes, targetMimetype, actualOptions, null, unsupportedMsg); + } + + private void buildAndPopulateRegistry(Transformer[] transformers) { registry = buildTransformServiceRegistryImpl(); for (Transformer transformer : transformers) { registry.register(transformer); } - assertSupported(sourceExt, sourceSizeInBytes, targetExt, actualOptions, null, unsupportedMsg); } - private void assertSupported(String sourceExt, long sourceSizeInBytes, String targetExt, - Map actualOptions, String transformName, + private void assertSupported(String sourceMimetype, long sourceSizeInBytes, String targetMimetype, + Map actualOptions, String renditionName, String unsupportedMsg) { - String sourceMimetype = extensionMap.toMimetype(sourceExt); - String targetMimetype = extensionMap.toMimetype(targetExt); - boolean supported = registry.isSupported(sourceMimetype, sourceSizeInBytes, targetMimetype, actualOptions, transformName); + boolean supported = registry.isSupported(sourceMimetype, sourceSizeInBytes, targetMimetype, actualOptions, renditionName); if (unsupportedMsg == null || unsupportedMsg.isEmpty()) { - assertTrue(sourceExt+" to "+targetExt+" should be SUPPORTED", supported); + assertTrue(sourceMimetype+" to "+targetMimetype+" should be SUPPORTED", supported); } else { - assertFalse(sourceExt+" to "+targetExt+" should NOT be supported", supported); + assertFalse(sourceMimetype+" to "+targetMimetype+" should NOT be supported", supported); } } @@ -252,7 +235,7 @@ public class TransformServiceRegistryImplTest @Test public void testReadWriteJson() throws IOException { - Transformer libreOffice = new Transformer("libreOffice", "1", + Transformer libreoffice = new Transformer("libreoffice", null, // there are no options Arrays.asList( new SupportedSourceAndTarget(DOC, PDF, -1), @@ -260,7 +243,7 @@ public class TransformServiceRegistryImplTest new SupportedSourceAndTarget(PPT, PDF, -1), new SupportedSourceAndTarget(MSG, PDF, -1))); - Transformer pdfRenderer = new Transformer("pdfRenderer", "1", + Transformer pdfrenderer = new Transformer("pdfrenderer", Arrays.asList( new TransformOptionValue(false, "page"), new TransformOptionValue(false, "width"), @@ -270,7 +253,7 @@ public class TransformServiceRegistryImplTest Arrays.asList( new SupportedSourceAndTarget(PDF, PNG, -1))); - Transformer tika = new Transformer("tika", "1", + Transformer tika = new Transformer("tika", Arrays.asList( new TransformOptionValue(false, "transform"), new TransformOptionValue(false, "includeContents"), @@ -284,7 +267,7 @@ public class TransformServiceRegistryImplTest new SupportedSourceAndTarget(PPT, TXT, -1), new SupportedSourceAndTarget(MSG, TXT, -1))); - Transformer imageMagick = new Transformer("imageMagick", "1", + Transformer imagemagick = new Transformer("imagemagick", Arrays.asList( new TransformOptionValue(false, "alphaRemove"), new TransformOptionValue(false, "autoOrient"), @@ -322,7 +305,7 @@ public class TransformServiceRegistryImplTest new SupportedSourceAndTarget(TIFF, PNG, -1), new SupportedSourceAndTarget(TIFF, TIFF, -1))); - Transformer officeToImage = builder.buildPipeLine("officeToImageViaPdf", "1", + Transformer officeToImage = builder.buildPipeLine("transformer1", Arrays.asList( new SupportedSourceAndTarget(DOC, GIF, -1), new SupportedSourceAndTarget(DOC, JPEG, -1), @@ -341,11 +324,11 @@ public class TransformServiceRegistryImplTest new SupportedSourceAndTarget(MSG, PNG, -1), new SupportedSourceAndTarget(MSG, TIFF, -1)), Arrays.asList( - new ChildTransformer(false, libreOffice), // to pdf - new ChildTransformer(false, pdfRenderer), // to png - new ChildTransformer(true, imageMagick))); // to other image formats + new ChildTransformer(false, libreoffice), // to pdf + new ChildTransformer(false, pdfrenderer), // to png + new ChildTransformer(true, imagemagick))); // to other image formats - List transformers1 = Arrays.asList(libreOffice, tika, pdfRenderer, imageMagick, officeToImage); + List transformers1 = Arrays.asList(libreoffice, tika, pdfrenderer, imagemagick, officeToImage); File tempFile = File.createTempFile("test", ".json"); ObjectMapper objectMapper = new ObjectMapper(); @@ -367,97 +350,89 @@ public class TransformServiceRegistryImplTest assertSupported(PDF, 1234, PNG, null, null, ""); // pdfrenderer assertSupported(JPEG,1234, GIF, null, null, ""); // imagemagick assertSupported(MSG, 1234, TXT, null, null, ""); // tika - assertSupported(MSG, 1234, GIF, null, null, ""); // officeToImageViaPdf - assertSupported(DOC, 1234, PNG, null, null, ""); // officeToImageViaPdf + assertSupported(MSG, 1234, GIF, null, null, ""); // transformer1 (officeToImageViaPdf) + assertSupported(DOC, 1234, PNG, null, null, ""); // transformer1 (officeToImageViaPdf) } } @Test public void testJsonConfig() throws IOException { - try (Reader reader = new BufferedReader(new InputStreamReader(getClass().getClassLoader(). - getResourceAsStream(TRANSFORM_SERVICE_CONFIG)))) - { - registry.register(reader); + registry.register(getTransformServiceConfig()); - // Check the count of transforms supported - assertEquals("The number of UNIQUE source to target mimetypes transforms has changed. Config change?", - 63, countSupportedTransforms(true)); - assertEquals("The number of source to target mimetypes transforms has changed. " + - "There may be multiple transformers for the same combination. Config change?", - 63, countSupportedTransforms(false)); + // Check the count of transforms supported + assertEquals("The number of UNIQUE source to target mimetypes transforms has changed. Config change?", + 60, countSupportedTransforms(true)); + assertEquals("The number of source to target mimetypes transforms has changed. " + + "There may be multiple transformers for the same combination. Config change?", + 60, countSupportedTransforms(false)); - // Check a supported transform for each transformer. - assertSupported(DOC, 1234, PDF, null, null, ""); // libreoffice - assertSupported(DOC, 1234, PDF, null, null, ""); // libreoffice - assertSupported(PDF, 1234, PNG, null, null, ""); // pdfrenderer - assertSupported(JPEG,1234, GIF, null, null, ""); // imagemagick - assertSupported(MSG, 1234, TXT, null, null, ""); // tika - assertSupported(MSG, 1234, GIF, null, null, ""); // officeToImageViaPdf + // Check a supported transform for each transformer. + assertSupported(DOC, 1234, PDF, null, null, ""); // libreoffice + assertSupported(DOC, 1234, PDF, null, null, ""); // libreoffice + assertSupported(PDF, 1234, PNG, null, null, ""); // pdfrenderer + assertSupported(JPEG,1234, GIF, null, null, ""); // imagemagick + assertSupported(MSG, 1234, TXT, null, null, ""); // tika + assertSupported(MSG, 1234, GIF, null, null, ""); // officeToImageViaPdf - Map invalidPdfOptions = new HashMap<>(); - invalidPdfOptions.put("allowEnlargement", "false"); - assertSupported(DOC, 1234, PDF, invalidPdfOptions, null, "Invalid as there is a extra option"); - } + Map invalidPdfOptions = new HashMap<>(); + invalidPdfOptions.put("allowEnlargement", "false"); + assertSupported(DOC, 1234, PDF, invalidPdfOptions, null, "Invalid as there is a extra option"); } @Test public void testJsonPipeline() throws IOException { - try (Reader reader = new BufferedReader(new InputStreamReader(getClass().getClassLoader(). - getResourceAsStream("alfresco/transform-service-config-test1.json")))) - { - registry.register(reader); + registry.register("alfresco/transform-service-config-test1.json"); - // Check the count of transforms supported - assertEquals("The number of UNIQUE source to target mimetypes transforms has changed. Config change?", - 4, countSupportedTransforms(true)); - assertEquals("The number of source to target mimetypes transforms has changed. " + - "There may be multiple transformers for the same combination. Config change?", - 4, countSupportedTransforms(false)); + // Check the count of transforms supported + assertEquals("The number of UNIQUE source to target mimetypes transforms has changed. Config change?", + 4, countSupportedTransforms(true)); + assertEquals("The number of source to target mimetypes transforms has changed. " + + "There may be multiple transformers for the same combination. Config change?", + 4, countSupportedTransforms(false)); - ConcurrentMap> transformer = - registry.transformers.get("officeToImageViaPdf"); + ConcurrentMap> transformer = + registry.transformers.get("officeToImageViaPdf"); - // Check required and optional default correctly - ConcurrentMap> transformsToWord = - registry.transformers.get("application/msword"); - List supportedTransforms = transformsToWord.get("image/gif"); - TransformServiceRegistryImpl.SupportedTransform supportedTransform = supportedTransforms.get(0); + // Check required and optional default correctly + ConcurrentMap> transformsToWord = + registry.transformers.get(DOC); + List supportedTransforms = transformsToWord.get(GIF); + TransformServiceRegistryImpl.SupportedTransform supportedTransform = supportedTransforms.get(0); - TransformOptionGroup imageMagick = (TransformOptionGroup)supportedTransform.transformOptions.transformOptions.get(0); - TransformOptionGroup pdf = (TransformOptionGroup)supportedTransform.transformOptions.transformOptions.get(1); + TransformOptionGroup imagemagick = (TransformOptionGroup)supportedTransform.transformOptions.transformOptions.get(0); + TransformOptionGroup pdf = (TransformOptionGroup)supportedTransform.transformOptions.transformOptions.get(1); - TransformOptionValue alphaRemove = (TransformOptionValue)imageMagick.transformOptions.get(0); - TransformOptionGroup crop = (TransformOptionGroup)imageMagick.transformOptions.get(4); - TransformOptionValue cropGravity = (TransformOptionValue)crop.transformOptions.get(0); - TransformOptionValue cropWidth = (TransformOptionValue)crop.transformOptions.get(1); + TransformOptionValue alphaRemove = (TransformOptionValue)imagemagick.transformOptions.get(0); + TransformOptionGroup crop = (TransformOptionGroup)imagemagick.transformOptions.get(4); + TransformOptionValue cropGravity = (TransformOptionValue)crop.transformOptions.get(0); + TransformOptionValue cropWidth = (TransformOptionValue)crop.transformOptions.get(1); - assertTrue("The holding group should be required", supportedTransform.transformOptions.isRequired()); - assertTrue("imageMagick should be required as it is set", imageMagick.isRequired()); - assertFalse("pdf should be optional as required is not set", pdf.isRequired()); - assertEquals("alphaRemove", alphaRemove.getName()); - assertEquals("cropGravity", cropGravity.getName()); - assertEquals("cropWidth", cropWidth.getName()); - assertFalse("alphaRemove should be optional as required is not set", alphaRemove.isRequired()); - assertFalse("crop should be optional as required is not set", crop.isRequired()); - assertTrue("cropGravity should be required as it is set", cropGravity.isRequired()); - assertFalse("cropWidth should be optional as required is not set", cropWidth.isRequired()); + assertTrue("The holding group should be required", supportedTransform.transformOptions.isRequired()); + assertFalse("imagemagick should be optional as it is not set", imagemagick.isRequired()); + assertFalse("pdf should be optional as required is not set", pdf.isRequired()); + assertEquals("alphaRemove", alphaRemove.getName()); + assertEquals("cropGravity", cropGravity.getName()); + assertEquals("cropWidth", cropWidth.getName()); + assertFalse("alphaRemove should be optional as required is not set", alphaRemove.isRequired()); + assertFalse("crop should be optional as required is not set", crop.isRequired()); + assertTrue("cropGravity should be required as it is set", cropGravity.isRequired()); + assertFalse("cropWidth should be optional as required is not set", cropWidth.isRequired()); - // Check a supported transform for each transformer. - assertSupported(DOC,1234, GIF, null, null, ""); - assertSupported(DOC,1234, PNG, null, null, ""); - assertSupported(DOC,1234, JPEG, null, null, ""); - assertSupported(DOC,1234, TIFF, null, null, ""); + // Check a supported transform for each transformer. + assertSupported(DOC,1234, GIF, null, null, ""); + assertSupported(DOC,1234, PNG, null, null, ""); + assertSupported(DOC,1234, JPEG, null, null, ""); + assertSupported(DOC,1234, TIFF, null, null, ""); - Map actualOptions = new HashMap<>(); - actualOptions.put("thumbnail", "true"); - actualOptions.put("resizeWidth", "100"); - actualOptions.put("resizeHeight", "100"); - actualOptions.put("allowEnlargement", "false"); - actualOptions.put("maintainAspectRatio", "true"); - assertSupported(DOC,1234, PNG, actualOptions, null, ""); - } + Map actualOptions = new HashMap<>(); + actualOptions.put("thumbnail", "true"); + actualOptions.put("resizeWidth", "100"); + actualOptions.put("resizeHeight", "100"); + actualOptions.put("allowEnlargement", "false"); + actualOptions.put("maintainAspectRatio", "true"); + assertSupported(DOC,1234, PNG, actualOptions, null, ""); } private int countSupportedTransforms(boolean unique) @@ -627,7 +602,7 @@ public class TransformServiceRegistryImplTest @Test public void testSupported() { - transformer = new Transformer("name", "1", + transformer = new Transformer("name", Arrays.asList( new TransformOptionValue(false, "page"), new TransformOptionValue(false, "width"), @@ -653,7 +628,7 @@ public class TransformServiceRegistryImplTest public void testCache() { // Note: transformNames are an alias for a set of actualOptions and the target mimetpe. The source mimetype may change. - transformer = new Transformer("name", "1", + transformer = new Transformer("name", Arrays.asList( new TransformOptionValue(false, "page"), new TransformOptionValue(false, "width"), @@ -667,18 +642,50 @@ public class TransformServiceRegistryImplTest assertSupported(DOC, 1024, GIF, null, "doclib", ""); assertSupported(MSG, 1024, GIF, null, "doclib", ""); - assertEquals(102400L, registry.getMaxSize(DOC_MIMETYPE, GIF_MIMETYPE, null, "doclib")); - assertEquals(-1L, registry.getMaxSize(MSG_MIMETYPE, GIF_MIMETYPE, null, "doclib")); + assertEquals(102400L, registry.getMaxSize(DOC, GIF, null, "doclib")); + assertEquals(-1L, registry.getMaxSize(MSG, GIF, null, "doclib")); // Change the cached value and try and check we are now using the cached value. - registry.cachedMaxSizes.get("doclib").put(DOC_MIMETYPE, 1234L); - assertEquals(1234L, registry.getMaxSize(DOC_MIMETYPE, GIF_MIMETYPE, null, "doclib")); + List supportedTransforms = registry.cachedSupportedTransformList.get("doclib").get(DOC); + supportedTransforms.get(0).maxSourceSizeBytes = 1234L; + assertEquals(1234L, registry.getMaxSize(DOC, GIF, null, "doclib")); + } + + @Test + public void testGetTransformerName() + { + Transformer t1 = new Transformer("transformer1", null, + Arrays.asList(new SupportedSourceAndTarget(MSG, GIF, 100, 50))); + Transformer t2 = new Transformer("transformer2", null, + Arrays.asList(new SupportedSourceAndTarget(MSG, GIF, 200, 60))); + Transformer t3 = new Transformer("transformer3", null, + Arrays.asList(new SupportedSourceAndTarget(MSG, GIF, 200, 40))); + Transformer t4 = new Transformer("transformer4", null, + Arrays.asList(new SupportedSourceAndTarget(MSG, GIF, -1, 100))); + Transformer t5 = new Transformer("transformer5", null, + Arrays.asList(new SupportedSourceAndTarget(MSG, GIF, -1, 80))); + + Map actualOptions = null; + + // Select on size - priority is ignored + assertTransformerName(MSG, 100, GIF, actualOptions, "transformer1", t1, t2); + assertTransformerName(MSG, 150, GIF, actualOptions, "transformer2", t1, t2); + assertTransformerName(MSG, 250, GIF, actualOptions, null, t1, t2); + // Select on priority - t1, t2 and t4 are discarded. + // t3 is a higher priority and has a larger size than t1 and t2. + // Similar story fo t4 with t5. + assertTransformerName(MSG, 100, GIF, actualOptions, "transformer3", t1, t2, t3, t4, t5); + assertTransformerName(MSG, 200, GIF, actualOptions, "transformer3", t1, t2, t3, t4, t5); + // Select on size and priority, t1 and t2 discarded + assertTransformerName(MSG, 200, GIF, actualOptions, "transformer3", t1, t2, t3, t4); + assertTransformerName(MSG, 300, GIF, actualOptions, "transformer4", t1, t2, t3, t4); + assertTransformerName(MSG, 300, GIF, actualOptions, "transformer5", t1, t2, t3, t4, t5); } @Test public void testMultipleTransformers() { - Transformer transformer1 = new Transformer("transformer1", "1", + Transformer transformer1 = new Transformer("transformer1", Arrays.asList( new TransformOptionValue(false, "page"), new TransformOptionValue(false, "width"), @@ -688,7 +695,7 @@ public class TransformServiceRegistryImplTest new SupportedSourceAndTarget(DOC, JPEG, -1), new SupportedSourceAndTarget(MSG, GIF, -1))); - Transformer transformer2 = new Transformer("transformer2", "1", + Transformer transformer2 = new Transformer("transformer2", Arrays.asList( new TransformOptionValue(false, "opt1"), new TransformOptionValue(false, "opt2")), @@ -696,7 +703,7 @@ public class TransformServiceRegistryImplTest new SupportedSourceAndTarget(PDF, GIF, -1), new SupportedSourceAndTarget(PPT, JPEG, -1))); - Transformer transformer3 = new Transformer("transformer3", "1", + Transformer transformer3 = new Transformer("transformer3", Arrays.asList( new TransformOptionValue(false, "opt1")), Arrays.asList( @@ -725,13 +732,13 @@ public class TransformServiceRegistryImplTest @Test public void testPipeline() { - Transformer transformer1 = new Transformer("transformer1", "1", + Transformer transformer1 = new Transformer("transformer1", null, // there are no options Arrays.asList( new SupportedSourceAndTarget(DOC, PDF, -1), new SupportedSourceAndTarget(MSG, PDF, -1))); - Transformer transformer2 = new Transformer("transformer2", "1", + Transformer transformer2 = new Transformer("transformer2", Arrays.asList( new TransformOptionValue(false, "page"), new TransformOptionValue(false, "width"), @@ -764,7 +771,7 @@ public class TransformServiceRegistryImplTest private void buildPipelineTransformer(Transformer transformer1, Transformer transformer2) { - transformer = builder.buildPipeLine("officeToImage", "1", + transformer = builder.buildPipeLine("transformer1", Arrays.asList( new SupportedSourceAndTarget(DOC, GIF, -1), new SupportedSourceAndTarget(DOC, JPEG, -1), @@ -773,14 +780,4 @@ public class TransformServiceRegistryImplTest new ChildTransformer(false, transformer1), new ChildTransformer(true, transformer2))); } - - @Test(expected = IllegalArgumentException.class) - public void textMissingMimetype() - { - transformer = new Transformer("name", "1", - null, // there are no options - Arrays.asList( - new SupportedSourceAndTarget("rubbish", PDF, -1))); - registry.register(transformer); - } } \ No newline at end of file diff --git a/src/test/resources/alfresco/transform-service-config-test1.json b/src/test/resources/alfresco/transform-service-config-test1.json index c3921c391d..9eba9ea9e6 100644 --- a/src/test/resources/alfresco/transform-service-config-test1.json +++ b/src/test/resources/alfresco/transform-service-config-test1.json @@ -1,49 +1,47 @@ -[ - { - "name": "officeToImageViaPdf", - "version": "1", - "transformOptions": [ - { - "group": { - "required": true, - "transformOptions": [ - {"value": {"name": "alphaRemove"}}, - {"value": {"name": "autoOrient"}}, - {"value": {"name": "startPage"}}, - {"value": {"name": "endPage"}}, - {"group": {"transformOptions": [ - {"value": {"required": "true", "name": "cropGravity"}}, - {"value": {"name": "cropWidth"}}, - {"value": {"name": "cropHeight"}}, - {"value": {"name": "cropPercentage"}}, - {"value": {"name": "cropXOffset"}}, - {"value": {"name": "cropYOffset"}} - ]}}, - {"group": {"transformOptions": [ - {"value": {"name": "thumbnail"}}, - {"value": {"name": "resizeHeight"}}, - {"value": {"name": "resizeWidth"}}, - {"value": {"name": "resizePercentage"}}, - {"value": {"name": "allowEnlargement"}}, - {"value": {"name": "maintainAspectRatio"}} - ]}} - ]}},{ - "group": { - "transformOptions": [ - {"value": {"name": "page"}}, - {"value": {"name": "width"}}, - {"value": {"name": "height"}}, - {"value": {"name": "allowPdfEnlargement"}}, - {"value": {"name": "maintainPdfAspectRatio"}} - ] - } - } +{ + "transformOptions": { + "pdfrendererOptions": [ + {"value": {"name": "page"}}, + {"value": {"name": "width"}}, + {"value": {"name": "height"}}, + {"value": {"name": "allowPdfEnlargement"}}, + {"value": {"name": "maintainPdfAspectRatio"}} ], - "supportedSourceAndTargetList": [ - {"sourceExt": "doc", "targetExt": "gif" }, - {"sourceExt": "doc", "targetExt": "jpeg"}, - {"sourceExt": "doc", "targetExt": "png" }, - {"sourceExt": "doc", "targetExt": "tiff"} + "imagemagickOptions": [ + {"value": {"name": "alphaRemove"}}, + {"value": {"name": "autoOrient"}}, + {"value": {"name": "startPage"}}, + {"value": {"name": "endPage"}}, + {"group": {"transformOptions": [ + {"value": {"required": "true", "name": "cropGravity"}}, + {"value": {"name": "cropWidth"}}, + {"value": {"name": "cropHeight"}}, + {"value": {"name": "cropPercentage"}}, + {"value": {"name": "cropXOffset"}}, + {"value": {"name": "cropYOffset"}} + ]}}, + {"group": {"transformOptions": [ + {"value": {"name": "thumbnail"}}, + {"value": {"name": "resizeHeight"}}, + {"value": {"name": "resizeWidth"}}, + {"value": {"name": "resizePercentage"}}, + {"value": {"name": "allowEnlargement"}}, + {"value": {"name": "maintainAspectRatio"}} + ]}} ] - } -] \ No newline at end of file + }, + "transformers": [ + { + "supportedSourceAndTargetList": [ + {"sourceMediaType": "application/msword", "targetMediaType": "image/gif" }, + {"sourceMediaType": "application/msword", "targetMediaType": "image/jpeg"}, + {"sourceMediaType": "application/msword", "targetMediaType": "image/png" }, + {"sourceMediaType": "application/msword", "targetMediaType": "image/tiff"} + ], + "transformOptions": [ + "pdfrendererOptions", + "imagemagickOptions" + ] + } + ] +} \ No newline at end of file