From bebd04f49e1fbd16bbe64dce12d16b712d0dac15 Mon Sep 17 00:00:00 2001 From: alandavis Date: Fri, 2 Aug 2019 17:46:33 +0100 Subject: [PATCH] REPO-4329 Configure Custom Renditions (#537) Break done of changes: - Removed rendition definition spring beans and created 0100-baseRenditions.json. Changes are picked up periodically by the running system. - Refactored TransformServiceRegistryImpl to extract ConfigFileFinder to reads json from resources, files or directories. - Refactored TransformServiceRegistryImpl to extract ConfigScheduler to periodically reads config data. - Used ConfigFileFinder and ConfigScheduler in RenditionDefinition2Impl to read rendition defs. - Removed the need for a current 'Data' parameter to be passed to register methods when adding transforms or renditions. - Changes in test classes of AbstractRenditionIntegrationTests to force the config to be read once before each test, as tests were sometimes not getting the correct config due to scheduling. --- pom.xml | 7 + .../LocalTransformServiceRegistry.java | 38 +-- .../content/transform/TransformerDebug.java | 8 +- .../LegacyTransformServiceRegistry.java | 5 + .../RenditionDefinitionRegistry2Impl.java | 228 +++++++++++++++++- .../client/model/config/CombinedConfig.java | 176 ++++---------- .../config/TransformServiceRegistryImpl.java | 194 +++------------ .../org/alfresco/util/ConfigFileFinder.java | 164 +++++++++++++ .../org/alfresco/util/ConfigScheduler.java | 223 +++++++++++++++++ .../org/alfresco/util/ResourceFinder.java | 60 ++--- .../alfresco/rendition-services2-context.xml | 111 +-------- .../renditions/0100-baseRenditions.json | 69 ++++++ .../resources/alfresco/repository.properties | 10 + .../0150-officeToImageViaPdf.json | 0 .../AbstractRenditionIntegrationTest.java | 13 + .../rendition2/AbstractRenditionTest.java | 2 +- ...ansformServiceRegistryIntegrationTest.java | 8 + ...ansformServiceRegistryIntegrationTest.java | 27 ++- .../rendition2/RenditionService2Test.java | 51 +++- ...calTransformServiceRegistryConfigTest.java | 70 +++--- .../TransformServiceRegistryConfigTest.java | 17 +- 21 files changed, 973 insertions(+), 508 deletions(-) create mode 100644 src/main/java/org/alfresco/util/ConfigFileFinder.java create mode 100644 src/main/java/org/alfresco/util/ConfigScheduler.java create mode 100644 src/main/resources/alfresco/renditions/0100-baseRenditions.json rename src/main/resources/alfresco/{transformers => transforms}/0150-officeToImageViaPdf.json (100%) diff --git a/pom.xml b/pom.xml index d063143540..e86bbf1e1b 100644 --- a/pom.xml +++ b/pom.xml @@ -64,6 +64,7 @@ 5.15.9 2.0.16 3.3.2 + 2.9.9 @@ -545,6 +546,12 @@ 1.1 + + com.fasterxml.jackson.core + jackson-databind + ${dependency.jackson.version} + + org.alfresco.surf diff --git a/src/main/java/org/alfresco/repo/content/transform/LocalTransformServiceRegistry.java b/src/main/java/org/alfresco/repo/content/transform/LocalTransformServiceRegistry.java index 94fa728a32..95b2dd7aff 100644 --- a/src/main/java/org/alfresco/repo/content/transform/LocalTransformServiceRegistry.java +++ b/src/main/java/org/alfresco/repo/content/transform/LocalTransformServiceRegistry.java @@ -62,7 +62,7 @@ public class LocalTransformServiceRegistry extends TransformServiceRegistryImpl private static final String URL = ".url"; static final String STRICT_MIMETYPE_CHECK_WHITELIST_MIMETYPES = "transformer.strict.mimetype.check.whitelist.mimetypes"; - class LocalData extends TransformServiceRegistryImpl.Data + public class LocalData extends TransformServiceRegistryImpl.Data { private Map localTransforms = new HashMap<>(); } @@ -130,35 +130,40 @@ public class LocalTransformServiceRegistry extends TransformServiceRegistryImpl } @Override - protected Data readConfig() throws IOException + public boolean readConfig() throws IOException { - Data data = createData(); CombinedConfig combinedConfig = new CombinedConfig(getLog()); List urls = getTEngineUrls(); - boolean successReadingRemoteConfig = combinedConfig.addRemoteConfig(urls, "T-Engine"); - setSuccessReadingRemoteConfig(data, successReadingRemoteConfig); - combinedConfig.addLocalConfig("alfresco/transformers"); - if (!pipelineConfigDir.isEmpty()) + boolean successReadingConfig = combinedConfig.addRemoteConfig(urls, "T-Engine"); + successReadingConfig &= combinedConfig.addLocalConfig("alfresco/transforms"); + if (pipelineConfigDir != null && !pipelineConfigDir.isBlank()) { - combinedConfig.addLocalConfig(pipelineConfigDir); + successReadingConfig &= combinedConfig.addLocalConfig(pipelineConfigDir); } - combinedConfig.register(data, this); - return data; + combinedConfig.register(this); + return successReadingConfig; } @Override - protected Data createData() + public synchronized LocalData getData() + { + return (LocalData)super.getData(); + } + + @Override + public Data createData() { return new LocalData(); } @Override - protected void register(Data data, Transformer transformer, String baseUrl, String readFrom) + protected void register(Transformer transformer, String baseUrl, String readFrom) { try { String name = transformer.getTransformerName(); - Map localTransforms = ((LocalData)data).localTransforms; + LocalData data = getData(); + Map localTransforms = data.localTransforms; if (name == null || localTransforms.get(name) != null) { throw new IllegalArgumentException("Local transformer names must exist and be unique (" + name + ")."+ @@ -238,7 +243,7 @@ public class LocalTransformServiceRegistry extends TransformServiceRegistryImpl } } localTransforms.put(name, localTransform); - super.register(data, transformer, baseUrl, readFrom); + super.register(transformer, baseUrl, readFrom); } catch (IllegalArgumentException e) { @@ -396,7 +401,7 @@ public class LocalTransformServiceRegistry extends TransformServiceRegistryImpl if (getFirstTime()) { setFirstTime(false); - transformerDebug.debug("Local transforms "+getCounts()+" are " + (enabled ? "enabled" : "disabled")); + transformerDebug.debug("Local transforms "+getData()+" are " + (enabled ? "enabled" : "disabled")); } return enabled @@ -419,7 +424,8 @@ public class LocalTransformServiceRegistry extends TransformServiceRegistryImpl String sourceMimetype, String targetMimetype, long sourceSizeInBytes) { String name = getTransformerName(sourceMimetype, sourceSizeInBytes, targetMimetype, actualOptions, renditionName); - Map localTransforms = ((LocalData)data).localTransforms; + LocalData data = getData(); + Map localTransforms = data.localTransforms; return localTransforms.get(name); } } 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 67b3cdc3f1..606681f2ef 100644 --- a/src/main/java/org/alfresco/repo/content/transform/TransformerDebug.java +++ b/src/main/java/org/alfresco/repo/content/transform/TransformerDebug.java @@ -1717,7 +1717,13 @@ public class TransformerDebug implements ApplicationContextAware finally { setStringBuilder(null); - renditionDefinitionRegistry2.unregister(testRenditionName); + try + { + renditionDefinitionRegistry2.unregister(testRenditionName); + } + catch (IllegalArgumentException ignore) + { + } deleteSourceNode(sourceNodeRef); } return sb.toString(); diff --git a/src/main/java/org/alfresco/repo/rendition2/LegacyTransformServiceRegistry.java b/src/main/java/org/alfresco/repo/rendition2/LegacyTransformServiceRegistry.java index 1e73405a80..4300f75eb1 100644 --- a/src/main/java/org/alfresco/repo/rendition2/LegacyTransformServiceRegistry.java +++ b/src/main/java/org/alfresco/repo/rendition2/LegacyTransformServiceRegistry.java @@ -65,6 +65,11 @@ public class LegacyTransformServiceRegistry extends AbstractTransformServiceRegi firstTime = true; } + public boolean isEnabled() + { + return enabled; + } + public void setTransformerDebug(TransformerDebug transformerDebug) { this.transformerDebug = transformerDebug; diff --git a/src/main/java/org/alfresco/repo/rendition2/RenditionDefinitionRegistry2Impl.java b/src/main/java/org/alfresco/repo/rendition2/RenditionDefinitionRegistry2Impl.java index 5890440539..c9025a33f0 100644 --- a/src/main/java/org/alfresco/repo/rendition2/RenditionDefinitionRegistry2Impl.java +++ b/src/main/java/org/alfresco/repo/rendition2/RenditionDefinitionRegistry2Impl.java @@ -25,9 +25,19 @@ */ package org.alfresco.repo.rendition2; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import org.alfresco.transform.client.model.config.TransformServiceRegistry; +import org.alfresco.util.ConfigFileFinder; +import org.alfresco.util.ConfigScheduler; import org.alfresco.util.Pair; +import org.alfresco.util.PropertyCheck; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.quartz.CronExpression; +import org.springframework.beans.factory.InitializingBean; +import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -39,17 +49,204 @@ import java.util.Set; * * @author adavis */ -public class RenditionDefinitionRegistry2Impl implements RenditionDefinitionRegistry2 +public class RenditionDefinitionRegistry2Impl implements RenditionDefinitionRegistry2, InitializingBean { - private TransformServiceRegistry transformServiceRegistry; + private static final Log log = LogFactory.getLog(RenditionDefinitionRegistry2Impl.class); - private final Map renditionDefinitions = new HashMap(); - private final Map>> renditionsFor = new HashMap<>(); + static class Data + { + Map renditionDefinitions = new HashMap(); + Map>> renditionsFor = new HashMap<>(); + private int count = 0; + + @Override + public String toString() + { + return "("+count+")"; + } + } + + static class RenditionDef + { + private String renditionName; + private String targetMediaType; + private Set options; + + public void setRenditionName(String renditionName) + { + this.renditionName = renditionName; + } + + public void setTargetMediaType(String targetMediaType) + { + this.targetMediaType = targetMediaType; + } + + public void setOptions(Set options) + { + this.options = options; + } + } + + static class RenditionOpt + { + private String name; + private String value; + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getValue() + { + return value; + } + + public void setValue(String value) + { + this.value = value; + } + } + + private TransformServiceRegistry transformServiceRegistry; + private String renditionConfigDir; + private String timeoutDefault; + private ObjectMapper jsonObjectMapper; + private CronExpression cronExpression; + private CronExpression initialAndOnErrorCronExpression; + + private ConfigScheduler configScheduler = new ConfigScheduler(this) + { + @Override + public boolean readConfig() throws IOException + { + return RenditionDefinitionRegistry2Impl.this.readConfig(); + } + + @Override + public Object createData() + { + return RenditionDefinitionRegistry2Impl.this.createData(); + } + }; + private ConfigFileFinder configFileFinder; public void setTransformServiceRegistry(TransformServiceRegistry transformServiceRegistry) { this.transformServiceRegistry = transformServiceRegistry; - renditionsFor.clear(); + } + + public void setRenditionConfigDir(String renditionConfigDir) + { + this.renditionConfigDir = renditionConfigDir; + } + + public void setTimeoutDefault(String timeoutDefault) + { + this.timeoutDefault = timeoutDefault; + } + + public void setJsonObjectMapper(ObjectMapper jsonObjectMapper) + { + this.jsonObjectMapper = jsonObjectMapper; + } + + public CronExpression getCronExpression() + { + return cronExpression; + } + + public void setCronExpression(CronExpression cronExpression) + { + this.cronExpression = cronExpression; + } + + public CronExpression getInitialAndOnErrorCronExpression() + { + return initialAndOnErrorCronExpression; + } + + public void setInitialAndOnErrorCronExpression(CronExpression initialAndOnErrorCronExpression) + { + this.initialAndOnErrorCronExpression = initialAndOnErrorCronExpression; + } + + @Override + public void afterPropertiesSet() throws Exception + { + PropertyCheck.mandatory(this, "transformServiceRegistry", transformServiceRegistry); + PropertyCheck.mandatory(this, "renditionConfigDir", renditionConfigDir); + PropertyCheck.mandatory(this, "timeoutDefault", timeoutDefault); + PropertyCheck.mandatory(this, "jsonObjectMapper", jsonObjectMapper); + if (cronExpression != null) + { + PropertyCheck.mandatory(this, "initialAndOnErrorCronExpression", initialAndOnErrorCronExpression); + } + configFileFinder = new ConfigFileFinder(jsonObjectMapper) + { + @Override + protected void readJson(JsonNode jsonNode, String readFromMessage, String baseUrl) throws IOException + { + try + { + JsonNode renditions = jsonNode.get("renditions"); + if (renditions != null && renditions.isArray()) + { + for (JsonNode rendition : renditions) + { + RenditionDef def = jsonObjectMapper.convertValue(rendition, RenditionDef.class); + Map map = new HashMap<>(); + if (def.options != null) + { + def.options.forEach(o -> map.put(o.name, o.value)); + } + if (!map.containsKey(RenditionDefinition2.TIMEOUT)) + { + map.put(RenditionDefinition2.TIMEOUT, timeoutDefault); + } + new RenditionDefinition2Impl(def.renditionName, def.targetMediaType, map, + RenditionDefinitionRegistry2Impl.this); + } + } + } + catch (IllegalArgumentException e) + { + log.error("Error reading "+readFromMessage+" "+e.getMessage()); + } + } + }; + configScheduler.run(true, log, cronExpression, initialAndOnErrorCronExpression); + } + + public Data createData() + { + return new Data(); + } + + public synchronized Data getData() + { + return configScheduler.getData(); + } + + public boolean readConfig() throws IOException + { + boolean successReadingConfig = configFileFinder.readFiles("alfresco/renditions", log); + if (renditionConfigDir != null && !renditionConfigDir.isBlank()) + { + successReadingConfig &= configFileFinder.readFiles(renditionConfigDir, log); + } + return successReadingConfig; + } + + public boolean isEnabled() + { + return true; } /** @@ -71,12 +268,14 @@ public class RenditionDefinitionRegistry2Impl implements RenditionDefinitionRegi { throw new IllegalArgumentException("RenditionDefinition "+renditionName+" was already registered."); } - renditionDefinitions.put(renditionName, renditionDefinition); + Data data = getData(); + data.renditionDefinitions.put(renditionName, renditionDefinition); } public void unregister(String renditionName) { - if (renditionDefinitions.remove(renditionName) == null) + Data data = getData(); + if (data.renditionDefinitions.remove(renditionName) == null) { throw new IllegalArgumentException("RenditionDefinition "+renditionName+" was not registered."); } @@ -85,20 +284,21 @@ public class RenditionDefinitionRegistry2Impl implements RenditionDefinitionRegi @Override public Set getRenditionNames() { - return renditionDefinitions.keySet(); + return getData().renditionDefinitions.keySet(); } @Override public Set getRenditionNamesFrom(String sourceMimetype, long size) { Set> renditionNamesWithMaxSize; - synchronized (renditionsFor) + Data data = getData(); + synchronized (data.renditionsFor) { - renditionNamesWithMaxSize = renditionsFor.get(sourceMimetype); + renditionNamesWithMaxSize = data.renditionsFor.get(sourceMimetype); if (renditionNamesWithMaxSize == null) { renditionNamesWithMaxSize = getRenditionNamesWithMaxSize(sourceMimetype); - renditionsFor.put(sourceMimetype, renditionNamesWithMaxSize); + data.renditionsFor.put(sourceMimetype, renditionNamesWithMaxSize); } } @@ -125,7 +325,8 @@ public class RenditionDefinitionRegistry2Impl implements RenditionDefinitionRegi private Set> getRenditionNamesWithMaxSize(String sourceMimetype) { Set> renditions = new HashSet(); - for (Map.Entry entry : renditionDefinitions.entrySet()) + Data data = getData(); + for (Map.Entry entry : data.renditionDefinitions.entrySet()) { RenditionDefinition2 renditionDefinition2 = entry.getValue(); String targetMimetype = renditionDefinition2.getTargetMimetype(); @@ -145,6 +346,7 @@ public class RenditionDefinitionRegistry2Impl implements RenditionDefinitionRegi @Override public RenditionDefinition2 getRenditionDefinition(String renditionName) { - return renditionDefinitions.get(renditionName); + Data data = getData(); + return data.renditionDefinitions.get(renditionName); } } diff --git a/src/main/java/org/alfresco/transform/client/model/config/CombinedConfig.java b/src/main/java/org/alfresco/transform/client/model/config/CombinedConfig.java index 2ce2e0979b..ea80070b63 100644 --- a/src/main/java/org/alfresco/transform/client/model/config/CombinedConfig.java +++ b/src/main/java/org/alfresco/transform/client/model/config/CombinedConfig.java @@ -25,12 +25,12 @@ */ 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.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.util.ConfigFileFinder; import org.apache.commons.logging.Log; import org.apache.http.HttpEntity; import org.apache.http.StatusLine; @@ -40,26 +40,15 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; -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.io.StringReader; -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.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; -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 @@ -84,6 +73,7 @@ public class CombinedConfig private Map allTransformOptions = new HashMap<>(); private List allTransforms = new ArrayList<>(); private ObjectMapper jsonObjectMapper = new ObjectMapper(); + private ConfigFileFinder configFileFinder; static class TransformNodeAndItsOrigin { @@ -116,26 +106,62 @@ public class CombinedConfig public CombinedConfig(Log log) { this.log = log; + + configFileFinder = new ConfigFileFinder(jsonObjectMapper) + { + @Override + protected void readJson(JsonNode jsonNode, String readFromMessage, String baseUrl) throws IOException + { + 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(new TransformNodeAndItsOrigin((ObjectNode)transformer, baseUrl, readFromMessage)); + } + } + } + } + }; } public boolean addRemoteConfig(List urls, String remoteType) { - boolean successReadingRemoteConfig = true; + boolean successReadingConfig = true; for (String url : urls) { if (!addRemoteConfig(url, remoteType)) { - successReadingRemoteConfig = false; + successReadingConfig = false; } } - return successReadingRemoteConfig; + return successReadingConfig; } private boolean addRemoteConfig(String baseUrl, String remoteType) { String url = baseUrl + TRANSFORM_CONFIG; HttpGet httpGet = new HttpGet(url); - boolean successReadingRemoteConfig = true; + boolean successReadingConfig = true; try { try (CloseableHttpClient httpclient = HttpClients.createDefault()) @@ -160,10 +186,10 @@ public class CombinedConfig try (StringReader reader = new StringReader(content)) { int transformCount = allTransforms.size(); - addJsonSource(reader, baseUrl, remoteType+" on "+baseUrl); + configFileFinder.readFile(reader, remoteType+" on "+baseUrl, "json", baseUrl, log); if (transformCount == allTransforms.size()) { - successReadingRemoteConfig = false; + successReadingConfig = false; } } @@ -201,9 +227,9 @@ public class CombinedConfig catch (AlfrescoRuntimeException e) { log.error(e.getMessage()); - successReadingRemoteConfig = false; + successReadingConfig = false; } - return successReadingRemoteConfig; + return successReadingConfig; } // Tests mock the return values @@ -235,117 +261,15 @@ public class CombinedConfig return message; } - public void addLocalConfig(String path) throws IOException + public boolean addLocalConfig(String path) throws IOException { - boolean somethingRead = false; - 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) - { - somethingRead = true; - addJsonSource(new InputStreamReader(getResourceAsStream(name)), null, - name+" from jar "+jarFile.getName()); - } - - jar.close(); - } - else - { - URL url = getClass().getClassLoader().getResource(path); - if (url != null) - { - File root = new File(url.getPath()); - String rootPath = root.getPath(); - if (root.isDirectory()) - { - File[] files = root.listFiles(); - Arrays.sort(files, (file1, file2) -> file1.getName().compareTo(file2.getName())); - for (File file: files) - { - somethingRead = true; - addJsonSource(new FileReader(file), null,"File " + file.getPath()); - } - } - else - { - somethingRead = true; - addJsonSource(new FileReader(root), null, "File " + rootPath); - } - } - } - - if (!somethingRead) - { - log.warn("No config read from "+path); - } + return configFileFinder.readFiles(path, log); } - 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, String baseUrl, String readFrom) throws IOException - { - JsonNode jsonNode = jsonObjectMapper.readValue(reader, new TypeReference() {}); - if (log.isTraceEnabled()) - { - log.trace(readFrom+" config is: "+jsonNode); - } - else - { - log.debug(readFrom+" config read"); - } - - 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(new TransformNodeAndItsOrigin((ObjectNode)transformer, baseUrl, readFrom)); - } - } - } - } - - public void register(TransformServiceRegistryImpl.Data data, TransformServiceRegistryImpl registry) throws IOException + public void register(TransformServiceRegistryImpl registry) throws IOException { List transformers = getTransforms(); - transformers.forEach(t->registry.register(data, t.transform, t.baseUrl, t.readFrom)); + transformers.forEach(t->registry.register(t.transform, t.baseUrl, t.readFrom)); } public List getTransforms() throws IOException 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 6627c468f5..78310f196d 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,21 +27,11 @@ package org.alfresco.transform.client.model.config; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import org.alfresco.util.ConfigScheduler; import org.alfresco.util.PropertyCheck; import org.apache.commons.logging.Log; import org.quartz.CronExpression; -import org.quartz.CronScheduleBuilder; -import org.quartz.CronTrigger; -import org.quartz.Job; -import org.quartz.JobBuilder; -import org.quartz.JobDataMap; -import org.quartz.JobDetail; -import org.quartz.JobExecutionContext; -import org.quartz.JobExecutionException; -import org.quartz.Scheduler; import org.quartz.SchedulerException; -import org.quartz.TriggerBuilder; -import org.quartz.impl.StdSchedulerFactory; import org.springframework.beans.factory.InitializingBean; import java.io.IOException; @@ -68,7 +58,12 @@ public abstract class TransformServiceRegistryImpl implements TransformServiceRe private int transformerCount = 0; private int transformCount = 0; boolean firstTime = true; - boolean successReadingRemoteConfig = true; + + @Override + public String toString() + { + return "("+transformerCount+":"+transformCount+")"; + } } static class SupportedTransform @@ -90,40 +85,31 @@ public abstract class TransformServiceRegistryImpl implements TransformServiceRe } } - public static class TransformServiceRegistryJob implements Job - { - @Override - public void execute(JobExecutionContext context) throws JobExecutionException - { - JobDataMap dataMap = context.getJobDetail().getJobDataMap(); - TransformServiceRegistryImpl registry = (TransformServiceRegistryImpl)dataMap.get("registry"); - registry.readConfigAndReplace(); - } - } - protected boolean enabled = true; - protected Data data; private ObjectMapper jsonObjectMapper; - private Scheduler scheduler; private CronExpression cronExpression; private CronExpression initialAndOnErrorCronExpression; - private boolean normalCronSchedule; + + private ConfigScheduler configScheduler = new ConfigScheduler(this) + { + @Override + public boolean readConfig() throws IOException + { + return TransformServiceRegistryImpl.this.readConfig(); + } + + @Override + public Object createData() + { + return TransformServiceRegistryImpl.this.createData(); + } + }; public void setJsonObjectMapper(ObjectMapper jsonObjectMapper) { this.jsonObjectMapper = jsonObjectMapper; } - public synchronized Scheduler getScheduler() - { - return scheduler; - } - - public synchronized void setScheduler(Scheduler scheduler) - { - this.scheduler = scheduler; - } - public CronExpression getCronExpression() { return cronExpression; @@ -147,126 +133,31 @@ public abstract class TransformServiceRegistryImpl implements TransformServiceRe @Override public void afterPropertiesSet() throws Exception { - if (jsonObjectMapper == null) + PropertyCheck.mandatory(this, "jsonObjectMapper", jsonObjectMapper); + if (cronExpression != null) { - throw new IllegalStateException("jsonObjectMapper has not been set"); + PropertyCheck.mandatory(this, "initialAndOnErrorCronExpression", initialAndOnErrorCronExpression); } - PropertyCheck.mandatory(this, "cronExpression", cronExpression); - PropertyCheck.mandatory(this, "initialAndOnErrorCronExpression", initialAndOnErrorCronExpression); - setData(null); - if (enabled) - { - schedule(); - } - } - - private synchronized void schedule() - { - // Don't do an initial readConfigAndReplace() as the first scheduled read can be done almost instantly and - // there is little point doing two in the space of a few seconds. If however the scheduler is already running - // we do need to run it (this is only from test cases). - if (scheduler == null) - { - StdSchedulerFactory sf = new StdSchedulerFactory(); - String jobName = getClass().getName()+"Job"; - try - { - scheduler = sf.getScheduler(); - - JobDetail job = JobBuilder.newJob() - .withIdentity(jobName) - .ofType(TransformServiceRegistryJob.class) - .build(); - job.getJobDataMap().put("registry", this); - CronExpression cronExpression = normalCronSchedule ? this.cronExpression : initialAndOnErrorCronExpression; - CronTrigger trigger = TriggerBuilder.newTrigger() - .withIdentity(jobName+"Trigger", Scheduler.DEFAULT_GROUP) - .withSchedule(CronScheduleBuilder.cronSchedule(cronExpression)) - .build(); - scheduler.startDelayed(0); - scheduler.scheduleJob(job, trigger); - } - catch (SchedulerException e) - { - getLog().error("Failed to start "+jobName+" "+e.getMessage()); - } - } - else - { - readConfigAndReplace(); - } - } - - protected void readConfigAndReplace() - { - boolean successReadingRemoteConfig = true; Log log = getLog(); - log.debug("Config read started"); - try - { - Data data = readConfig(); - successReadingRemoteConfig = data.successReadingRemoteConfig; - setData(data); - log.debug("Config read finished "+getCounts()); - } - catch (Exception e) - { - successReadingRemoteConfig = false; - log.error("Config read failed. "+e.getMessage(), e); - } - - // Switch schedule sequence if we were on the normal schedule and we now have problems or if - // we are on the initial/error schedule and there were no errors. - if (normalCronSchedule && !successReadingRemoteConfig || - !normalCronSchedule && successReadingRemoteConfig) - { - normalCronSchedule = !normalCronSchedule; - if (scheduler != null) - { - try - { - CronExpression cronExpression = normalCronSchedule ? this.cronExpression : initialAndOnErrorCronExpression; - scheduler.clear(); - scheduler = null; - schedule(); - } - catch (SchedulerException e) - { - getLog().error("Problem stopping scheduler for transformer configuration "+e.getMessage()); - } - } - else - { - System.out.println("Switch schedule "+normalCronSchedule+" WITHOUT new schedule"); - } - } + configScheduler.run(enabled, log, cronExpression, initialAndOnErrorCronExpression); } - protected abstract Data readConfig() throws IOException; - - private synchronized void setData(Data data) - { - this.data = data; - } - - protected synchronized Data getData() - { - if (data == null) - { - data = createData(); - } - return data; - } - - protected Data createData() + public Data createData() { return new Data(); } - protected void setSuccessReadingRemoteConfig(Data data, boolean successReadingRemoteConfig) + public synchronized Data getData() { - data.successReadingRemoteConfig = successReadingRemoteConfig; + return configScheduler.getData(); + } + + public abstract boolean readConfig() throws IOException; + + public boolean isEnabled() + { + return enabled; } public void setEnabled(boolean enabled) @@ -287,14 +178,15 @@ public abstract class TransformServiceRegistryImpl implements TransformServiceRe protected abstract Log getLog(); - public void register(Data data, Reader reader, String readFrom) throws IOException + public void register(Reader reader, String readFrom) throws IOException { List transformers = jsonObjectMapper.readValue(reader, new TypeReference>(){}); - transformers.forEach(t -> register(data, t, null, readFrom)); + transformers.forEach(t -> register(t, null, readFrom)); } - protected void register(Data data, Transformer transformer, String baseUrl, String readFrom) + protected void register(Transformer transformer, String baseUrl, String readFrom) { + Data data = getData(); data.transformerCount++; transformer.getSupportedSourceAndTargetList().forEach( e -> data.transformers.computeIfAbsent(e.getSourceMediaType(), @@ -304,11 +196,6 @@ public abstract class TransformServiceRegistryImpl implements TransformServiceRe transformer.getTransformOptions(), e.getMaxSourceSizeBytes(), e.getPriority()))); } - protected String getCounts() - { - return "("+getData().transformerCount+":"+getData().transformCount+")"; - } - @Override public boolean isSupported(String sourceMimetype, long sourceSizeInBytes, String targetMimetype, Map actualOptions, String renditionName) @@ -366,7 +253,6 @@ public abstract class TransformServiceRegistryImpl implements TransformServiceRe renditionName = null; } - Data data = getData(); List transformListBySize = renditionName == null ? null : data.cachedSupportedTransformList.computeIfAbsent(renditionName, k -> new ConcurrentHashMap<>()).get(sourceMimetype); diff --git a/src/main/java/org/alfresco/util/ConfigFileFinder.java b/src/main/java/org/alfresco/util/ConfigFileFinder.java new file mode 100644 index 0000000000..d6fd3393a3 --- /dev/null +++ b/src/main/java/org/alfresco/util/ConfigFileFinder.java @@ -0,0 +1,164 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2019 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.util; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +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.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +/** + * Used to find configuration files as resources from the jar file or from some external location. The path supplied + * to {@link #readFiles(String, Log)} may be a directory name. Normally used by ConfigScheduler. + * + * @author adavis + */ +public abstract class ConfigFileFinder +{ + private final ObjectMapper jsonObjectMapper; + + public ConfigFileFinder(ObjectMapper jsonObjectMapper) + { + this.jsonObjectMapper = jsonObjectMapper; + } + + public boolean readFiles(String path, Log log) + { + boolean successReadingConfig = true; + try + { + boolean somethingRead = false; + final File jarFile = new File(getClass().getProtectionDomain().getCodeSource().getLocation().getPath()); + if (jarFile.isFile()) + { + Enumeration entries = new JarFile(jarFile).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) + { + somethingRead = true; + successReadingConfig &= readFile(new InputStreamReader(getResourceAsStream(name)),"resource", name, null, log); + } + + new JarFile(jarFile).close(); + } + else + { + URL url = getClass().getClassLoader().getResource(path); + if (url != null) + { + File root = new File(url.getPath()); + String rootPath = root.getPath(); + if (root.isDirectory()) + { + File[] files = root.listFiles(); + Arrays.sort(files, (file1, file2) -> file1.getName().compareTo(file2.getName())); + for (File file: files) + { + somethingRead = true; + successReadingConfig &= readFile(new FileReader(file), "file", file.getPath(), null, log); + } + } + else + { + somethingRead = true; + successReadingConfig = readFile(new FileReader(root), "file", rootPath, null, log); + } + } + } + + if (!somethingRead) + { + log.warn("No config read from "+path); + } + } + catch (IOException e) + { + log.error("Error reading from "+path); + successReadingConfig = false; + } + return successReadingConfig; + } + + private InputStream getResourceAsStream(String resource) + { + final InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource); + return in == null ? getClass().getResourceAsStream(resource) : in; + } + + public boolean readFile(Reader reader, String readFrom, String path, String baseUrl, Log log) + { + // At the moment it is assumed the file is Json, but that does not need to be the case. + // We have the path including extension. + boolean successReadingConfig = true; + try + { + JsonNode jsonNode = jsonObjectMapper.readValue(reader, new TypeReference() {}); + String readFromMessage = readFrom + ' ' + path; + if (log.isTraceEnabled()) + { + log.trace(readFromMessage + " config is: " + jsonNode); + } + else + { + log.debug(readFromMessage + " config read"); + } + readJson(jsonNode, readFromMessage, baseUrl); + } + catch (Exception e) + { + log.error("Error reading "+path); + successReadingConfig = false; + } + return successReadingConfig; + } + + protected abstract void readJson(JsonNode jsonNode, String readFromMessage, String baseUrl) throws IOException; +} diff --git a/src/main/java/org/alfresco/util/ConfigScheduler.java b/src/main/java/org/alfresco/util/ConfigScheduler.java new file mode 100644 index 0000000000..58d54ba976 --- /dev/null +++ b/src/main/java/org/alfresco/util/ConfigScheduler.java @@ -0,0 +1,223 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2019 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.quartz.CronExpression; +import org.quartz.CronScheduleBuilder; +import org.quartz.CronTrigger; +import org.quartz.Job; +import org.quartz.JobBuilder; +import org.quartz.JobDataMap; +import org.quartz.JobDetail; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.quartz.Scheduler; +import org.quartz.TriggerBuilder; +import org.quartz.impl.StdSchedulerFactory; + +import java.io.IOException; +import java.util.Date; + +/** + * Used to schedule reading of config. The config is assumed to change from time to time. + * Initially or on error the reading frequency is high but slower once no problems are reported. + * If the normal cron schedule is not set or is in the past, the config is read only once when + * {@link #run(boolean, Log, CronExpression, CronExpression)} is called. + * + * @author adavis + */ +public abstract class ConfigScheduler +{ + public static class ConfigSchedulerJob implements Job + { + @Override + public void execute(JobExecutionContext context) throws JobExecutionException + { + JobDataMap dataMap = context.getJobDetail().getJobDataMap(); + ConfigScheduler configScheduler = (ConfigScheduler)dataMap.get(CONFIG_SCHEDULER); + boolean successReadingConfig = configScheduler.readConfigAndReplace(); + configScheduler.changeScheduleOnStateChange(successReadingConfig); + } + } + + public static final String CONFIG_SCHEDULER = "configScheduler"; + + private static final Log defaultLog = LogFactory.getLog(ConfigScheduler.class); + private static StdSchedulerFactory schedulerFactory = new StdSchedulerFactory(); + + private final String jobName; + private Log log; + private CronExpression cronExpression; + private CronExpression initialAndOnErrorCronExpression; + + private Scheduler scheduler; + private boolean normalCronSchedule; + + protected Data data; + private ThreadLocal threadData = ThreadLocal.withInitial(() -> data); + + public ConfigScheduler(Object client) + { + jobName = client.getClass().getName()+"Job"; + } + + public abstract boolean readConfig() throws IOException; + + public abstract Data createData(); + + public synchronized Data getData() + { + Data data = threadData.get(); + if (data == null) + { + data = createData(); + setData(data); + } + return data; + } + + private synchronized void setData(Data data) + { + this.data = data; + threadData.set(data); + } + + private synchronized void clearData() + { + this.data = null; // as run() should only be called multiple times in testing, it is okay to discard the + // previous data, as there should be no other Threads trying to read it, unless they are + // left over from previous tests. + threadData.remove(); // we need to pick up the initial value next time (whatever the data value is at that point) + } + + /** + * This method should only be called once in production on startup generally from Spring afterPropertiesSet methods. + * In testing it is allowed to call this method multiple times, but in that case it is recommended to pass in a + * null cronExpression (or a cronExpression such as a date in the past) so the scheduler is not started. If this is + * done, the config is still read, but before the method returns. + */ + public void run(boolean enabled, Log log, CronExpression cronExpression, CronExpression initialAndOnErrorCronExpression) + { + clearPreviousSchedule(); + clearData(); + if (enabled) + { + this.log = log == null ? ConfigScheduler.defaultLog : log; + Date now = new Date(); + if (cronExpression != null && + initialAndOnErrorCronExpression != null && + cronExpression.getNextValidTimeAfter(now) != null && + initialAndOnErrorCronExpression.getNextValidTimeAfter(now) != null) + { + this.cronExpression = cronExpression; + this.initialAndOnErrorCronExpression = initialAndOnErrorCronExpression; + schedule(); + } + else + { + readConfigAndReplace(); + } + } + } + + private synchronized void schedule() + { + try + { + scheduler = schedulerFactory.getScheduler(); + + JobDetail job = JobBuilder.newJob() + .withIdentity(jobName) + .ofType(ConfigSchedulerJob.class) + .build(); + job.getJobDataMap().put(CONFIG_SCHEDULER, this); + CronExpression cronExpression = normalCronSchedule ? this.cronExpression : initialAndOnErrorCronExpression; + CronTrigger trigger = TriggerBuilder.newTrigger() + .withIdentity(jobName+"Trigger", Scheduler.DEFAULT_GROUP) + .withSchedule(CronScheduleBuilder.cronSchedule(cronExpression)) + .build(); + scheduler.startDelayed(0); + scheduler.scheduleJob(job, trigger); + } + catch (Exception e) + { + log.error("Error scheduling "+e.getMessage()); + } + } + + private void clearPreviousSchedule() + { + if (scheduler != null) + { + try + { + scheduler.clear(); + } + catch (Exception e) + { + log.error("Error clearing previous schedule " + e.getMessage()); + } + } + } + + private boolean readConfigAndReplace() + { + boolean successReadingConfig; + log.debug("Config read started"); + Data data = getData(); + try + { + Data newData = createData(); + threadData.set(newData); + successReadingConfig = readConfig(); + data = newData; + log.debug("Config read finished "+data+ + (successReadingConfig ? "" : ". Config replaced but there were problems")); + } + catch (Exception e) + { + successReadingConfig = false; + log.error("Config read failed. "+e.getMessage(), e); + } + setData(data); + return successReadingConfig; + } + + private void changeScheduleOnStateChange(boolean successReadingConfig) + { + // Switch schedule sequence if we were on the normal schedule and we now have problems or if + // we are on the initial/error schedule and there were no errors. + if ( normalCronSchedule && !successReadingConfig || + !normalCronSchedule && successReadingConfig) + { + normalCronSchedule = !normalCronSchedule; + clearPreviousSchedule(); + schedule(); + } + } +} diff --git a/src/main/java/org/alfresco/util/ResourceFinder.java b/src/main/java/org/alfresco/util/ResourceFinder.java index 27f6cf2264..fc76816274 100644 --- a/src/main/java/org/alfresco/util/ResourceFinder.java +++ b/src/main/java/org/alfresco/util/ResourceFinder.java @@ -1,40 +1,40 @@ -/* - * #%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.util; -import java.io.IOException; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; - import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.web.context.support.ServletContextResourcePatternResolver; +import java.io.IOException; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + /** * Can be used in Spring configuration to search for all resources matching an array of patterns. * diff --git a/src/main/resources/alfresco/rendition-services2-context.xml b/src/main/resources/alfresco/rendition-services2-context.xml index b5430e9b9c..e291d6f0bf 100644 --- a/src/main/resources/alfresco/rendition-services2-context.xml +++ b/src/main/resources/alfresco/rendition-services2-context.xml @@ -147,9 +147,16 @@ + + + + + + + @@ -157,108 +164,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/src/main/resources/alfresco/renditions/0100-baseRenditions.json b/src/main/resources/alfresco/renditions/0100-baseRenditions.json new file mode 100644 index 0000000000..d828a0e8bd --- /dev/null +++ b/src/main/resources/alfresco/renditions/0100-baseRenditions.json @@ -0,0 +1,69 @@ +{ + "renditions": [ + { + "renditionName": "medium", + "targetMediaType": "image/jpeg", + "options": [ + {"name": "resizeWidth", "value": 100}, + {"name": "resizeHeight", "value": 100}, + {"name": "maintainAspectRatio", "value": true}, + {"name": "thumbnail", "value": true} + ] + }, + { + "renditionName": "doclib", + "targetMediaType": "image/png", + "options": [ + {"name": "resizeWidth", "value": 100}, + {"name": "resizeHeight", "value": 100}, + {"name": "allowEnlargement", "value": false}, + {"name": "maintainAspectRatio", "value": true}, + {"name": "thumbnail", "value": true} + ] + }, + { + "renditionName": "imgpreview", + "targetMediaType": "image/jpeg", + "options": [ + {"name": "resizeWidth", "value": 960}, + {"name": "resizeHeight", "value": 960}, + {"name": "allowEnlargement", "value": false}, + {"name": "maintainAspectRatio", "value": true}, + {"name": "thumbnail", "value": true} + ] + }, + { + "renditionName": "avatar", + "targetMediaType": "image/png", + "options": [ + {"name": "resizeWidth", "value": 64}, + {"name": "resizeHeight", "value": 64}, + {"name": "allowEnlargement", "value": false}, + {"name": "maintainAspectRatio", "value": true}, + {"name": "thumbnail", "value": true} + ] + }, + { + "renditionName": "avatar32", + "targetMediaType": "image/png", + "options": [ + {"name": "resizeWidth", "value": 32}, + {"name": "resizeHeight", "value": 32}, + {"name": "allowEnlargement", "value": false}, + {"name": "maintainAspectRatio", "value": true}, + {"name": "thumbnail", "value": true} + ] + }, + { + "renditionName": "webpreview", + "targetMediaType": "application/x-shockwave-flash", + "options": [ + {"name": "flashVersion", "value": 9} + ] + }, + { + "renditionName": "pdf", + "targetMediaType": "application/pdf" + } + ] +} diff --git a/src/main/resources/alfresco/repository.properties b/src/main/resources/alfresco/repository.properties index f551df254a..9789e75dfe 100644 --- a/src/main/resources/alfresco/repository.properties +++ b/src/main/resources/alfresco/repository.properties @@ -1079,6 +1079,16 @@ people.search.honor.hint.useCQ=true # Delays cron jobs after bootstrap to allow server to fully come up before jobs start system.cronJob.startDelayMilliseconds=60000 +# Schedule for reading rendition config definitions dynamically. Initially checks every 10 seconds and then switches to +# every 10 minutes after the configuration is read successfully. If there is a error later reading the config, the +# checks return to every 10 seconds. +rendition.config.cronExpression=0 0/10 * * * ? +rendition.config.initialAndOnError.cronExpression=0/10 * * * * ? + +# Optional property to specify an external file or directory that will be read for rendition definitions from YAML +# files (possibly added to a volume via k8 ConfigMaps). +rendition.config.dir= + # Optional property to specify an external file or directory that will be read for transformer json config. local.transform.pipeline.config.dir= diff --git a/src/main/resources/alfresco/transformers/0150-officeToImageViaPdf.json b/src/main/resources/alfresco/transforms/0150-officeToImageViaPdf.json similarity index 100% rename from src/main/resources/alfresco/transformers/0150-officeToImageViaPdf.json rename to src/main/resources/alfresco/transforms/0150-officeToImageViaPdf.json diff --git a/src/test/java/org/alfresco/repo/rendition2/AbstractRenditionIntegrationTest.java b/src/test/java/org/alfresco/repo/rendition2/AbstractRenditionIntegrationTest.java index 2fe276f72a..ec5c443599 100644 --- a/src/test/java/org/alfresco/repo/rendition2/AbstractRenditionIntegrationTest.java +++ b/src/test/java/org/alfresco/repo/rendition2/AbstractRenditionIntegrationTest.java @@ -53,6 +53,7 @@ import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; +import org.quartz.CronExpression; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.ResourceUtils; @@ -119,6 +120,9 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest protected static final String ADMIN = "admin"; protected static final String DOC_LIB = "doclib"; + private CronExpression origLocalTransCron; + private CronExpression origRenditionCron; + @BeforeClass public static void before() { @@ -192,16 +196,25 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest legacyTransformServiceRegistry.setEnabled(Boolean.parseBoolean(System.getProperty("legacy.transform.service.enabled"))); legacyTransformServiceRegistry.afterPropertiesSet(); + origLocalTransCron = localTransformServiceRegistry.getCronExpression(); + localTransformServiceRegistry.setCronExpression(null); localTransformServiceRegistry.setEnabled(Boolean.parseBoolean(System.getProperty("local.transform.service.enabled"))); localTransformServiceRegistry.afterPropertiesSet(); + origRenditionCron = renditionDefinitionRegistry2.getCronExpression(); + renditionDefinitionRegistry2.setCronExpression(null); renditionDefinitionRegistry2.setTransformServiceRegistry(transformServiceRegistry); + renditionDefinitionRegistry2.afterPropertiesSet(); + thumbnailRegistry.setTransformServiceRegistry(transformServiceRegistry); } @After public void cleanUp() { + localTransformServiceRegistry.setCronExpression(origLocalTransCron); + renditionDefinitionRegistry2.setCronExpression(origRenditionCron); + AuthenticationUtil.clearCurrentSecurityContext(); } diff --git a/src/test/java/org/alfresco/repo/rendition2/AbstractRenditionTest.java b/src/test/java/org/alfresco/repo/rendition2/AbstractRenditionTest.java index 2c1073ec03..3440824902 100644 --- a/src/test/java/org/alfresco/repo/rendition2/AbstractRenditionTest.java +++ b/src/test/java/org/alfresco/repo/rendition2/AbstractRenditionTest.java @@ -130,7 +130,7 @@ public abstract class AbstractRenditionTest extends AbstractRenditionIntegration List thumbnailDefinitions = thumbnailRegistry.getThumbnailDefinitions(sourceMimetype, -1); Set thumbnailNames = getThumbnailNames(thumbnailDefinitions); assertEquals("There should be the same renditions ("+renditionNames+") as deprecated thumbnails ("+thumbnailNames+")", - renditionNames, thumbnailNames); + thumbnailNames, renditionNames); renditionCount += renditionNames.size(); for (String renditionName : renditionNames) diff --git a/src/test/java/org/alfresco/repo/rendition2/LegacyTransformServiceRegistryIntegrationTest.java b/src/test/java/org/alfresco/repo/rendition2/LegacyTransformServiceRegistryIntegrationTest.java index 5bc1eceacd..fff4914190 100644 --- a/src/test/java/org/alfresco/repo/rendition2/LegacyTransformServiceRegistryIntegrationTest.java +++ b/src/test/java/org/alfresco/repo/rendition2/LegacyTransformServiceRegistryIntegrationTest.java @@ -59,8 +59,16 @@ public class LegacyTransformServiceRegistryIntegrationTest extends LocalTransfor AbstractRenditionIntegrationTest.after(); } + @Override protected void setEnabled(boolean enabled) { legacyTransformServiceRegistry.setEnabled(enabled); + legacyTransformServiceRegistry.afterPropertiesSet(); + } + + @Override + protected boolean isEnabled() + { + return legacyTransformServiceRegistry.isEnabled(); } } diff --git a/src/test/java/org/alfresco/repo/rendition2/LocalTransformServiceRegistryIntegrationTest.java b/src/test/java/org/alfresco/repo/rendition2/LocalTransformServiceRegistryIntegrationTest.java index fe0a2d39f4..4683c88907 100644 --- a/src/test/java/org/alfresco/repo/rendition2/LocalTransformServiceRegistryIntegrationTest.java +++ b/src/test/java/org/alfresco/repo/rendition2/LocalTransformServiceRegistryIntegrationTest.java @@ -27,11 +27,14 @@ package org.alfresco.repo.rendition2; import org.alfresco.repo.content.transform.LocalTransformServiceRegistry; import org.alfresco.transform.client.model.config.TransformServiceRegistry; +import org.alfresco.transform.client.model.config.TransformServiceRegistryImpl; +import org.junit.After; import org.junit.AfterClass; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import org.quartz.CronExpression; import org.springframework.beans.factory.annotation.Autowired; import java.util.HashMap; @@ -45,6 +48,8 @@ import static org.alfresco.repo.content.MimetypeMap.MIMETYPE_PDF; */ public class LocalTransformServiceRegistryIntegrationTest extends AbstractRenditionIntegrationTest { + private static final String RENDITION_NAME = "pdf"; + @Autowired private LocalTransformServiceRegistry localTransformServiceRegistry; @@ -65,6 +70,7 @@ public class LocalTransformServiceRegistryIntegrationTest extends AbstractRendit AbstractRenditionIntegrationTest.after(); } + @Override @Before public void setUp() throws Exception { @@ -75,11 +81,16 @@ public class LocalTransformServiceRegistryIntegrationTest extends AbstractRendit options = definition2.getTransformOptions(); } - protected void setEnabled(boolean enabled) + protected void setEnabled(boolean enabled) throws Exception { localTransformServiceRegistry.setEnabled(enabled); + localTransformServiceRegistry.afterPropertiesSet(); + } + + protected boolean isEnabled() + { + return localTransformServiceRegistry.isEnabled(); } - private static final String RENDITION_NAME = "pdf"; @Test public void testIsSupported() @@ -121,18 +132,22 @@ public class LocalTransformServiceRegistryIntegrationTest extends AbstractRendit } @Test - public void testEnabledDisabled() + public void testEnabledDisabled() throws Exception { - Assert.assertTrue(transformServiceRegistry.isSupported(MIMETYPE_OPENXML_WORDPROCESSING, 1234, MIMETYPE_PDF, options, RENDITION_NAME)); + boolean origEnabled = isEnabled(); // should be true try { + Assert.assertTrue(transformServiceRegistry.isSupported(MIMETYPE_OPENXML_WORDPROCESSING, 1234, MIMETYPE_PDF, options, RENDITION_NAME)); + setEnabled(false); Assert.assertFalse(transformServiceRegistry.isSupported(MIMETYPE_OPENXML_WORDPROCESSING, 1234, MIMETYPE_PDF, options, RENDITION_NAME)); + + setEnabled(true); + Assert.assertTrue(transformServiceRegistry.isSupported(MIMETYPE_OPENXML_WORDPROCESSING, 1234, MIMETYPE_PDF, options, RENDITION_NAME)); } finally { - setEnabled(true); + setEnabled(origEnabled); } - Assert.assertTrue(transformServiceRegistry.isSupported(MIMETYPE_OPENXML_WORDPROCESSING, 1234, MIMETYPE_PDF, options, RENDITION_NAME)); } } diff --git a/src/test/java/org/alfresco/repo/rendition2/RenditionService2Test.java b/src/test/java/org/alfresco/repo/rendition2/RenditionService2Test.java index cd67fae84f..60334f9080 100644 --- a/src/test/java/org/alfresco/repo/rendition2/RenditionService2Test.java +++ b/src/test/java/org/alfresco/repo/rendition2/RenditionService2Test.java @@ -25,6 +25,7 @@ */ package org.alfresco.repo.rendition2; +import com.fasterxml.jackson.databind.ObjectMapper; import org.alfresco.model.ContentModel; import org.alfresco.repo.policy.BehaviourFilter; import org.alfresco.repo.policy.PolicyComponent; @@ -38,16 +39,22 @@ import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.rule.RuleService; import org.alfresco.service.namespace.QName; import org.alfresco.service.transaction.TransactionService; +import org.alfresco.transform.client.model.config.TransformServiceRegistryImpl; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; +import org.quartz.CronExpression; +import java.io.IOException; import java.util.Date; import java.util.HashMap; import java.util.Map; +import java.util.Set; +import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; @@ -68,6 +75,8 @@ import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class RenditionService2Test { + private static final ObjectMapper JSON_OBJECT_MAPPER = new ObjectMapper();; + private RenditionService2Impl renditionService2; private RenditionDefinitionRegistry2Impl renditionDefinitionRegistry2; @@ -80,9 +89,10 @@ public class RenditionService2Test @Mock private PolicyComponent policyComponent; @Mock private BehaviourFilter behaviourFilter; @Mock private RuleService ruleService; + @Mock private TransformServiceRegistryImpl transformServiceRegistry; private NodeRef nodeRef = new NodeRef("workspace://spacesStore/test-id"); - private static final String IMGPREVIEW = "imgpreview"; + private static final String TEST_RENDITION = "testRendition"; private static final String JPEG = "image/jpeg"; private String contentUrl = "test-content-url"; @@ -91,11 +101,11 @@ public class RenditionService2Test { renditionService2 = new RenditionService2Impl(); renditionDefinitionRegistry2 = new RenditionDefinitionRegistry2Impl(); - - Map options = new HashMap<>(); - options.put("width", "960"); - options.put("height", "1024"); - new RenditionDefinition2Impl(IMGPREVIEW, JPEG, options, renditionDefinitionRegistry2); + renditionDefinitionRegistry2.setTransformServiceRegistry(transformServiceRegistry); + renditionDefinitionRegistry2.setRenditionConfigDir(""); + renditionDefinitionRegistry2.setTimeoutDefault("120000"); + renditionDefinitionRegistry2.setJsonObjectMapper(JSON_OBJECT_MAPPER); + renditionDefinitionRegistry2.setCronExpression(null); // just read it once when(nodeService.exists(nodeRef)).thenReturn(true); when(nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT)).thenReturn(contentData); @@ -115,7 +125,14 @@ public class RenditionService2Test renditionService2.setEnabled(true); renditionService2.setThumbnailsEnabled(true); renditionService2.setRenditionRequestSheduler(new RenditionRequestSchedulerMock()); + + renditionDefinitionRegistry2.afterPropertiesSet(); renditionService2.afterPropertiesSet(); + + Map options = new HashMap<>(); + options.put("width", "960"); + options.put("height", "1024"); + new RenditionDefinition2Impl(TEST_RENDITION, JPEG, options, renditionDefinitionRegistry2); } private class RenditionRequestSchedulerMock extends PostTxnCallbackScheduler @@ -138,20 +155,20 @@ public class RenditionService2Test public void disabled() { renditionService2.setEnabled(false); - renditionService2.render(nodeRef, IMGPREVIEW); + renditionService2.render(nodeRef, TEST_RENDITION); } @Test(expected = RenditionService2Exception.class) public void thumbnailsDisabled() { renditionService2.setThumbnailsEnabled(false); - renditionService2.render(nodeRef, IMGPREVIEW); + renditionService2.render(nodeRef, TEST_RENDITION); } @Test public void useLocalTransform() { - renditionService2.render(nodeRef, IMGPREVIEW); + renditionService2.render(nodeRef, TEST_RENDITION); verify(transformClient, times(1)).transform(any(), any(), anyString(), anyInt()); } @@ -159,14 +176,14 @@ public class RenditionService2Test public void noTransform() { doThrow(UnsupportedOperationException.class).when(transformClient).checkSupported(any(), any(), any(), anyLong(), any()); - renditionService2.render(nodeRef, IMGPREVIEW); + renditionService2.render(nodeRef, TEST_RENDITION); } @Test(expected = RenditionService2PreventedException.class) public void checkSourceNodeForPreventionClass() { when(renditionPreventionRegistry.isContentClassRegistered((QName)any())).thenReturn(true); - renditionService2.render(nodeRef, IMGPREVIEW); + renditionService2.render(nodeRef, TEST_RENDITION); } @Test(expected = IllegalArgumentException.class) @@ -174,4 +191,16 @@ public class RenditionService2Test { renditionService2.render(nodeRef, "doesNotExist"); } + + @Test + public void definitionExists() throws IOException + { + renditionDefinitionRegistry2.readConfig(); + + Set renditionNames = renditionDefinitionRegistry2.getRenditionNames(); + for (String name: new String[] {"medium", "doclib", "imgpreview", "avatar", "avatar32", "webpreview", "pdf"}) + { + assertTrue("Expected rendition "+name, renditionNames.contains(name)); + } + } } diff --git a/src/test/java/org/alfresco/transform/client/model/config/LocalTransformServiceRegistryConfigTest.java b/src/test/java/org/alfresco/transform/client/model/config/LocalTransformServiceRegistryConfigTest.java index 443216e3fe..ad0d5396ae 100644 --- a/src/test/java/org/alfresco/transform/client/model/config/LocalTransformServiceRegistryConfigTest.java +++ b/src/test/java/org/alfresco/transform/client/model/config/LocalTransformServiceRegistryConfigTest.java @@ -61,18 +61,18 @@ public class LocalTransformServiceRegistryConfigTest extends TransformServiceReg { private class TestLocalTransformServiceRegistry extends LocalTransformServiceRegistry { - private boolean lastReadSucceed = false; - private boolean mockSuccessReadingRemoteConfig = true; + private boolean mockSuccessReadingConfig = true; + LocalData dummyData = new LocalData(); - public synchronized boolean getMockSuccessReadingRemoteConfig() + public synchronized boolean getMockSuccessReadingConfig() { - return mockSuccessReadingRemoteConfig; + return mockSuccessReadingConfig; } - public synchronized void setMockSuccessReadingRemoteConfig(boolean mockSuccessReadingRemoteConfig) + public synchronized void setMockSuccessReadingConfig(boolean mockSuccessReadingConfig) { - System.out.println("\n"+getMs()+": set next mock read to "+(mockSuccessReadingRemoteConfig ? "success" : "failure")); - this.mockSuccessReadingRemoteConfig = mockSuccessReadingRemoteConfig; + System.out.println("\n"+getMs()+": set next mock read to "+(mockSuccessReadingConfig ? "success" : "failure")); + this.mockSuccessReadingConfig = mockSuccessReadingConfig; } @Override @@ -84,31 +84,36 @@ public class LocalTransformServiceRegistryConfigTest extends TransformServiceReg } @Override - protected TransformServiceRegistryImpl.Data readConfig() throws IOException + public boolean readConfig() throws IOException { readConfigCount++; - data = createData(); - boolean mockSuccessReadingRemoteConfig = getMockSuccessReadingRemoteConfig(); - lastReadSucceed = mockSuccessReadingRemoteConfig; - setSuccessReadingRemoteConfig(data, mockSuccessReadingRemoteConfig); + dummyData = new LocalData(); + boolean mockSuccessReadingRemoteConfig = getMockSuccessReadingConfig(); System.out.println(getMs() + "readConfig() success="+mockSuccessReadingRemoteConfig+" reads="+readConfigCount); - return data; + return mockSuccessReadingRemoteConfig; } - public Data assertDataChanged(Data data, String msg) + @Override + public synchronized LocalData getData() + { + return dummyData; + } + + public Data assertDataChanged(Data prevData, String msg) { // If the data changes, there has been a read System.out.println(getMs()+msg); - assertNotEquals("The configuration data should have changed: "+msg, this.data, data); - return this.data; + Data data = getData(); + assertNotEquals("The configuration data should have changed: "+msg, data, prevData); + return data; } public Data assertDataUnchanged(Data data, String msg) { // If the data changes, there has been a read System.out.println(getMs()+msg); - assertEquals("The configuration data should be the same: "+msg, this.data, data); - return this.data; + assertEquals("The configuration data should be the same: "+msg, getData(), data); + return getData(); } } @@ -134,7 +139,6 @@ public class LocalTransformServiceRegistryConfigTest extends TransformServiceReg private Map> libreofficeSupportedTransformation; private Map> officeToImageViaPdfSupportedTransformation; - private TransformServiceRegistryImpl.Data data; private int readConfigCount; private long startMs; @@ -155,8 +159,8 @@ public class LocalTransformServiceRegistryConfigTest extends TransformServiceReg registry.setTransformerDebug(transformerDebug); registry.setMimetypeService(mimetypeMap); registry.setPipelineConfigDir(""); - registry.setCronExpression(new CronExpression("* * * * * ? 2099")); // not for a long time. - registry.setInitialAndOnErrorCronExpression(new CronExpression("* * * * * ? 2099")); // not for a long time. + registry.setCronExpression(null); // just read it once + registry.afterPropertiesSet(); return registry; } @@ -416,22 +420,12 @@ public class LocalTransformServiceRegistryConfigTest extends TransformServiceReg { CronExpression origCronExpression = registry.getCronExpression(); CronExpression origInitialAndOnErrorCronExpression = registry.getInitialAndOnErrorCronExpression(); - String origPipelineConfigDir = registry.getPipelineConfigDir(); - Scheduler origScheduler = registry.getScheduler(); - - if (origScheduler != null) - { - origScheduler.clear(); - } try { - TransformServiceRegistryImpl.Data prevData; - data = null; readConfigCount = 0; - registry.setScheduler(null); registry.setInitialAndOnErrorCronExpression(new CronExpression(("0/2 * * ? * * *"))); // every 2 seconds rather than 10 seconds registry.setCronExpression(new CronExpression(("0/4 * * ? * * *"))); // every 4 seconds rather than 10 mins @@ -439,8 +433,9 @@ public class LocalTransformServiceRegistryConfigTest extends TransformServiceReg // It avoids having to work out schedule offsets and extra quick runs that can otherwise take place. Thread.sleep(4000-System.currentTimeMillis()%4000); startMs = System.currentTimeMillis(); - registry.setMockSuccessReadingRemoteConfig(false); + registry.setMockSuccessReadingConfig(false); registry.afterPropertiesSet(); + TransformServiceRegistryImpl.Data data = registry.getData(); Thread.sleep(1000); // 1 seconds data = registry.assertDataChanged(data, "There should have been a read after a few milliseconds that fails"); @@ -456,7 +451,7 @@ public class LocalTransformServiceRegistryConfigTest extends TransformServiceReg // Should switch to normal 4s schedule after the next read, so the read at 12 seconds will be on that schedule. // It is always possible that another quick one gets scheduled almost straight away after the next read. - registry.setMockSuccessReadingRemoteConfig(true); + registry.setMockSuccessReadingConfig(true); Thread.sleep(2000); // 9 seconds data = registry.assertDataChanged(data, "There should have been a read after 8 seconds that succeeds"); @@ -467,7 +462,7 @@ public class LocalTransformServiceRegistryConfigTest extends TransformServiceReg data = registry.assertDataChanged(data, "There should have been a read after 12 seconds that succeeds"); // Should switch back to initial/error schedule after failure - registry.setMockSuccessReadingRemoteConfig(false); + registry.setMockSuccessReadingConfig(false); Thread.sleep(4000); // 17 seconds data = registry.assertDataChanged(data, "There should have been a read after 16 seconds that fails"); @@ -476,15 +471,10 @@ public class LocalTransformServiceRegistryConfigTest extends TransformServiceReg } finally { - registry.setMockSuccessReadingRemoteConfig(true); - - // Reset scheduler properties just in case another tests needs them in future. - // We don't start the scheduler with registry.afterPropertiesSet() as this is - // really just mocked up version of the registry. + registry.setMockSuccessReadingConfig(true); registry.setCronExpression(origCronExpression); registry.setInitialAndOnErrorCronExpression(origInitialAndOnErrorCronExpression); registry.setPipelineConfigDir(origPipelineConfigDir); - registry.setScheduler(null); } } } diff --git a/src/test/java/org/alfresco/transform/client/model/config/TransformServiceRegistryConfigTest.java b/src/test/java/org/alfresco/transform/client/model/config/TransformServiceRegistryConfigTest.java index a1d4cb25eb..6abfecd002 100644 --- a/src/test/java/org/alfresco/transform/client/model/config/TransformServiceRegistryConfigTest.java +++ b/src/test/java/org/alfresco/transform/client/model/config/TransformServiceRegistryConfigTest.java @@ -33,6 +33,7 @@ import org.apache.log4j.LogManager; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.quartz.CronExpression; import java.io.BufferedReader; import java.io.File; @@ -93,9 +94,9 @@ public class TransformServiceRegistryConfigTest TransformServiceRegistryImpl registry = new TransformServiceRegistryImpl() { @Override - protected Data readConfig() throws IOException + public boolean readConfig() throws IOException { - return createData(); // Empty config + return true; } @Override @@ -105,6 +106,8 @@ public class TransformServiceRegistryConfigTest } }; registry.setJsonObjectMapper(JSON_OBJECT_MAPPER); + registry.setCronExpression(null); // just read once + registry.afterPropertiesSet(); return registry; } @@ -182,7 +185,7 @@ public class TransformServiceRegistryConfigTest new SupportedSourceAndTarget(XLS, TXT, 1024000))); registry = buildTransformServiceRegistryImpl(); - registry.register(registry.getData(), transformer, getBaseUrl(transformer), getClass().getName()); + registry.register(transformer, getBaseUrl(transformer), getClass().getName()); assertTrue(registry.isSupported(XLS, 1024, TXT, Collections.emptyMap(), null)); assertTrue(registry.isSupported(XLS, 1024000, TXT, null, null)); @@ -223,7 +226,7 @@ public class TransformServiceRegistryConfigTest registry = buildTransformServiceRegistryImpl(); for (Transformer transformer : transformers) { - registry.register(registry.getData(), transformer, getBaseUrl(transformer), getClass().getName()); + registry.register(transformer, getBaseUrl(transformer), getClass().getName()); } } @@ -257,7 +260,7 @@ public class TransformServiceRegistryConfigTest { CombinedConfig combinedConfig = new CombinedConfig(log); combinedConfig.addLocalConfig(path); - combinedConfig.register(registry.getData(), registry); + combinedConfig.register(registry); } @Test @@ -364,7 +367,7 @@ public class TransformServiceRegistryConfigTest try (Reader reader = new BufferedReader(new FileReader(tempFile))) { - registry.register(registry.getData(), reader, getClass().getName()); + registry.register(reader, getClass().getName()); // Check the count of transforms supported assertEquals("The number of UNIQUE source to target mimetypes transforms has changed. Config change?", 42, countSupportedTransforms(true)); @@ -671,7 +674,7 @@ public class TransformServiceRegistryConfigTest new SupportedSourceAndTarget(DOC, GIF, 102400), new SupportedSourceAndTarget(MSG, GIF, -1))); - registry.register(registry.getData(), transformer, getBaseUrl(transformer), getClass().getName()); + registry.register(transformer, getBaseUrl(transformer), getClass().getName()); assertSupported(DOC, 1024, GIF, null, "doclib", ""); assertSupported(MSG, 1024, GIF, null, "doclib", "");