ALF-14306: Merged DEV to HEAD (4.2)

Add priorities to transformers
   - Addition of a separate Transformers subsystem (holds configuration to allow JMX to be used to reset it on the fly)
   - Moved supported, unsupported and explicit transformation configuration from spring into transformers.properties in the subsystem
   - Moved all transformer limits configuration from spring and into transformers.properties with the exception of some added for thumbnails
     and the rendering service - not done as this code is known to be changing.
   - Added priority, errorTimes and thresholdCount to enable selection of transformers by priority.
   - Removed the concept of 'Explicit' transformation and replaced with priority and unsupported.
   - Added logging to aid in the creation of global properties and removal of spring configuration.
   - Implementation of spring pluggable transformation selector that uses priority.
   - Merging current state of code changes to allow other work in this area to take place.



git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@46719 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Alan Davis
2013-02-16 07:40:55 +00:00
parent 4c393ef70f
commit f5bd27c36c
62 changed files with 3381 additions and 1234 deletions

View File

@@ -30,6 +30,7 @@ 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.TransformationOptions;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.extensions.surf.util.ParameterCheck;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -47,7 +48,7 @@ import org.apache.commons.logging.LogFactory;
* @author Derek Hulley
*/
@Deprecated
public abstract class AbstractContentTransformer implements ContentTransformer
public abstract class AbstractContentTransformer implements ContentTransformer, BeanNameAware
{
private static final Log logger = LogFactory.getLog(AbstractContentTransformer.class);
@@ -58,6 +59,9 @@ public abstract class AbstractContentTransformer implements ContentTransformer
private double averageTime = 0.0;
private long count = 0L;
/** The bean name. */
private String beanName;
/**
* All transformers start with an average transformation time of 0.0ms.
*/
@@ -408,6 +412,11 @@ public abstract class AbstractContentTransformer implements ContentTransformer
return (long) averageTime;
}
public long getTransformationTime(String sourceMimetype, String targetMimetype)
{
return (long) averageTime;
}
/**
* Records and updates the average transformation time for this transformer.
* <p>
@@ -433,4 +442,29 @@ public abstract class AbstractContentTransformer implements ContentTransformer
double diffTime = ((double) transformationTime) - averageTime;
averageTime += diffTime / (double) count;
}
/**
* Sets the Spring bean name.
*/
@Override
public void setBeanName(String beanName)
{
this.beanName = beanName;
}
/**
* Returns the Spring bean name.
*/
public String getBeanName()
{
return beanName;
}
/**
* Returns transformer name. Uses the Spring bean name, but if null uses the class name.
*/
public String getName()
{
return (beanName == null) ? getClass().getSimpleName() : beanName;
}
}

View File

@@ -19,7 +19,6 @@
package org.alfresco.repo.content.transform;
import java.util.Map;
import java.util.Properties;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.service.cmr.repository.ContentIOException;
@@ -46,13 +45,20 @@ public abstract class AbstractContentTransformer2 extends AbstractContentTransfo
private static final Log logger = LogFactory.getLog(AbstractContentTransformer2.class);
private ContentTransformerRegistry registry;
private Properties properties;
private double averageTime;
private long count = 0L;
private ThreadLocal<Integer> depth = new ThreadLocal<Integer>()
{
@Override
protected Integer initialValue()
{
return 0;
}
};
/**
* All transformers start with an average transformation time of 0.0 ms,
* unless there is an Alfresco global property {@code <beanName>.initialTime}.
* unless there is an Alfresco global property {@code <beanName>.time}.
* May also be set for given combinations of source and target mimetypes.
*/
protected AbstractContentTransformer2()
{
@@ -68,70 +74,12 @@ public abstract class AbstractContentTransformer2 extends AbstractContentTransfo
this.registry = registry;
}
/**
* The Alfresco global properties.
*/
public void setProperties(Properties properties)
{
this.properties = properties;
}
/**
* Sets the averageTime and count (if global properties were used to set the averageTime).
* Both default to 0. The property names use the transformer bean name with a ".time"
* or ".count" suffix. Spring bean configuration is not being used as we don't wish to
* break existing transformers that know nothing about these properties.
*/
private void setAverageTimeFromAlfrescoGlobalProperties()
{
String beanName = getBeanName();
averageTime = Long.valueOf(getPositiveLongProperty(beanName+".time", 0L));
if (averageTime > 0.0)
{
// This normally is a large number so that it does not change much if used.
count = Long.valueOf(getPositiveLongProperty(beanName+".count", 10000));
}
}
/**
* Returns a positive long value from an optional Alfresco global property.
* Invalid values are ignored but a log message is issued.
* @param name of the property
* @param defaultValue if the property does not exist or is negative
* @return the value
*/
private long getPositiveLongProperty(String name, long defaultValue)
{
long value = defaultValue;
if (properties != null)
{
String property = properties.getProperty(name);
if (property != null)
{
try
{
value = Long.valueOf(property);
if (value < 0)
{
value = defaultValue;
throw new NumberFormatException();
}
}
catch (NumberFormatException e)
{
logger.warn("Alfresco global property "+name+" is must be a positive Java long value. Using "+defaultValue);
}
}
}
return value;
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append(this.getClass().getSimpleName())
.append("[ average=").append((long)averageTime).append("ms")
.append("[ average=").append(transformerConfig.getStatistics(this, null, null).getAverageTime()).append("ms")
.append("]");
return sb.toString();
}
@@ -140,10 +88,12 @@ public abstract class AbstractContentTransformer2 extends AbstractContentTransfo
* Registers this instance with the {@link #setRegistry(ContentTransformerRegistry) registry}
* if it is present.
*
* THIS IS A CUSTOME SPRING INIT METHOD
* THIS IS A CUSTOM SPRING INIT METHOD
*/
public void register()
{
super.register();
if (registry == null)
{
logger.warn("Property 'registry' has not been set. Ignoring auto-registration: \n" +
@@ -151,8 +101,6 @@ public abstract class AbstractContentTransformer2 extends AbstractContentTransfo
return;
}
setAverageTimeFromAlfrescoGlobalProperties();
// register this instance for the fallback case
registry.addTransformer(this);
}
@@ -211,115 +159,127 @@ public abstract class AbstractContentTransformer2 extends AbstractContentTransfo
public final void transform(ContentReader reader, ContentWriter writer, TransformationOptions options)
throws ContentIOException
{
// begin timing
long before = System.currentTimeMillis();
// check options map
if (options == null)
{
options = new TransformationOptions();
}
try
{
if (transformerDebug.isEnabled())
depth.set(depth.get()+1);
// begin timing
long before = System.currentTimeMillis();
String sourceMimetype = reader.getMimetype();
String targetMimetype = writer.getMimetype();
// check options map
if (options == null)
{
transformerDebug.pushTransform(this, reader.getContentUrl(), reader.getMimetype(),
writer.getMimetype(), reader.getSize(), options);
options = new TransformationOptions();
}
// Check the transformability
checkTransformable(reader, writer, options);
// Pass on any limits to the reader
setReaderLimits(reader, writer, options);
try
{
if (transformerDebug.isEnabled())
{
transformerDebug.pushTransform(this, reader.getContentUrl(), sourceMimetype,
targetMimetype, reader.getSize(), options);
}
// Check the transformability
checkTransformable(reader, writer, options);
// Pass on any limits to the reader
setReaderLimits(reader, writer, options);
// Transform
transformInternal(reader, writer, options);
}
catch (ContentServiceTransientException cste)
{
// A transient failure has occurred within the content transformer.
// This should not be interpreted as a failure and therefore we should not
// update the transformer's average time.
// Transform
transformInternal(reader, writer, options);
}
catch (ContentServiceTransientException cste)
{
// A transient failure has occurred within the content transformer.
// This should not be interpreted as a failure and therefore we should not
// update the transformer's average time.
if (logger.isDebugEnabled())
{
logger.debug("Transformation has been transiently declined: \n" +
" reader: " + reader + "\n" +
" writer: " + writer + "\n" +
" options: " + options + "\n" +
" transformer: " + this);
}
// the finally block below will still perform tidyup. Otherwise we're done.
// We rethrow the exception
throw cste;
}
catch (Throwable e)
{
// Make sure that this transformation gets set back i.t.o. time taken.
// This will ensure that transformers that compete for the same transformation
// will be prejudiced against transformers that tend to fail
recordError(sourceMimetype, targetMimetype);
// Ask Tika to detect the document, and report back on if
// the current mime type is plausible
String differentType = getMimetypeService().getMimetypeIfNotMatches(reader.getReader());
// Report the error
if(differentType == null)
{
transformerDebug.debug(" Failed", e);
throw new ContentIOException("Content conversion failed: \n" +
" reader: " + reader + "\n" +
" writer: " + writer + "\n" +
" options: " + options.toString(false) + "\n" +
" limits: " + getLimits(reader, writer, options),
e);
}
else
{
transformerDebug.debug(" Failed: Mime type was '"+differentType+"'", e);
throw new ContentIOException("Content conversion failed: \n" +
" reader: " + reader + "\n" +
" writer: " + writer + "\n" +
" options: " + options.toString(false) + "\n" +
" limits: " + getLimits(reader, writer, options) + "\n" +
" claimed mime type: " + reader.getMimetype() + "\n" +
" detected mime type: " + differentType,
e);
}
}
finally
{
transformerDebug.popTransform();
// check that the reader and writer are both closed
if (reader.isChannelOpen())
{
logger.error("Content reader not closed by transformer: \n" +
" reader: " + reader + "\n" +
" transformer: " + this);
}
if (writer.isChannelOpen())
{
logger.error("Content writer not closed by transformer: \n" +
" writer: " + writer + "\n" +
" transformer: " + this);
}
}
// record time
long after = System.currentTimeMillis();
recordTime(sourceMimetype, targetMimetype, after - before);
// done
if (logger.isDebugEnabled())
{
logger.debug("Transformation has been transiently declined: \n" +
logger.debug("Completed transformation: \n" +
" reader: " + reader + "\n" +
" writer: " + writer + "\n" +
" options: " + options + "\n" +
" transformer: " + this);
}
// the finally block below will still perform tidyup. Otherwise we're done.
// We rethrow the exception
throw cste;
}
catch (Throwable e)
{
// Make sure that this transformation gets set back i.t.o. time taken.
// This will ensure that transformers that compete for the same transformation
// will be prejudiced against transformers that tend to fail
recordTime(60 * 1000); // 1 minute, i.e. rubbish
// Ask Tika to detect the document, and report back on if
// the current mime type is plausible
String differentType = getMimetypeService().getMimetypeIfNotMatches(reader.getReader());
// Report the error
if(differentType == null)
{
transformerDebug.debug(" Failed", e);
throw new ContentIOException("Content conversion failed: \n" +
" reader: " + reader + "\n" +
" writer: " + writer + "\n" +
" options: " + options.toString(false) + "\n" +
" limits: " + getLimits(reader, writer, options),
e);
}
else
{
transformerDebug.debug(" Failed: Mime type was '"+differentType+"'", e);
throw new ContentIOException("Content conversion failed: \n" +
" reader: " + reader + "\n" +
" writer: " + writer + "\n" +
" options: " + options.toString(false) + "\n" +
" limits: " + getLimits(reader, writer, options) + "\n" +
" claimed mime type: " + reader.getMimetype() + "\n" +
" detected mime type: " + differentType,
e);
}
}
finally
{
transformerDebug.popTransform();
// check that the reader and writer are both closed
if (reader.isChannelOpen())
{
logger.error("Content reader not closed by transformer: \n" +
" reader: " + reader + "\n" +
" transformer: " + this);
}
if (writer.isChannelOpen())
{
logger.error("Content writer not closed by transformer: \n" +
" writer: " + writer + "\n" +
" transformer: " + this);
}
}
// record time
long after = System.currentTimeMillis();
recordTime(after - before);
// done
if (logger.isDebugEnabled())
{
logger.debug("Completed transformation: \n" +
" reader: " + reader + "\n" +
" writer: " + writer + "\n" +
" options: " + options + "\n" +
" transformer: " + this);
depth.set(depth.get()+1);
}
}
@@ -336,7 +296,23 @@ public abstract class AbstractContentTransformer2 extends AbstractContentTransfo
*/
public synchronized long getTransformationTime()
{
return (long) averageTime;
return transformerConfig.getStatistics(this, null, null).getAverageTime();
}
/**
* @return Returns the calculated running average of the current transformations
*/
public synchronized long getTransformationTime(String sourceMimetype, String targetMimetype)
{
return transformerConfig.getStatistics(this, sourceMimetype, targetMimetype).getAverageTime();
}
/**
* @deprecated use method with mimetypes.
*/
protected final synchronized void recordTime(long transformationTime)
{
recordTime(TransformerConfig.ANY, TransformerConfig.ANY, transformationTime);
}
/**
@@ -348,20 +324,33 @@ public abstract class AbstractContentTransformer2 extends AbstractContentTransfo
* This method is thread-safe. The time spent in this method is negligible
* so the impact will be minor.
*
* @param sourceMimetype
* @param targetMimetype
* @param transformationTime the time it took to perform the transformation.
* The value may be 0.
*/
protected final synchronized void recordTime(long transformationTime)
protected final synchronized void recordTime(String sourceMimetype, String targetMimetype,
long transformationTime)
{
if (count == Long.MAX_VALUE)
transformerConfig.getStatistics(this, sourceMimetype, targetMimetype).recordTime(transformationTime);
if (depth.get() == 1)
{
// we have reached the max count - reduce it by half
// the average fluctuation won't be extreme
count /= 2L;
transformerConfig.getStatistics(null, sourceMimetype, targetMimetype).recordTime(transformationTime);
}
}
/**
* Records an error and updates the average time as if the transformation took a
* long time, so that it is less likely to be called again.
* @param sourceMimetype
* @param targetMimetype
*/
protected final synchronized void recordError(String sourceMimetype, String targetMimetype)
{
transformerConfig.getStatistics(this, sourceMimetype, targetMimetype).recordError();
if (depth.get() == 1)
{
transformerConfig.getStatistics(null, sourceMimetype, targetMimetype).recordError();
}
// adjust the average
count++;
double diffTime = ((double) transformationTime) - averageTime;
averageTime += diffTime / (double) count;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2012 Alfresco Software Limited.
* Copyright (C) 2005-2013 Alfresco Software Limited.
*
* This file is part of Alfresco
*
@@ -18,14 +18,22 @@
*/
package org.alfresco.repo.content.transform;
import static org.alfresco.service.cmr.repository.TransformationOptionLimits.OPT_MAX_PAGES;
import static org.alfresco.service.cmr.repository.TransformationOptionLimits.OPT_MAX_SOURCE_SIZE_K_BYTES;
import static org.alfresco.service.cmr.repository.TransformationOptionLimits.OPT_PAGE_LIMIT;
import static org.alfresco.service.cmr.repository.TransformationOptionLimits.OPT_READ_LIMIT_K_BYTES;
import static org.alfresco.service.cmr.repository.TransformationOptionLimits.OPT_READ_LIMIT_TIME_MS;
import static org.alfresco.service.cmr.repository.TransformationOptionLimits.OPT_TIMEOUT_MS;
import java.util.Map;
import java.util.Map.Entry;
import org.alfresco.repo.content.AbstractContentReader;
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.TransformationOptionLimits;
import org.alfresco.service.cmr.repository.TransformationOptions;
import org.springframework.beans.factory.BeanNameAware;
/**
* Provides transformation limits for {@link org.alfresco.repo.content.transform.ContentTransformer}
@@ -38,26 +46,14 @@ import org.springframework.beans.factory.BeanNameAware;
*
* @author Alan Davis
*/
public abstract class AbstractContentTransformerLimits extends ContentTransformerHelper implements ContentTransformer, BeanNameAware
public abstract class AbstractContentTransformerLimits extends ContentTransformerHelper implements ContentTransformer
{
/** Transformer wide Time, KBytes and page limits */
private TransformationOptionLimits limits = new TransformationOptionLimits();
/**
* Time, KBytes and page limits by source and target mimetype combination.
* The first map's key is the source mimetype. The second map's key is the
* target mimetype and the value are the limits. */
private Map<String, Map<String, TransformationOptionLimits>> mimetypeLimits;
/** Indicates if 'page' limits are supported. */
private boolean pageLimitsSupported;
/** For debug **/
protected TransformerDebug transformerDebug;
/** The bean name. Used in debug only. */
private String beanName;
/**
* Indicates if 'page' limits are supported.
* @return false by default.
@@ -172,21 +168,17 @@ public abstract class AbstractContentTransformerLimits extends ContentTransforme
*/
protected long getTimeoutMs()
{
return limits.getTimeoutMs();
return getLimits().getTimeoutMs();
}
/**
* Sets a timeout (ms) on the InputStream after which an IOExecption is thrown
* to terminate very slow transformations or to terminate (kill) a subprocess.
* @param timeoutMs in milliseconds. If less than or equal to zero (the default)
* there is no timeout.
* If greater than zero the {@code readLimitTimeMs} must not be set.
* @deprecated transformation limits are now set with global properties rather than spring configuration.
*/
public void setTimeoutMs(long timeoutMs)
{
limits.setTimeoutMs(timeoutMs);
deprecatedSetter(OPT_TIMEOUT_MS+'='+timeoutMs);
}
/**
* Gets the limit in terms of the amount of data read (by time) to limit transformations where
* only the start of the content is needed. After this limit is reached the InputStream reports
@@ -195,19 +187,15 @@ public abstract class AbstractContentTransformerLimits extends ContentTransforme
*/
protected long getReadLimitTimeMs()
{
return limits.getReadLimitTimeMs();
return getLimits().getReadLimitTimeMs();
}
/**
* Sets a limit in terms of the amount of data read (by time) to limit transformations where
* only the start of the content is needed. After this limit is reached the InputStream reports
* end of file.
* @param readLimitBytes if less than or equal to zero (the default) there is no limit.
* If greater than zero the {@code timeoutMs} must not be set.
* @deprecated transformation limits are now set with global properties rather than spring configuration.
*/
public void setReadLimitTimeMs(long readLimitTimeMs)
{
limits.setReadLimitTimeMs(readLimitTimeMs);
deprecatedSetter(OPT_READ_LIMIT_TIME_MS+'='+readLimitTimeMs);
}
/**
@@ -218,19 +206,15 @@ public abstract class AbstractContentTransformerLimits extends ContentTransforme
*/
protected long getMaxSourceSizeKBytes()
{
return limits.getMaxSourceSizeKBytes();
return getLimits().getMaxSourceSizeKBytes();
}
/**
* Sets a maximum source content size, to skip transformations where
* the source is just too large to expect it to perform. If the source is larger
* the transformer indicates it is not available.
* @param maxSourceSizeKBytes if less than or equal to zero (the default) there is no limit.
* If greater than zero the {@code readLimitKBytes} must not be set.
* @deprecated transformation limits are now set with global properties rather than spring configuration.
*/
public void setMaxSourceSizeKBytes(long maxSourceSizeKBytes)
{
limits.setMaxSourceSizeKBytes(maxSourceSizeKBytes);
deprecatedSetter(OPT_MAX_SOURCE_SIZE_K_BYTES+'='+maxSourceSizeKBytes);
}
/**
@@ -241,19 +225,15 @@ public abstract class AbstractContentTransformerLimits extends ContentTransforme
*/
protected long getReadLimitKBytes()
{
return limits.getReadLimitKBytes();
return getLimits().getReadLimitKBytes();
}
/**
* Sets a limit in terms of the about of data read to limit transformations where
* only the start of the content is needed. After this limit is reached the InputStream reports
* end of file.
* @param readLimitKBytes if less than or equal to zero (the default) there is no limit.
* If greater than zero the {@code maxSourceSizeKBytes} must not be set.
* @deprecated transformation limits are now set with global properties rather than spring configuration.
*/
public void setReadLimitKBytes(long readLimitKBytes)
{
limits.setReadLimitKBytes(readLimitKBytes);
deprecatedSetter(OPT_READ_LIMIT_K_BYTES+'='+readLimitKBytes);
}
/**
@@ -262,18 +242,15 @@ public abstract class AbstractContentTransformerLimits extends ContentTransforme
*/
protected int getMaxPages()
{
return limits.getMaxPages();
return getLimits().getMaxPages();
}
/**
* Set the number of pages read from the source before an exception is thrown.
*
* @param maxPages the number of pages to be read from the source. If less than or equal to zero
* (the default) no limit is applied.
* @deprecated transformation limits are now set with global properties rather than spring configuration.
*/
public void setMaxPages(int maxPages)
{
limits.setMaxPages(maxPages);
deprecatedSetter(OPT_MAX_PAGES+'='+maxPages);
}
/**
@@ -282,18 +259,15 @@ public abstract class AbstractContentTransformerLimits extends ContentTransforme
*/
protected int getPageLimit()
{
return limits.getPageLimit();
return getLimits().getPageLimit();
}
/**
* Set the number of pages read from the source before returning EOF.
*
* @param pageLimit the number of pages to be read from the source. If less
* than or equal to zero (the default) no limit is applied.
* @deprecated transformation limits are now set with global properties rather than spring configuration.
*/
public void setPageLimit(int pageLimit)
{
limits.setPageLimit(pageLimit);
deprecatedSetter(OPT_PAGE_LIMIT+'='+pageLimit);
}
/**
@@ -301,33 +275,57 @@ public abstract class AbstractContentTransformerLimits extends ContentTransforme
*/
protected TransformationOptionLimits getLimits()
{
return limits;
return transformerConfig.getLimits(this, null, null);
}
/**
* Sets max and limit values for time, size and pages in a single operation.
* @deprecated transformation limits are now set with global properties rather than spring configuration.
*/
public void setLimits(TransformationOptionLimits limits)
{
this.limits = limits;
deprecatedLimitsSetter("", limits);
}
/**
* Gets the max and limit values for time, size and pages per source and target mimetype
* combination.
*/
protected Map<String, Map<String, TransformationOptionLimits>> getMimetypeLimits()
{
return mimetypeLimits;
}
/**
* Sets the max and limit values for time, size and pages per source and target mimetype
* combination.
* @deprecated transformation limits are now set with global properties rather than spring configuration.
*/
public void setMimetypeLimits(Map<String, Map<String, TransformationOptionLimits>> mimetypeLimits)
{
this.mimetypeLimits = mimetypeLimits;
for (Entry<String, Map<String, TransformationOptionLimits>> source: mimetypeLimits.entrySet())
{
String sourceExt = getExtensionOrAny(source.getKey());
for (Entry<String, TransformationOptionLimits> target: source.getValue().entrySet())
{
String targetExt = getExtensionOrAny(target.getKey());
TransformationOptionLimits limits = target.getValue();
String mimetypeSuffix = TransformerConfig.MIMETYPES_SEPARATOR.substring(1)+sourceExt+'.'+targetExt;
deprecatedLimitsSetter(mimetypeSuffix, limits);
}
}
}
private void deprecatedLimitsSetter(String mimetypeSuffix, TransformationOptionLimits limits)
{
if (limits.supported())
{
// Ignore limit pairs that are not specified
for (String limit: new String[] {
limits.getTimePair().toString(OPT_TIMEOUT_MS, OPT_READ_LIMIT_TIME_MS),
limits.getKBytesPair().toString(OPT_MAX_SOURCE_SIZE_K_BYTES, OPT_READ_LIMIT_K_BYTES),
limits.getPagesPair().toString(OPT_MAX_PAGES, OPT_PAGE_LIMIT)
})
{
if (limit != null)
{
deprecatedSetter(mimetypeSuffix+'.'+limit);
}
}
}
else
{
deprecatedSetter(mimetypeSuffix+TransformerConfig.SUPPORTED+"=false");
}
}
/**
@@ -339,7 +337,7 @@ public abstract class AbstractContentTransformerLimits extends ContentTransforme
TransformationOptions options)
{
return (reader == null || writer == null)
? limits.combine(options.getLimits())
? getLimits().combine(options.getLimits())
: getLimits(reader.getMimetype(), writer.getMimetype(), options);
}
@@ -351,63 +349,8 @@ public abstract class AbstractContentTransformerLimits extends ContentTransforme
protected TransformationOptionLimits getLimits(String sourceMimetype, String targetMimetype,
TransformationOptions options)
{
// Get the limits for the source and target mimetypes
TransformationOptionLimits mimetypeLimits = null;
if (this.mimetypeLimits != null)
{
boolean anySource = false;
Map<String, TransformationOptionLimits> targetLimits =
this.mimetypeLimits.get(sourceMimetype);
if (targetLimits == null)
{
targetLimits = this.mimetypeLimits.get("*");
anySource = true;
}
if (targetLimits != null)
{
mimetypeLimits = targetLimits.get(targetMimetype);
if (mimetypeLimits == null)
{
mimetypeLimits = targetLimits.get("*");
// Allow for the case where have specific source and target mimetype limits
// and general source limits (avoid having to repeat the general values in
// each specific source definition)
if (mimetypeLimits == null && !anySource)
{
targetLimits = this.mimetypeLimits.get("*");
if (targetLimits != null)
{
mimetypeLimits = targetLimits.get(targetMimetype);
if (mimetypeLimits == null)
{
mimetypeLimits = targetLimits.get("*");
}
}
}
}
}
}
TransformationOptionLimits combined = (mimetypeLimits == null) ? limits : limits.combine(mimetypeLimits);
return (options == null) ? combined : combined.combine(options.getLimits());
}
/**
* Sets the Spring bean name - only for use in debug.
*/
@Override
public void setBeanName(String beanName)
{
this.beanName = beanName;
}
/**
* Returns the Spring bean name - only for use in debug.
*/
public String getBeanName()
{
return beanName;
TransformationOptionLimits limits = transformerConfig.getLimits(this, sourceMimetype, targetMimetype);
return (options == null) ? limits : limits.combine(options.getLimits());
}
/**

View File

@@ -28,6 +28,7 @@ import java.util.Map;
import org.alfresco.repo.content.AbstractContentReader;
import org.alfresco.repo.content.ContentMinimalContextTestSuite;
import org.alfresco.repo.content.AbstractContentReaderLimitTest.DummyAbstractContentReader;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentWriter;
@@ -43,9 +44,9 @@ import org.springframework.context.ApplicationContext;
*/
public class AbstractContentTransformerLimitsTest
{
private static final String A = "a";
private static final String B = "b";
private static final String C = "c";
private static final String A = MimetypeMap.MIMETYPE_XML;
private static final String B = MimetypeMap.MIMETYPE_HTML;
private static final String C = MimetypeMap.MIMETYPE_PDF;
private AbstractContentTransformerLimits transformer;
private TransformationOptionLimits limits;
@@ -59,6 +60,7 @@ public class AbstractContentTransformerLimitsTest
ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY);
MimetypeService mimetypeService = serviceRegistry.getMimetypeService();
TransformerDebug transformerDebug = (TransformerDebug) ctx.getBean("transformerDebug");
TransformerConfig transformerConfig = (TransformerConfig) ctx.getBean("transformerConfig");
transformer = new AbstractContentTransformer2()
{
@@ -77,6 +79,8 @@ public class AbstractContentTransformerLimitsTest
};
transformer.setMimetypeService(mimetypeService);
transformer.setTransformerDebug(transformerDebug);
transformer.setTransformerConfig(transformerConfig);
transformer.setBeanName("transformer.test"+System.currentTimeMillis()%100000);
limits = new TransformationOptionLimits();
options = new TransformationOptions();
@@ -104,6 +108,7 @@ public class AbstractContentTransformerLimitsTest
{
long value = 1234;
transformer.setTimeoutMs(value);
transformer.register();
long actual = transformer.getTimeoutMs();
assertEquals("Getter did not return set value", value, actual);
}
@@ -113,6 +118,7 @@ public class AbstractContentTransformerLimitsTest
{
long value = 1234;
transformer.setReadLimitTimeMs(value);
transformer.register();
long actual = transformer.getReadLimitTimeMs();
assertEquals("Getter did not return set value", value, actual);
}
@@ -122,6 +128,7 @@ public class AbstractContentTransformerLimitsTest
{
long value = 1234;
transformer.setMaxSourceSizeKBytes(value);
transformer.register();
long actual = transformer.getMaxSourceSizeKBytes();
assertEquals("Getter did not return set value", value, actual);
}
@@ -131,6 +138,7 @@ public class AbstractContentTransformerLimitsTest
{
long value = 1234;
transformer.setReadLimitKBytes(value);
transformer.register();
long actual = transformer.getReadLimitKBytes();
assertEquals("Getter did not return set value", value, actual);
}
@@ -140,6 +148,7 @@ public class AbstractContentTransformerLimitsTest
{
int value = 1234;
transformer.setMaxPages(value);
transformer.register();
int actual = transformer.getMaxPages();
assertEquals("Getter did not return set value", value, actual);
}
@@ -149,6 +158,7 @@ public class AbstractContentTransformerLimitsTest
{
int value = 1234;
transformer.setPageLimit(value);
transformer.register();
int actual = transformer.getPageLimit();
assertEquals("Getter did not return set value", value, actual);
}
@@ -161,6 +171,7 @@ public class AbstractContentTransformerLimitsTest
addMimetypeLimits(A, B, limits);
transformer.setMimetypeLimits(mimetypeLimits);
transformer.register();
long actual = transformer.getLimits(A, B, options).getMaxSourceSizeKBytes();
assertEquals("Getter did not return set value", value, actual);
@@ -176,6 +187,7 @@ public class AbstractContentTransformerLimitsTest
addMimetypeLimits(A, "*", limits);
transformer.setMimetypeLimits(mimetypeLimits);
transformer.register();
long actual = transformer.getLimits(A, B, options).getMaxSourceSizeKBytes();
assertEquals("Getter did not return set value", value, actual);
@@ -191,6 +203,7 @@ public class AbstractContentTransformerLimitsTest
addMimetypeLimits("*", B, limits);
transformer.setMimetypeLimits(mimetypeLimits);
transformer.register();
long actual = transformer.getLimits(A, B, options).getMaxSourceSizeKBytes();
assertEquals("Getter did not return set value", value, actual);
@@ -206,6 +219,7 @@ public class AbstractContentTransformerLimitsTest
addMimetypeLimits(A, B, limits);
transformer.setMimetypeLimits(mimetypeLimits);
transformer.register();
long actual = transformer.getLimits(A, B, options).getMaxSourceSizeKBytes();
assertEquals("Getter did not return set value", value+1, actual);
@@ -228,6 +242,7 @@ public class AbstractContentTransformerLimitsTest
limits.setMaxSourceSizeKBytes(kValue);
addMimetypeLimits(A, B, limits);
transformer.setMimetypeLimits(mimetypeLimits);
transformer.register();
assertEquals("Expected to have set value returned", kValue,
transformer.getMaxSourceSizeKBytes(A, B, options));
@@ -235,51 +250,115 @@ public class AbstractContentTransformerLimitsTest
// With a mimetype that does not have any specific limits
assertEquals("Expected to have -1 (unlimited) returned", -1,
transformer.getMaxSourceSizeKBytes(C, B, options));
}
@Test
// Combination using just transformer limit to start with
public void testGetMaxSourceSizeKBytesCombination() throws Exception
{
long kValue = 12;
long byteValue = kValue*1024;
// Clear the mimetype limits and double check
limits.setMaxSourceSizeKBytes(-1);
assertEquals("Expected to have -1 (unlimited) returned", -1,
transformer.getMaxSourceSizeKBytes(A, B, options));
// Not set mimetype limits yet
assertTrue("No limits so should have been ok",
transformer.isTransformableSize(A, byteValue+1, B, options));
// Check for combinations with transformer limits
// a) Using just transformer limit to start with
transformer.setMaxSourceSizeKBytes(kValue);
transformer.register();
assertEquals("Expected to have transformer set value returned", kValue,
transformer.getMaxSourceSizeKBytes(A, B, options));
}
// b) combination where transformer limit is used
@Test
// Combination where transformer limit is used
public void testGetMaxSourceSizeKBytesCombinationTransUsed() throws Exception
{
long kValue = 12;
long byteValue = kValue*1024;
// Not set mimetype limits yet
assertTrue("No limits so should have been ok",
transformer.isTransformableSize(A, byteValue+1, B, options));
transformer.setMaxSourceSizeKBytes(kValue);
limits.setMaxSourceSizeKBytes(kValue+1);
addMimetypeLimits(A, B, limits);
transformer.setMimetypeLimits(mimetypeLimits);
transformer.register();
assertEquals("Expected to have transformer set value returned", kValue,
transformer.getMaxSourceSizeKBytes(A, B, options));
// c) combination where mimetype limit is used
}
@Test
// Combination where mimetype limit is used
public void testGetMaxSourceSizeKBytesCombinationMimetypeUsed() throws Exception
{
long kValue = 12;
long byteValue = kValue*1024;
// Not set mimetype limits yet
assertTrue("No limits so should have been ok",
transformer.isTransformableSize(A, byteValue+1, B, options));
transformer.setMaxSourceSizeKBytes(kValue+1);
limits.setMaxSourceSizeKBytes(kValue);
addMimetypeLimits(A, B, limits);
transformer.setMimetypeLimits(mimetypeLimits);
transformer.register();
assertEquals("Expected to have transformer set value returned", kValue,
transformer.getMaxSourceSizeKBytes(A, B, options));
}
@Test
// Check no limit when page limit set on a transformer that does not support page limit
// maxSourceSizeKbytes value should be ignored if a page limit is in use
public void testGetMaxSourceSizeKBytesPageSupportsNot() throws Exception
{
long kValue = 12;
long byteValue = kValue*1024;
// Check no limit when page limit set on a transformer that does not support page limit
// Not set mimetype limits yet
assertTrue("No limits so should have been ok",
transformer.isTransformableSize(A, byteValue+1, B, options));
transformer.setPageLimitsSuported(false);
transformer.setMaxSourceSizeKBytes(kValue);
limits.setMaxSourceSizeKBytes(kValue+1);
limits.setPageLimit(1);
addMimetypeLimits(A, B, limits);
transformer.setMimetypeLimits(mimetypeLimits);
transformer.register();
assertEquals("Expected to ignore the page limit as the transformer does not support it", kValue,
transformer.getMaxSourceSizeKBytes(A, B, options));
}
@Test
// Check no limit when page limit set on a transformer that does support page limit
// maxSourceSizeKbytes value should be ignored if a page limit is in use
public void testGetMaxSourceSizeKBytesPageSupports() throws Exception
{
long kValue = 12;
long byteValue = kValue*1024;
// Check no limit when page limit set on a transformer that does support page limit
// Not set mimetype limits yet
assertTrue("No limits so should have been ok",
transformer.isTransformableSize(A, byteValue+1, B, options));
transformer.setPageLimitsSuported(true);
transformer.setMaxSourceSizeKBytes(kValue);
limits.setMaxSourceSizeKBytes(kValue+1);
transformer.setPageLimitsSuported(true);
limits.setPageLimit(1);
addMimetypeLimits(A, B, limits);
transformer.setMimetypeLimits(mimetypeLimits);
transformer.register();
assertEquals("Expected to have -1 (unlimited) returned when there are page limits", -1,
transformer.getMaxSourceSizeKBytes(A, B, options));
}
@Test
public void testIsTransformableSize() throws Exception
// Using limit on a mimetype
public void testIsTransformableSizeMimetype() throws Exception
{
long kValue = 12;
long byteValue = kValue*1024;
@@ -292,12 +371,13 @@ public class AbstractContentTransformerLimitsTest
limits.setMaxSourceSizeKBytes(kValue);
addMimetypeLimits(A, B, limits);
transformer.setMimetypeLimits(mimetypeLimits);
transformer.register();
assertTrue("Size is less than limit so should have been ok",
transformer.isTransformableSize(A, byteValue-1, B, options));
assertTrue("Size is equal to limit so should have been ok",
transformer.isTransformableSize(A, byteValue, B, options));
assertFalse("Size is greater than limit so should not have failed",
assertFalse("Size is greater than limit so should have failed",
transformer.isTransformableSize(A, byteValue+1, B, options));
// With a mimetype that does not have any specific limits
@@ -307,36 +387,68 @@ public class AbstractContentTransformerLimitsTest
transformer.isTransformableSize(A, byteValue+1, C, options));
assertTrue("No limits so should have been ok",
transformer.isTransformableSize(C, byteValue+1, C, options));
// Clear the mimetype limits and double check
limits.setMaxSourceSizeKBytes(-1);
}
@Test
// Using limit on transformer as a whole
public void testIsTransformableSizeTrans() throws Exception
{
long kValue = 12;
long byteValue = kValue*1024;
// Not set mimetype limits yet
assertTrue("No limits so should have been ok",
transformer.isTransformableSize(A, byteValue+1, B, options));
// Check for combinations with transformer limits
// a) Using just transformer limit to start with
transformer.setMaxSourceSizeKBytes(kValue);
transformer.register();
assertTrue("Size is equal to limit so should have been ok",
transformer.isTransformableSize(A, byteValue, B, options));
assertFalse("Size is greater than limit so should not have failed",
assertFalse("Size is greater than limit so should have failed",
transformer.isTransformableSize(A, byteValue+1, B, options));
}
// b) combination where transformer limit is used
@Test
// Combination where transformer limit is used
public void testIsTransformableSizeCombinationTransUsed() throws Exception
{
long kValue = 12;
long byteValue = kValue*1024;
// Not set mimetype limits yet
assertTrue("No limits so should have been ok",
transformer.isTransformableSize(A, byteValue+1, B, options));
transformer.setMaxSourceSizeKBytes(kValue);
limits.setMaxSourceSizeKBytes(kValue+1);
addMimetypeLimits(A, B, limits);
transformer.setMimetypeLimits(mimetypeLimits);
transformer.register();
assertTrue("Size is equal to limit so should have been ok",
transformer.isTransformableSize(A, byteValue, B, options));
assertFalse("Size is greater than limit so should not have failed",
assertFalse("Size is greater than limit so should have failed",
transformer.isTransformableSize(A, byteValue+1, B, options));
}
// c) combination where mimetype limit is used
@Test
// Combination where mimetype limit is used
public void testIsTransformableSizeCombinationMimetypeUsed() throws Exception
{
long kValue = 12;
long byteValue = kValue*1024;
// Not set mimetype limits yet
assertTrue("No limits so should have been ok",
transformer.isTransformableSize(A, byteValue+1, B, options));
transformer.setMaxSourceSizeKBytes(kValue+1);
limits.setMaxSourceSizeKBytes(kValue);
addMimetypeLimits(A, B, limits);
transformer.setMimetypeLimits(mimetypeLimits);
transformer.register();
assertTrue("Size is equal to limit so should have been ok",
transformer.isTransformableSize(A, byteValue, B, options));
assertFalse("Size is greater than limit so should not have failed",
assertFalse("Size is greater than limit so should have failed",
transformer.isTransformableSize(A, byteValue+1, B, options));
}
@@ -348,6 +460,7 @@ public class AbstractContentTransformerLimitsTest
transformer.setMaxSourceSizeKBytes(kValue);
transformer.setPageLimitsSuported(true);
transformer.register();
// Test works as normal before setting the pageLimit
assertTrue("Size is less than limit so should have been ok",
@@ -370,6 +483,7 @@ public class AbstractContentTransformerLimitsTest
long value = 1234;
transformer.setTimeoutMs(value);
transformer.register();
assertEquals("Limit should not have been set in the reader", null, reader.getLimits());

View File

@@ -70,6 +70,7 @@ public abstract class AbstractContentTransformerTest extends TestCase
protected ServiceRegistry serviceRegistry;
protected MimetypeService mimetypeService;
protected TransformerDebug transformerDebug;
protected TransformerConfig transformerConfig;
/**
* Fetches a transformer to test for a given transformation. The transformer
@@ -97,6 +98,8 @@ public abstract class AbstractContentTransformerTest extends TestCase
serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY);
mimetypeService = serviceRegistry.getMimetypeService();
transformerDebug = (TransformerDebug) ctx.getBean("transformerDebug");
transformerConfig = (TransformerConfig) ctx.getBean("transformerConfig");
// perform a little cleaning up
long now = System.currentTimeMillis();
TempFileProvider.TempFileCleanerJob.removeFiles(now);

View File

@@ -40,6 +40,7 @@ public class AppleIWorksContentTransformerTest extends AbstractContentTransforme
// Ugly cast just to set the MimetypeService
((ContentTransformerHelper)transformer).setMimetypeService(mimetypeService);
((ContentTransformerHelper)transformer).setTransformerConfig(transformerConfig);
}
@Override

View File

@@ -47,6 +47,7 @@ public class ArchiveContentTransformerTest extends AbstractContentTransformerTes
transformer = new ArchiveContentTransformer();
transformer.setMimetypeService(mimetypeService);
transformer.setTransformerDebug(transformerDebug);
transformer.setTransformerConfig(transformerConfig);
}
protected ContentTransformer getTransformer(String sourceMimetype, String targetMimetype)

View File

@@ -38,6 +38,7 @@ public class BinaryPassThroughContentTransformerTest extends AbstractContentTran
transformer = new BinaryPassThroughContentTransformer();
transformer.setMimetypeService(mimetypeService);
transformer.setTransformerDebug(transformerDebug);
transformer.setTransformerConfig(transformerConfig);
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2012 Alfresco Software Limited.
* Copyright (C) 2005-2013 Alfresco Software Limited.
*
* This file is part of Alfresco
*
@@ -267,7 +267,7 @@ public class ComplexContentTransformer extends AbstractContentTransformer2 imple
transformer = transformerIterator.next();
if (transformer != null)
{
if (transformer.isTransformableMimetype(sourceMimetype, targetMimetype, options))
if (transformer.isTransformable(sourceMimetype, -1, targetMimetype, options))
{
return false;
}
@@ -306,7 +306,7 @@ public class ComplexContentTransformer extends AbstractContentTransformer2 imple
}
else
{
if (transformer.isTransformableMimetype(currentSourceMimetype, currentTargetMimetype, options) == false)
if (transformer.isTransformable(currentSourceMimetype, -1, currentTargetMimetype, options) == false)
{
result = false;
break;

View File

@@ -63,6 +63,7 @@ public class ComplexContentTransformerTest extends AbstractContentTransformerTes
transformer = new ComplexContentTransformer();
transformer.setMimetypeService(mimetypeService);
transformer.setTransformerDebug(transformerDebug);
transformer.setTransformerConfig(transformerConfig);
// set the transformer list
List<ContentTransformer> transformers = new ArrayList<ContentTransformer>(2);
transformers.add(unoTransformer);

View File

@@ -78,29 +78,39 @@ public interface ContentTransformer extends ContentWorker
public long getMaxSourceSizeKBytes(String sourceMimetype, String targetMimetype, TransformationOptions options);
/**
* Indicates whether given the provided transformation parmaters this transformer can prvide an explict
* @deprecated Use transformer priority and unsupported transformer properties.
*
* Indicates whether given the provided transformation parameters this transformer can provide an explicit
* transformation.
*
* An explict transformation indicates that the transformation happens directly and not as a result of
* another transformation process. Explict transformation always take presidence over normal transformations.
* An explicit transformation indicates that the transformation happens directly and not as a result of
* another transformation process. Explicit transformation always take presidency over normal transformations.
*
* @param sourceMimetype the source mimetype
* @param targetMimetype the target mimetype
* @param options the transformation options
* @return boolean true if it is an explicit transformation, flase otherwise
* @return boolean true if it is an explicit transformation, false otherwise
*/
public boolean isExplicitTransformation(String sourceMimetype, String targetMimetype, TransformationOptions options);
/**
* @deprecated use mimetype specific version.
*/
public long getTransformationTime();
/**
* Provides an estimate, usually a worst case guess, of how long a transformation
* will take.
* will take. Null mimetype values provide the overall value for the transformer.
* <p>
* This method is used to determine, up front, which of a set of
* equally reliant transformers will be used for a specific transformation.
*
* @param sourceMimetype the source mimetype
* @param targetMimetype the target mimetype
*
* @return Returns the approximate number of milliseconds per transformation
*/
public long getTransformationTime();
public long getTransformationTime(String sourceMimetype, String targetMimetype);
/**
* @see #transform(ContentReader, ContentWriter, TransformationOptions)
@@ -157,4 +167,9 @@ public interface ContentTransformer extends ContentWorker
*/
public void transform(ContentReader reader, ContentWriter contentWriter, TransformationOptions options)
throws ContentIOException;
/**
* Returns transformer's name used in configuration.
*/
public String getName();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2012 Alfresco Software Limited.
* Copyright (C) 2005-2013 Alfresco Software Limited.
*
* This file is part of Alfresco
*
@@ -18,37 +18,36 @@
*/
package org.alfresco.repo.content.transform;
import java.util.Collections;
import static org.alfresco.repo.content.transform.TransformerConfig.ANY;
import java.util.ArrayList;
import java.util.List;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.service.cmr.repository.ContentAccessor;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.TransformationOptions;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanNameAware;
/**
* A class providing basic functionality shared by both {@link ContentTransformer}s and {@link ContentTransformerWorker}
* s.
* A class providing basic functionality shared by both {@link ContentTransformer}s and {@link ContentTransformerWorker}s.
*
* @author dward
*/
public class ContentTransformerHelper
public class ContentTransformerHelper implements BeanNameAware
{
private static final Log logger = LogFactory.getLog(ContentTransformerHelper.class);
private MimetypeService mimetypeService;
private List<ExplictTransformationDetails> explicitTransformations;
private List<SupportedTransformation> supportedTransformations;
private List<SupportedTransformation> unsupportedTransformations;
protected TransformerConfig transformerConfig;
private List<String> deprecatedSetterMessages;
private static boolean firstDeprecatedSetter = true;
/**
*
*/
public ContentTransformerHelper()
{
setExplicitTransformations(Collections.<ExplictTransformationDetails> emptyList());
setSupportedTransformations(null);
setUnsupportedTransformations(null);
}
/** The bean name. */
private String beanName;
/**
* Helper setter of the mimetype service. This is not always required.
@@ -69,37 +68,35 @@ public class ContentTransformerHelper
}
/**
* Specifies transformations that are considered to be 'exceptional' so
* should be used in preference to other transformers that can perform
* the same transformation.
* @deprecated supported transformations are now set with global properties rather than spring configuration.
*/
public void setExplicitTransformations(List<ExplictTransformationDetails> explicitTransformations)
{
this.explicitTransformations = explicitTransformations;
deprecatedSupportedTransformations(explicitTransformations, null);
// TODO Should suggest properties that indicate lower priority transformers should be unsupported.
// This is for completeness rather than needed as the priority will avoid the non explicit
// transformers from being used. Explicit transformers are given a priority of 5 rather than 10.
}
/**
* Restricts the transformations that may be performed even though the transformer
* may perform other transformations. An null value applies no additional restrictions.
* Even if a list is specified, the
* {@link ContentTransformer#isTransformableMimetype(String, String, TransformationOptions)}
* method will still be called.
* @deprecated supported transformations are now set with global properties rather than spring configuration.
*/
public void setSupportedTransformations(List<SupportedTransformation> supportedTransformations)
{
this.supportedTransformations = supportedTransformations;
deprecatedSupportedTransformations(supportedTransformations, "true");
}
/**
* Restricts the transformations that may be performed even though the transformer
* may claim to perform the transformations. An null value applies no additional restrictions.
* Even if a list is specified, the
* {@link ContentTransformer#isTransformableMimetype(String, String, TransformationOptions)}
* method will still be called.
* @deprecated supported transformations are now set with global properties rather than spring configuration.
*/
public void setUnsupportedTransformations(List<SupportedTransformation> unsupportedTransformations)
{
this.unsupportedTransformations = unsupportedTransformations;
deprecatedSupportedTransformations(unsupportedTransformations, "false");
}
public void setTransformerConfig(TransformerConfig transformerConfig)
{
this.transformerConfig = transformerConfig;
}
/**
@@ -123,58 +120,138 @@ public class ContentTransformerHelper
}
/**
* Default implementation, override if need to extend logic
* @deprecated Should now use priority and unsupported transformer properties.
*
* @see org.alfresco.repo.content.transform.ContentTransformer#isExplicitTransformation(java.lang.String,
* java.lang.String, org.alfresco.service.cmr.repository.TransformationOptions)
*/
public boolean isExplicitTransformation(String sourceMimetype, String targetMimetype, TransformationOptions options)
{
boolean result = false;
for (ExplictTransformationDetails explicitTransformation : this.explicitTransformations)
{
if (sourceMimetype.equals(explicitTransformation.getSourceMimetype()) == true
&& targetMimetype.equals(explicitTransformation.getTargetMimetype()) == true)
{
result = true;
break;
}
}
return result;
return transformerConfig.getPriority(((ContentTransformer)this), sourceMimetype, targetMimetype) == TransformerConfig.PRIORITY_EXPLICIT;
}
public boolean isSupportedTransformation(String sourceMimetype, String targetMimetype, TransformationOptions options)
{
boolean supported = true;
if (supportedTransformations != null)
return transformerConfig.isSupportedTransformation(((ContentTransformer)this), sourceMimetype, targetMimetype, options);
}
/**
* Sets the Spring bean name.
*/
@Override
public void setBeanName(String beanName)
{
this.beanName = beanName;
}
/**
* THIS IS A CUSTOM SPRING INIT METHOD
*/
public void register()
{
logDeprecatedSetter();
}
/**
* Returns the Spring bean name.
*/
public String getBeanName()
{
return beanName;
}
/**
* Returns transformer name. Uses the Spring bean name, but if null uses the class name.
*/
public String getName()
{
return (beanName == null) ? getClass().getSimpleName() : beanName;
}
/**
* Called by deprecated property setter methods that should no longer be called
* by Spring configuration as the values are now set using global properties.
* @param suffixAndValue that should have been used. The first part of the
* property name "content.transformer.<name>." should not be included.
* The reason is that the setter methods might be called before the bean
* name is set.
*/
protected void deprecatedSetter(String suffixAndValue)
{
if (deprecatedSetterMessages == null)
{
supported = false;
for (SupportedTransformation suportedTransformation : supportedTransformations)
deprecatedSetterMessages = new ArrayList<String>();
}
deprecatedSetterMessages.add(suffixAndValue);
}
/**
* Called when the bean name is set after all the deprecated setters to log
* INFO messages with the Alfresco global properties that should now be set
* (if no set) to replace Spring configuration.
*/
private void logDeprecatedSetter()
{
if (deprecatedSetterMessages != null)
{
for (String suffixAndValue: deprecatedSetterMessages)
{
String supportedSourceMimetype = suportedTransformation.getSourceMimetype();
String supportedTargetMimetype = suportedTransformation.getTargetMimetype();
if ((supportedSourceMimetype == null || sourceMimetype.equals(supportedSourceMimetype)) &&
(supportedTargetMimetype == null || targetMimetype.equals(supportedTargetMimetype)))
String propertyNameAndValue = TransformerConfig.CONTENT+beanName+'.'+suffixAndValue;
String propertyName = propertyNameAndValue.replaceAll("=.*", "");
if (transformerConfig.getProperty(propertyName) == null)
{
supported = true;
break;
if (firstDeprecatedSetter)
{
firstDeprecatedSetter = false;
logger.error("In order to support dynamic setting of transformer options, Spring XML configuration");
logger.error("is no longer used to initialise these options.");
logger.error(" ");
logger.error("Your system appears to contains custom Spring configuration which should be replace by");
logger.error("the following Alfresco global properties. In the case of the Enterprise edition these");
logger.error("values may then be dynamically changed via JMX.");
logger.error(" ");
// Note: Cannot set these automatically because, an MBean reset would clear them.
}
logger.error(propertyNameAndValue);
// Add them to the subsystem's properties anyway (even though an MBean reset would clear them),
// so that existing unit tests work.
transformerConfig.setProperty(propertyNameAndValue);
}
else
{
logger.warn(propertyNameAndValue+" is set, but spring config still exists");
}
}
deprecatedSetterMessages = null;
}
if (supported && unsupportedTransformations != null)
}
private void deprecatedSupportedTransformations(List<? extends SupportedTransformation> transformations, String value)
{
if (transformations != null)
{
for (SupportedTransformation unsuportedTransformation : unsupportedTransformations)
for (SupportedTransformation transformation: transformations)
{
String unsupportedSourceMimetype = unsuportedTransformation.getSourceMimetype();
String unsupportedTargetMimetype = unsuportedTransformation.getTargetMimetype();
if ((unsupportedSourceMimetype == null || sourceMimetype.equals(unsupportedSourceMimetype)) &&
(unsupportedTargetMimetype == null || targetMimetype.equals(unsupportedTargetMimetype)))
{
supported = false;
break;
}
String sourceMimetype = transformation.getSourceMimetype();
String targetMimetype = transformation.getTargetMimetype();
String sourceExt = getExtensionOrAny(sourceMimetype);
String targetExt = getExtensionOrAny(targetMimetype);
deprecatedSetter(TransformerConfig.MIMETYPES_SEPARATOR.substring(1)+sourceExt+'.'+targetExt+
(value == null // same as: transformation instanceof ExplictTransformationDetails
? TransformerConfig.PRIORITY+"="+TransformerConfig.PRIORITY_EXPLICIT
: TransformerConfig.SUPPORTED+"="+value));
}
}
return supported;
}
protected String getExtensionOrAny(String mimetype)
{
return mimetype == null || ANY.equals(mimetype) ? ANY : mimetypeService.getExtension(mimetype);
}
public String toString()
{
return getName();
}
}

View File

@@ -57,7 +57,7 @@ public class ContentTransformerRegistry
public ContentTransformerRegistry(TransformerSelector transformerSelector)
{
this.transformerSelector = transformerSelector;
this.transformers = new ArrayList<ContentTransformer>(10);
this.transformers = new ArrayList<ContentTransformer>(70);
}
/**
@@ -114,7 +114,7 @@ public class ContentTransformerRegistry
public List<ContentTransformer> getActiveTransformers(String sourceMimetype, long sourceSize, String targetMimetype, TransformationOptions options)
{
// Get the list of transformers
List<ContentTransformer> transformers = transformerSelector.selectTransformers(this.transformers, sourceMimetype, sourceSize, targetMimetype, options);
List<ContentTransformer> transformers = transformerSelector.selectTransformers(sourceMimetype, sourceSize, targetMimetype, options);
if (logger.isDebugEnabled())
{
logger.debug("Searched for transformer: \n" +

View File

@@ -52,6 +52,12 @@ public class ContentTransformerRegistryTest extends AbstractContentTransformerTe
private ContentReader reader;
private ContentWriter writer;
private DummyTransformer ad20;
private DummyTransformer ad30;
private DummyTransformer ad10;
private DummyTransformer ad25a;
private DummyTransformer ad25b;
@Override
public void setUp() throws Exception
{
@@ -70,19 +76,23 @@ public class ContentTransformerRegistryTest extends AbstractContentTransformerTe
bytes[i] = (byte)i;
}
// create the dummyRegistry
dummyRegistry = new ContentTransformerRegistry(new TransformerSelectorImpl());
TransformerSelectorImpl transformerSelector = new TransformerSelectorImpl();
transformerSelector.setTransformerConfig(transformerConfig);
transformerSelector.setContentTransformerRegistry(dummyRegistry);
dummyRegistry = new ContentTransformerRegistry(transformerSelector);
transformerSelector.setContentTransformerRegistry(dummyRegistry);
// create some dummy transformers for reliability tests
new DummyTransformer(mimetypeService, transformerDebug, dummyRegistry, A, B, 10L);
new DummyTransformer(mimetypeService, transformerDebug, dummyRegistry, A, B, 10L);
new DummyTransformer(mimetypeService, transformerDebug, dummyRegistry, A, C, 10L);
new DummyTransformer(mimetypeService, transformerDebug, dummyRegistry, A, C, 10L);
new DummyTransformer(mimetypeService, transformerDebug, dummyRegistry, B, C, 10L);
new DummyTransformer(mimetypeService, "transformer.testAB10a", transformerDebug, transformerConfig, dummyRegistry, A, B, 10L);
new DummyTransformer(mimetypeService, "transformer.testAB10b", transformerDebug, transformerConfig, dummyRegistry, A, B, 10L);
new DummyTransformer(mimetypeService, "transformer.testAC10a", transformerDebug, transformerConfig, dummyRegistry, A, C, 10L);
new DummyTransformer(mimetypeService, "transformer.testAC10b", transformerDebug, transformerConfig, dummyRegistry, A, C, 10L);
new DummyTransformer(mimetypeService, "transformer.testBC10", transformerDebug, transformerConfig, dummyRegistry, B, C, 10L);
// create some dummy transformers for speed tests
new DummyTransformer(mimetypeService, transformerDebug, dummyRegistry, A, D, 20L);
new DummyTransformer(mimetypeService, transformerDebug, dummyRegistry, A, D, 30L);
new DummyTransformer(mimetypeService, transformerDebug, dummyRegistry, A, D, 10L); // the fast one
new DummyTransformer(mimetypeService, transformerDebug, dummyRegistry, A, D, 25L);
new DummyTransformer(mimetypeService, transformerDebug, dummyRegistry, A, D, 25L);
ad20 = new DummyTransformer(mimetypeService, "transformer.testAD20", transformerDebug, transformerConfig, dummyRegistry, A, D, 20L);
ad30 = new DummyTransformer(mimetypeService, "transformer.testAD30", transformerDebug, transformerConfig, dummyRegistry, A, D, 30L);
ad10 = new DummyTransformer(mimetypeService, "transformer.testAD10", transformerDebug, transformerConfig, dummyRegistry, A, D, 10L); // the fast one
ad25a = new DummyTransformer(mimetypeService, "transformer.testAD25a", transformerDebug, transformerConfig, dummyRegistry, A, D, 25L);
ad25b = new DummyTransformer(mimetypeService, "transformer.testAD25b", transformerDebug, transformerConfig, dummyRegistry, A, D, 25L);
}
/**
@@ -131,20 +141,45 @@ public class ContentTransformerRegistryTest extends AbstractContentTransformerTe
*/
public void testPerformanceRetrieval() throws Exception
{
// Until the threshold (3) is reached by each transformer with the same priority it will
// be tried that many times in the order defined. 20, 30, 10, 25, 25
for (int i=1; i<=3; i++)
{
long expectedTime = i == 1 ? 0L : 20L;
ContentTransformer transformer1 = dummyRegistry.getTransformer(A, -1, D, OPTIONS);
assertEquals(i+" incorrect transformation time", expectedTime, transformer1.getTransformationTime(A, D));
ad20.transformInternal(null, null, null);
}
for (int i=1; i<=3; i++)
{
long expectedTime = i == 1 ? 0L : 30L;
ContentTransformer transformer1 = dummyRegistry.getTransformer(A, -1, D, OPTIONS);
assertEquals(i+" incorrect transformation time", expectedTime, transformer1.getTransformationTime(A, D));
ad30.transformInternal(null, null, null);
}
for (int i=1; i<=3; i++)
{
ad10.transformInternal(null, null, null);
ad25a.transformInternal(null, null, null);
ad25b.transformInternal(null, null, null);
}
// Now the average times are set up, it should find the fastest one
// A -> D expect 1.0, 10ms
ContentTransformer transformer1 = dummyRegistry.getTransformer(A, -1, D, OPTIONS);
assertTrue("Incorrect reliability", transformer1.isTransformable(A, -1, D, OPTIONS));
assertFalse("Incorrect reliability", transformer1.isTransformable(D, -1, A, OPTIONS));
assertEquals("Incorrect transformation time", 10L, transformer1.getTransformationTime());
assertEquals("Incorrect transformation time", 10L, transformer1.getTransformationTime(A, D));
// A -> D has 10, 20, 25, 25, 30
List<ContentTransformer> activeTransformers = dummyRegistry.getActiveTransformers(A, -1, D, OPTIONS);
assertEquals("Not all found", 5, activeTransformers.size());
assertEquals("Incorrect order", 10L, activeTransformers.get(0).getTransformationTime());
assertEquals("Incorrect order", 20L, activeTransformers.get(1).getTransformationTime());
assertEquals("Incorrect order", 25L, activeTransformers.get(2).getTransformationTime());
assertEquals("Incorrect order", 25L, activeTransformers.get(3).getTransformationTime());
assertEquals("Incorrect order", 30L, activeTransformers.get(4).getTransformationTime());
assertEquals("Incorrect order", 10L, activeTransformers.get(0).getTransformationTime(A, D));
assertEquals("Incorrect order", 20L, activeTransformers.get(1).getTransformationTime(A, D));
assertEquals("Incorrect order", 25L, activeTransformers.get(2).getTransformationTime(A, D));
assertEquals("Incorrect order", 25L, activeTransformers.get(3).getTransformationTime(A, D));
assertEquals("Incorrect order", 30L, activeTransformers.get(4).getTransformationTime(A, D));
// Disable two of them, and re-test
((DummyTransformer)activeTransformers.get(2)).disable();
@@ -152,9 +187,9 @@ public class ContentTransformerRegistryTest extends AbstractContentTransformerTe
activeTransformers = dummyRegistry.getActiveTransformers(A, -1, D, OPTIONS);
assertEquals("Not all found", 3, activeTransformers.size());
assertEquals("Incorrect order", 10L, activeTransformers.get(0).getTransformationTime());
assertEquals("Incorrect order", 20L, activeTransformers.get(1).getTransformationTime());
assertEquals("Incorrect order", 25L, activeTransformers.get(2).getTransformationTime());
assertEquals("Incorrect order", 10L, activeTransformers.get(0).getTransformationTime(A, D));
assertEquals("Incorrect order", 20L, activeTransformers.get(1).getTransformationTime(A, D));
assertEquals("Incorrect order", 25L, activeTransformers.get(2).getTransformationTime(A, D));
}
public void testScoredRetrieval() throws Exception
@@ -180,9 +215,9 @@ public class ContentTransformerRegistryTest extends AbstractContentTransformerTe
{
AbstractContentTransformer2 dummyTransformer = new DummyTransformer(
mimetypeService,
transformerDebug,
dummyRegistry, MimetypeMap.MIMETYPE_FLASH,
MimetypeMap.MIMETYPE_EXCEL, 12345);
"transformer.testExplicit",
transformerDebug, transformerConfig,
dummyRegistry, MimetypeMap.MIMETYPE_FLASH, MimetypeMap.MIMETYPE_EXCEL, 12345);
// set an explicit transformation
ExplictTransformationDetails key =
new ExplictTransformationDetails(
@@ -212,25 +247,23 @@ public class ContentTransformerRegistryTest extends AbstractContentTransformerTe
public DummyTransformer(
MimetypeService mimetypeService,
TransformerDebug transformerDebug,
ContentTransformerRegistry registry, String sourceMimetype,
String targetMimetype, long transformationTime)
String name,
TransformerDebug transformerDebug, TransformerConfig transformerConfig,
ContentTransformerRegistry registry, String sourceMimetype, String targetMimetype, long transformationTime)
{
super.setMimetypeService(mimetypeService);
super.setTransformerDebug(transformerDebug);
super.setTransformerConfig(transformerConfig);
super.setRegistry(registry);
this.sourceMimetype = sourceMimetype;
this.targetMimetype = targetMimetype;
this.transformationTime = transformationTime;
setBeanName(name+'.'+System.currentTimeMillis()%100000);
// register
register();
}
protected void enable()
{
disable = false;
}
protected void disable()
{
disable = true;
@@ -262,16 +295,16 @@ public class ContentTransformerRegistryTest extends AbstractContentTransformerTe
TransformationOptions options) throws Exception
{
// just update the transformation time
super.recordTime(transformationTime);
super.recordTime(sourceMimetype, targetMimetype, transformationTime);
}
/**
* @return Returns the fixed dummy average transformation time
*/
public synchronized long getTransformationTime()
{
return transformationTime;
}
// /**
// * @return Returns the fixed dummy average transformation time
// */
// public synchronized long getTransformationTime(String sourceMimetype, String targetMimetype)
// {
// return transformationTime;
// }
}
@Override

View File

@@ -0,0 +1,125 @@
/*
* Copyright (C) 2005-2012 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.content.transform;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Provides simple get and put access to a Map like object with a double key that allows
* either or both keys to be a wild card that matches any value. May not contain null
* keys or values.<p>
*
* Originally created for mapping source and target mimetypes to transformer configuration data.<p>
*
* For example:
* <pre>
* DoubleMap<String, String, String> foodLikes = new DoubleMap<String, String, String>("*", "*");
*
* foodLikes.put("cat", "mouse", "likes");
*
* foodLikes.get("cat", "mouse"); // returns "likes"
* foodLikes.get("cat", "meat"); // returns null
*
* foodLikes.put("dog", "meat", "likes");
* foodLikes.put("dog", "stick", "unsure");
* foodLikes.put("child", "olive", "dislikes");
* foodLikes.put("bird", "*", "worms only");
* foodLikes.put("*", "meat", "unknown");
* foodLikes.put("*", "*", "no idea at all");
*
* foodLikes.get("cat", "mouse"); // returns "likes"
* foodLikes.get("cat", "meat"); // returns "unknown"
* foodLikes.get("cat", "tea"); // returns "unknown"
* foodLikes.get("*", "mouse"); // returns "no idea at all"
* foodLikes.get("dog", "*"); // returns "no idea at all"
* foodLikes.get("bird","*"); // returns "worms only"
* foodLikes.get("bird","tea"); // returns "worms only"
* </pre>
*
* @author Alan Davis
*/
public class DoubleMap<K1, K2, V>
{
private final Map<K1, Map<K2, V>> mapMap = new ConcurrentHashMap<K1, Map<K2, V>>();
private final K1 anyKey1;
private final K2 anyKey2;
public DoubleMap(K1 anyKey1, K2 anyKey2)
{
this.anyKey1 = anyKey1;
this.anyKey2 = anyKey2;
}
/**
* Returns a value for the given keys.
*/
public V get(K1 key1, K2 key2)
{
V value = null;
Map<K2, V> map = mapMap.get(key1);
boolean anySource = false;
if (map == null)
{
map = mapMap.get(anyKey1);
anySource = true;
}
if (map != null)
{
value = map.get(key2);
if (value == null)
{
value = map.get(anyKey2);
// Handle the case were there is no match using an non wildcarded key1 and
// key2 but is a match if key1 is wildcarded.
if (value == null && !anySource)
{
map = mapMap.get(anyKey1);
if (map != null)
{
value = map.get(key2);
if (value == null)
{
value = map.get(anyKey2);
}
}
}
}
}
return value;
}
/**
* Adds a value for the given keys.
*/
public void put(K1 key1, K2 key2, V t)
{
Map<K2, V> map = mapMap.get(key1);
if (map == null)
{
map = new ConcurrentHashMap<K2, V>();
mapMap.put(key1, map);
}
map.put(key2, t);
}
}

View File

@@ -50,6 +50,7 @@ public class EMLTransformerTest extends AbstractContentTransformerTest
transformer = new EMLTransformer();
transformer.setMimetypeService(mimetypeService);
transformer.setTransformerDebug(transformerDebug);
transformer.setTransformerConfig(transformerConfig);
}
@Override

View File

@@ -57,6 +57,7 @@ public class FailoverContentTransformerTest extends AbstractContentTransformerTe
transformer = (FailoverContentTransformer) failoverAppContext.getBean("transformer.failover.Test-FailThenSucceed");
transformer.setMimetypeService(mimetypeService);
transformer.setTransformerDebug(transformerDebug);
transformer.setTransformerConfig(transformerConfig);
}
/**

View File

@@ -42,6 +42,7 @@ public class HtmlParserContentTransformerTest extends AbstractContentTransformer
transformer = new HtmlParserContentTransformer();
transformer.setMimetypeService(mimetypeService);
transformer.setTransformerDebug(transformerDebug);
transformer.setTransformerConfig(transformerConfig);
}
protected ContentTransformer getTransformer(String sourceMimetype, String targetMimetype)

View File

@@ -45,6 +45,7 @@ public class MailContentTransformerTest extends AbstractContentTransformerTest
transformer = new MailContentTransformer();
transformer.setMimetypeService(mimetypeService);
transformer.setTransformerDebug(transformerDebug);
transformer.setTransformerConfig(transformerConfig);
}
/**

View File

@@ -70,6 +70,7 @@ public class MediaWikiContentTransformerTest extends AbstractContentTransformerT
transformer = new MediaWikiContentTransformer();
transformer.setMimetypeService(mimetypeService);
transformer.setTransformerDebug(transformerDebug);
transformer.setTransformerConfig(transformerConfig);
}
protected ContentTransformer getTransformer(String sourceMimetype, String targetMimetype)

View File

@@ -50,6 +50,7 @@ public class OpenOfficeContentTransformerTest extends AbstractContentTransformer
transformer = new ProxyContentTransformer();
transformer.setMimetypeService(mimetypeService);
transformer.setTransformerDebug(transformerDebug);
transformer.setTransformerConfig(transformerConfig);
transformer.setWorker(this.worker);
}

View File

@@ -38,6 +38,7 @@ public class PdfBoxContentTransformerTest extends AbstractContentTransformerTest
transformer = new PdfBoxContentTransformer();
transformer.setMimetypeService(mimetypeService);
transformer.setTransformerDebug(transformerDebug);
transformer.setTransformerConfig(transformerConfig);
}
/**

View File

@@ -38,6 +38,7 @@ public class PoiContentTransformerTest extends AbstractContentTransformerTest
transformer = new PoiContentTransformer();
transformer.setMimetypeService(mimetypeService);
transformer.setTransformerDebug(transformerDebug);
transformer.setTransformerConfig(transformerConfig);
}
/**

View File

@@ -46,6 +46,7 @@ public class PoiHssfContentTransformerTest extends TikaPoweredContentTransformer
transformer = new PoiHssfContentTransformer();
transformer.setMimetypeService(mimetypeService);
transformer.setTransformerDebug(transformerDebug);
transformer.setTransformerConfig(transformerConfig);
}
@Override

View File

@@ -38,6 +38,7 @@ public class PoiOOXMLContentTransformerTest extends AbstractContentTransformerTe
transformer = new PoiOOXMLContentTransformer();
transformer.setMimetypeService(mimetypeService);
transformer.setTransformerDebug(transformerDebug);
transformer.setTransformerConfig(transformerConfig);
}
/**

View File

@@ -74,10 +74,12 @@ public class RuntimeExecutableContentTransformerTest extends BaseAlfrescoTestCas
worker.afterPropertiesSet();
TransformerDebug transformerDebug = (TransformerDebug) ctx.getBean("transformerDebug");
TransformerConfig transformerConfig = (TransformerConfig) ctx.getBean("transformerConfig");
ProxyContentTransformer transformer = new ProxyContentTransformer();
transformer.setMimetypeService(serviceRegistry.getMimetypeService());
transformer.setTransformerDebug(transformerDebug);
transformer.setTransformerConfig(transformerConfig);
transformer.setWorker(worker);
this.transformer = transformer;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
* Copyright (C) 2005-2013 Alfresco Software Limited.
*
* This file is part of Alfresco
*
@@ -169,19 +169,7 @@ public class RuntimeExecutableContentTransformerWorker extends ContentTransforme
*/
public boolean isTransformable(String sourceMimetype, String targetMimetype, TransformationOptions options)
{
if (!available)
{
return false;
}
if (isExplicitTransformation(sourceMimetype, targetMimetype, options))
{
return true;
}
else
{
return false;
}
return available;
}
/**

View File

@@ -68,6 +68,7 @@ public class StringExtractingContentTransformerTest extends AbstractContentTrans
transformer = new StringExtractingContentTransformer();
transformer.setMimetypeService(mimetypeService);
transformer.setTransformerDebug(transformerDebug);
transformer.setTransformerConfig(transformerConfig);
targetWriter = new FileContentWriter(getTempFile());
targetWriter.setMimetype("text/plain");
targetWriter.setEncoding("UTF-8");

View File

@@ -46,6 +46,7 @@ public class TextMiningContentTransformerTest extends AbstractContentTransformer
transformer = new TextMiningContentTransformer();
transformer.setMimetypeService(mimetypeService);
transformer.setTransformerDebug(transformerDebug);
transformer.setTransformerConfig(transformerConfig);
}
/**

View File

@@ -50,9 +50,11 @@ public class TextToPdfContentTransformerTest extends AbstractContentTransformerT
transformer = new TextToPdfContentTransformer();
transformer.setMimetypeService(mimetypeService);
transformer.setTransformerDebug(transformerDebug);
transformer.setTransformerConfig(transformerConfig);
transformer.setStandardFont("Times-Roman");
transformer.setFontSize(20);
transformer.setPageLimit(-1);
transformer.register();
}
/**
@@ -120,7 +122,9 @@ public class TextToPdfContentTransformerTest extends AbstractContentTransformerT
private void transformTextAndCheckPageLength(int pageLimit) throws IOException
{
transformer.setBeanName("transformer.test"+System.currentTimeMillis()%100000);
transformer.setPageLimit(pageLimit);
transformer.register();
int pageLength = 32;
int lines = (pageLength+10) * ((pageLimit > 0) ? pageLimit : 1);

View File

@@ -43,6 +43,7 @@ public class TikaAutoContentTransformerTest extends TikaPoweredContentTransforme
transformer = new TikaAutoContentTransformer( config );
transformer.setMimetypeService(mimetypeService);
transformer.setTransformerDebug(transformerDebug);
transformer.setTransformerConfig(transformerConfig);
}
/**

View File

@@ -0,0 +1,204 @@
/*
* Copyright (C) 2005-2012 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.content.transform;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import org.alfresco.service.cmr.repository.TransformationOptionLimits;
import org.alfresco.service.cmr.repository.TransformationOptions;
/**
* Provides access to transformer configuration and current performance data.
*
* @author Alan Davis
*/
public interface TransformerConfig
{
/**
* Wild card mimetype and mimetype extension.
*/
public static final String ANY = "*";
/**
* Prefix before the transformer name of all property names that contain transformer
* information
*/
static final String CONTENT = "content.";
/**
* Prefix for all transformer names
*/
static final String TRANSFORMER = "transformer.";
/**
* The combined content and transformer name prefix of for all property names that contain
* transformer information
*/
static final String PREFIX = CONTENT+TRANSFORMER;
/**
* The 'transformer name' for system wide defaults for all transformers
*/
static final String DEFAULT_TRANSFORMER = TRANSFORMER+"default";
/**
* Name given to the 'SUMMARY' dummy (does not exist) transformer that gathers data
* from all root level transformations
*/
static final String SUMMARY_TRANSFORMER_NAME = "SUMMARY";
/**
* The separator between the transformer name and two mimetype extensions in a property name.
*/
static final String MIMETYPES_SEPARATOR = ".mimetypes.";
/**
* The suffix to property names for supported and unsupported combinations.
*/
static final String SUPPORTED = ".supported";
/**
* The suffix to property names for the priority.
*/
static final String PRIORITY = ".priority";
/**
* The suffix to property names for the threshold count.
*/
static final String THRESHOLD_COUNT = ".thresholdCount";
/**
* The suffix to property names for the error time.
*/
static final String ERROR_TIME = ".errorTime";
/**
* The suffix to property names for the the average time. Only used in the initial setup of
* TransformerData. This is a historical property used by the 'Transformation Server' to set
* an effective priority.
*/
static final String INITIAL_TIME = ".time";
/**
* The suffix to property names for the the average count. Only used in the initial setup of
* TransformerData. This is a historical property used by the 'Transformation Server' to set
* an effective priority.
*/
static final String INITIAL_COUNT = ".count";
/**
* To support the historical concept of EXPLICIT transformers, all such transformers
* are given a {@link PRIORITY_EXPLICIT} (5). By default transformers have a default of 10.
* A value of 5 allows better transformers to be added later.
*/
public int PRIORITY_EXPLICIT = 5;
/**
* Suffixes to property names used to define transformation limits
*/
static final Collection<String> LIMIT_SUFFIXES = Arrays.asList(new String [] {
'.'+TransformationOptionLimits.OPT_MAX_SOURCE_SIZE_K_BYTES,
'.'+TransformationOptionLimits.OPT_TIMEOUT_MS,
'.'+TransformationOptionLimits.OPT_MAX_PAGES,
'.'+TransformationOptionLimits.OPT_READ_LIMIT_K_BYTES,
'.'+TransformationOptionLimits.OPT_READ_LIMIT_TIME_MS,
'.'+TransformationOptionLimits.OPT_PAGE_LIMIT
});
/**
* No suffixes to property names used to define transformer settings.
*/
public static final Collection<String> NO_SUFFIXES = Collections.singletonList("");
/**
* Returns a transformer property value.
* @param name of the property.
* @return a transformer property or {@code null} if not set.
*/
String getProperty(String name);
/**
* Sets a transformer property value. This will be stored in the database but on an MBean
* reset would be cleared.
*
* @param propertyNameAndValue
*/
void setProperty(String propertyNameAndValue);
/**
* Returns and creates if needed the {@link TransformerStatistics} object for the combination of
* transformer, sourceMimetype and targetMimetype. When transformer is null this is the
* system wide summary object for a combination of sourceMimetype and targetMimetype.
* When both sourceMimetype and targetMimetype are null this is the transformer's summary
* object. When all three parameters are null this is the system wide summary for all
* transformers.
* @param transformer the transformer for which data is being recorded.
* @param sourceMimetype the source mimetype.
* @param targetMimetype the source mimetype.
* @return the requested {@link TransformerStatistics}.
*/
public TransformerStatistics getStatistics(ContentTransformer transformer, String sourceMimetype, String targetMimetype);
/**
* Returns the limits defined for the combination of transformer, sourceMimetype and targetMimetype.
* When the transformer is null, this is a default value. When both sourceMimetype and targetMimetype
* are null this is a default for the specified transformer.
* @param transformer
* @param sourceMimetype
* @param targetMimetype
* @return the combined (takes into account defaults from higher levels) limits for the combination.
*/
public TransformationOptionLimits getLimits(ContentTransformer transformer, String sourceMimetype, String targetMimetype);
/**
* Returns true if the supplied mimetype transformation pair is allowed by the list of supported
* and unsupported transformations.
* @param transformer
* @param sourceMimetype
* @param targetMimetype
* @param options not currently used
*/
public boolean isSupportedTransformation(ContentTransformer transformer, String sourceMimetype,
String targetMimetype, TransformationOptions options);
/**
* Returns the priority of the specified transformer for the the combination of source and target mimetype.
* @param transformer
* @param sourceMimetype
* @param targetMimetype
* @return the priority. To support the historical concept of EXPLICIT transformers, all such transformers
* are given a {@link PRIORITY_EXPLICIT} (5). By default transformers have a default of 10.
*/
public int getPriority(ContentTransformer contentTransformerHelper,
String sourceMimetype, String targetMimetype);
/**
* Returns the threshold of the transformer. It is only after this number of transformation attempts
* that the average time is used.
* @param transformer
* @param sourceMimetype
* @param targetMimetype
* @return the threshold.
*/
public int getThresholdCount(ContentTransformer contentTransformerHelper, String sourceMimetype,
String targetMimetype);
}

View File

@@ -0,0 +1,213 @@
/*
* Copyright (C) 2005-2012 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.content.transform;
import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.TransformationOptionLimits;
import org.alfresco.service.cmr.repository.TransformationOptions;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.extensions.surf.util.AbstractLifecycleBean;
/**
* Provides access to transformer configuration and current performance data.
*
* @author Alan Davis
*/
public class TransformerConfigImpl extends AbstractLifecycleBean implements TransformerConfig
{
/** The logger. */
private static Log logger = LogFactory.getLog(TransformerConfigImpl.class);
private MimetypeService mimetypeService;
// Holds statistics about each transformer, sourceMimeType and targetMimetype combination.
// A null transformer is the system wide value. Null sourceMimeType and targetMimetype values are
// transformer wide summaries.
private TransformerConfigStatistics statistics;
// Transformer limits.
private TransformerConfigLimits limits;
// Supported and unsupported transformations.
private TransformerConfigSupported supported;
// Priorities
private TransformerConfigProperty priorities;
// Threshold counts - Initially there will only be the system wide value, but
// having this structure provides flexibility.
private TransformerConfigProperty thresholdCounts;
// Times to be recorded if there is an error - Initially there will only be the system wide value, but
// having this structure provides flexibility.
private TransformerConfigProperty errorTimes;
// For backward compatibility where priority could not be set, with AMPs that need to have their
// transformer used rather than an inbuilt one. Achieved by making the inbuilt transformers look
// poor. Generally contains no entries, other than the system wide 0 values.
private TransformerConfigProperty initialAverageTimes;
private TransformerConfigProperty initialCounts;
// Needed to read properties.
private ChildApplicationContextFactory subsystemFactory;
/**
* Sets of the mimetype service.
*
* @param mimetypeService
*/
public void setMimetypeService(MimetypeService mimetypeService)
{
this.mimetypeService = mimetypeService;
}
/**
* Called by spring after bean is initialised.
*/
public void initialise()
{
statistics= new TransformerConfigStatistics(this, mimetypeService);
limits = new TransformerConfigLimits(getSubsystem(), mimetypeService);
supported = new TransformerConfigSupported(getSubsystem(), mimetypeService);
priorities = new TransformerConfigProperty(getSubsystem(), mimetypeService, PRIORITY);
thresholdCounts = new TransformerConfigProperty(getSubsystem(), mimetypeService, THRESHOLD_COUNT);
errorTimes = new TransformerConfigProperty(getSubsystem(), mimetypeService, ERROR_TIME);
initialAverageTimes = new TransformerConfigProperty(getSubsystem(), mimetypeService, INITIAL_TIME);
initialCounts = new TransformerConfigProperty(getSubsystem(), mimetypeService, INITIAL_COUNT);
}
/**
* Returns the 'transformers' subsystem which among other things holds transformer properties.
*/
private synchronized ChildApplicationContextFactory getSubsystem()
{
if (subsystemFactory == null)
{
subsystemFactory = getApplicationContext().getBean("Transformers", ChildApplicationContextFactory.class);
}
return subsystemFactory;
}
@Override
protected void onBootstrap(ApplicationEvent event)
{
}
@Override
protected void onShutdown(ApplicationEvent event)
{
}
/**
* {@inheritDoc}
*/
@Override
public String getProperty(String name)
{
return getSubsystem().getProperty(name);
}
/**
* {@inheritDoc}
*/
@Override
public void setProperty(String propertyNameAndValue)
{
int i = propertyNameAndValue.indexOf('=');
String name = i != -1 ? propertyNameAndValue.substring(0, i) : propertyNameAndValue;
String value = i != -1 ? propertyNameAndValue.substring(i+1) : "";
getSubsystem().setProperty(name, value);
}
/**
* {@inheritDoc}
*/
@Override
public TransformerStatistics getStatistics(ContentTransformer transformer, String sourceMimetype, String targetMimetype)
{
return statistics.getStatistics(transformer, sourceMimetype, targetMimetype);
}
/**
* {@inheritDoc}
*/
@Override
public TransformationOptionLimits getLimits(ContentTransformer transformer, String sourceMimetype,
String targetMimetype)
{
return limits.getLimits(transformer, sourceMimetype, targetMimetype);
}
/**
* {@inheritDoc}
*/
@Override
public boolean isSupportedTransformation(ContentTransformer transformer, String sourceMimetype,
String targetMimetype, TransformationOptions options)
{
return supported.isSupportedTransformation(transformer, sourceMimetype, targetMimetype, options);
}
/**
* {@inheritDoc}
*/
@Override
public int getPriority(ContentTransformer transformer, String sourceMimetype, String targetMimetype)
{
return priorities.getInt(transformer, sourceMimetype, targetMimetype);
}
/**
* {@inheritDoc}
*/
@Override
public int getThresholdCount(ContentTransformer transformer, String sourceMimetype, String targetMimetype)
{
return thresholdCounts.getInt(transformer, sourceMimetype, targetMimetype);
}
/**
* Gets the time to be recorded if there is an error.
*/
long getErrorTime(ContentTransformer transformer, String sourceMimetype, String targetMimetype)
{
return errorTimes.getLong(transformer, sourceMimetype, targetMimetype);
}
/**
* Gets the initial average time to be set for a transformer. Used historically to set the priority of transformers in AMPs.
* The initial count value may also obtained via {@link #getInitialCount(ContentTransformer, String, String)}.
*/
long getInitialAverageTime(ContentTransformer transformer, String sourceMimetype, String targetMimetype)
{
return initialAverageTimes.getLong(transformer, sourceMimetype, targetMimetype);
}
/**
* Gets the initial transformer count to be set for a transformer. Used historically to set the priority of transformers in AMPs.
* Only called if {@link #getInitialAverageTime(ContentTransformer, String, String)} returns a value larger than 0.
*/
int getInitialCount(ContentTransformer transformer, String sourceMimetype, String targetMimetype)
{
return initialCounts.getInt(transformer, sourceMimetype, targetMimetype);
}
}

View File

@@ -0,0 +1,186 @@
/*
* Copyright (C) 2005-2013 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.content.transform;
import static org.alfresco.repo.content.transform.TransformerConfig.ANY;
import static org.alfresco.repo.content.transform.TransformerConfig.CONTENT;
import static org.alfresco.repo.content.transform.TransformerConfig.DEFAULT_TRANSFORMER;
import static org.alfresco.repo.content.transform.TransformerConfig.LIMIT_SUFFIXES;
import static org.alfresco.repo.content.transform.TransformerConfig.MIMETYPES_SEPARATOR;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.TransformationOptionLimits;
import org.alfresco.util.Triple;
/**
* Provides access to transformer limits defined via properties.
*
* @author Alan Davis
*/
public class TransformerConfigLimits extends TransformerPropertyNameExtractor
{
// Holds configured (entries only exist if configured rather than for all possible combinations)
// limits for for transformer, sourceMimeType and targetMimetype combination.
// A null transformer is the system wide value. SourceMimeType and targetMimetype may be 'ANY'
// values to act as wild cards.
private Map<String, DoubleMap<String, String, TransformationOptionLimits>> limits;
public TransformerConfigLimits(ChildApplicationContextFactory subsystem, MimetypeService mimetypeService)
{
setLimits(subsystem, mimetypeService);
}
/**
* Sets the transformer limits created from system properties.
*/
private void setLimits(ChildApplicationContextFactory subsystem, MimetypeService mimetypeService)
{
limits = new HashMap<String, DoubleMap<String, String, TransformationOptionLimits>>();
// Gets all the transformer, source and target combinations in properties that define limits.
Set<Triple<String, String, String>> transformerNamesAndExt =
getTransformerNamesAndExt(MIMETYPES_SEPARATOR, LIMIT_SUFFIXES, true, subsystem, mimetypeService);
// Add the system wide default just in case it is not included, as we always need this one
transformerNamesAndExt.add(new Triple<String,String,String>(DEFAULT_TRANSFORMER, ANY, ANY));
// Populate the transformer limits
for (Triple<String, String, String> triple: transformerNamesAndExt)
{
String transformerName = triple.getFirst();
String sourceExt = triple.getSecond();
String targetExt = triple.getThird();
String sourceMimetype = ANY.equals(sourceExt) ? ANY : mimetypeService.getMimetype(sourceExt);
String targetMimetype = ANY.equals(targetExt) ? ANY : mimetypeService.getMimetype(targetExt);
TransformationOptionLimits limits = newTransformationOptionLimits(transformerName, sourceExt, targetExt, subsystem);
DoubleMap<String, String, TransformationOptionLimits> mimetypeLimits =
this.limits.get(transformerName);
if (mimetypeLimits == null)
{
mimetypeLimits = new DoubleMap<String, String, TransformationOptionLimits>(ANY, ANY);
this.limits.put(transformerName, mimetypeLimits);
}
mimetypeLimits.put(sourceMimetype, targetMimetype, limits);
}
}
/**
* Returns a TransformationOptionLimits object using property values.
* @param transformerName
* @param sourceExt is null for overall transformer options rather than for a specific mimetype pair
* @param targetExt is null for overall transformer options rather than for a specific mimetype pair
* @return a TransformationOptionLimits object or null if not created
*/
private TransformationOptionLimits newTransformationOptionLimits(String transformerName,
String sourceExt, String targetExt, ChildApplicationContextFactory subsystem)
{
TransformationOptionLimits limits = new TransformationOptionLimits();
// The overall values can be defined in two ways
if (ANY.equals(sourceExt) && ANY.equals(targetExt))
{
setTransformationOptionsFromProperties(limits, transformerName, null, null, subsystem);
}
setTransformationOptionsFromProperties(limits, transformerName, sourceExt, targetExt, subsystem);
return limits;
}
private void setTransformationOptionsFromProperties(TransformationOptionLimits limits,
String transformerName, String sourceExt, String targetExt, ChildApplicationContextFactory subsystem)
{
String propertyNameRoot = CONTENT+transformerName+
(sourceExt == null ? "" : MIMETYPES_SEPARATOR+sourceExt+'.'+targetExt);
int i = 0;
for (String suffix: LIMIT_SUFFIXES)
{
String value = subsystem.getProperty(propertyNameRoot+suffix);
if (value != null)
{
long l = Long.parseLong(value);
switch (i)
{
case 0:
limits.setMaxSourceSizeKBytes(l);
break;
case 1:
limits.setTimeoutMs(l);
break;
case 2:
limits.setMaxPages((int)l);
break;
case 3:
limits.setReadLimitKBytes(l);
break;
case 4:
limits.setReadLimitTimeMs(l);
break;
case 5:
limits.setPageLimit((int)l);
break;
}
}
i++;
}
}
/**
* See {@link TransformerConfig#getLimits(ContentTransformer, String, String)}.
*/
public TransformationOptionLimits getLimits(ContentTransformer transformer, String sourceMimetype,
String targetMimetype)
{
if (sourceMimetype == null)
{
sourceMimetype = ANY;
}
if (targetMimetype == null)
{
targetMimetype = ANY;
}
String name = (transformer == null) ? DEFAULT_TRANSFORMER : transformer.getName();
DoubleMap<String, String, TransformationOptionLimits> transformerLimits = limits.get(name);
TransformationOptionLimits limits = (transformerLimits == null) ? null : transformerLimits.get(sourceMimetype, targetMimetype);
// Individual transformer limits might not exist.
TransformationOptionLimits transformerWideLimits = (transformerLimits == null) ? null : transformerLimits.get(ANY, ANY);
limits = (limits == null) ? transformerWideLimits : transformerWideLimits == null ? limits : transformerWideLimits.combine(limits);
// If a non recursive call
if (transformer != null)
{
// System wide 'default' limits should exist.
TransformationOptionLimits systemWideLimits = getLimits(null, sourceMimetype, targetMimetype);
limits = (limits == null) ? systemWideLimits : systemWideLimits.combine(limits);
}
return limits;
}
}

View File

@@ -0,0 +1,155 @@
/*
* Copyright (C) 2005-2013 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.content.transform;
import static org.alfresco.repo.content.transform.TransformerConfig.ANY;
import static org.alfresco.repo.content.transform.TransformerConfig.CONTENT;
import static org.alfresco.repo.content.transform.TransformerConfig.DEFAULT_TRANSFORMER;
import static org.alfresco.repo.content.transform.TransformerConfig.MIMETYPES_SEPARATOR;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.util.Triple;
/**
* Provides access to single transformer configuration property depending on the
* transformer and source and target mimetypes, falling back to defaults.
*
* @author Alan Davis
*/
public class TransformerConfigProperty extends TransformerPropertyNameExtractor
{
private Map<String, DoubleMap<String, String, String>> values;
public TransformerConfigProperty(ChildApplicationContextFactory subsystem,
MimetypeService mimetypeService, String propertySuffix)
{
setValues(subsystem, mimetypeService, propertySuffix);
}
/**
* Sets the transformer values created from system properties.
*/
private void setValues(ChildApplicationContextFactory subsystem, MimetypeService mimetypeService, String propertySuffix)
{
values = new HashMap<String, DoubleMap<String, String, String>>();
// Gets all the transformer, source and target combinations in properties that define
// this value.
Set<Triple<String, String, String>> transformerNamesAndMimetypes =
getTransformerNamesAndExt(MIMETYPES_SEPARATOR, Collections.singletonList(propertySuffix), true, subsystem, mimetypeService);
// Add the system wide default just in case it is not included, as we always need this one
transformerNamesAndMimetypes.add(new Triple<String,String,String>(DEFAULT_TRANSFORMER, ANY, ANY));
// Populate the transformer values
for (Triple<String, String, String> triple: transformerNamesAndMimetypes)
{
String transformerName = triple.getFirst();
String sourceExt = triple.getSecond();
String targetExt = triple.getThird();
String sourceMimetype = ANY.equals(sourceExt) ? ANY : mimetypeService.getMimetype(sourceExt);
String targetMimetype = ANY.equals(targetExt) ? ANY : mimetypeService.getMimetype(targetExt);
String value = newTransformerValue(transformerName, sourceExt, targetExt, subsystem, propertySuffix);
DoubleMap<String, String, String> mimetypeLimits = this.values.get(transformerName);
if (mimetypeLimits == null)
{
mimetypeLimits = new DoubleMap<String, String, String>(ANY, ANY);
this.values.put(transformerName, mimetypeLimits);
}
mimetypeLimits.put(sourceMimetype, targetMimetype, value);
}
}
/**
* Returns a String object using property values.
* @param transformerName
* @param sourceExt is null for overall transformer options rather than for a specific mimetype pair
* @param targetExt is null for overall transformer options rather than for a specific mimetype pair
* @return a String object or null if not created
*/
private String newTransformerValue(String transformerName, String sourceExt, String targetExt,
ChildApplicationContextFactory subsystem, String propertySuffix)
{
String value = getValueFromProperties(transformerName, sourceExt, targetExt, subsystem, propertySuffix);
// The overall values can be defined in another way
if (value == null && ANY.equals(sourceExt) && ANY.equals(targetExt))
{
value = getValueFromProperties(transformerName, null, null, subsystem, propertySuffix);
}
return value;
}
private String getValueFromProperties(String transformerName, String sourceExt, String targetExt,
ChildApplicationContextFactory subsystem, String propertySuffix)
{
String propertyName = CONTENT+transformerName+
(sourceExt == null ? "" : MIMETYPES_SEPARATOR+sourceExt+'.'+targetExt)+
propertySuffix;
String value = subsystem.getProperty(propertyName);
return value;
}
private String getString(ContentTransformer transformer, String sourceMimetype,
String targetMimetype)
{
if (sourceMimetype == null)
{
sourceMimetype = ANY;
}
if (targetMimetype == null)
{
targetMimetype = ANY;
}
String name = (transformer == null) ? DEFAULT_TRANSFORMER : transformer.getName();
DoubleMap<String, String, String> mimetypeLimits = values.get(name);
String value = (mimetypeLimits == null) ? null : mimetypeLimits.get(sourceMimetype, targetMimetype);
if (value == null && transformer != null)
{
// System wide 'default' limits should exist, but individual transformer values might not.
value = getString(null, sourceMimetype, targetMimetype);
}
return value;
}
public long getLong(ContentTransformer transformer, String sourceMimetype, String targetMimetype)
{
return Long.parseLong(getString(transformer, sourceMimetype, targetMimetype));
}
public int getInt(ContentTransformer transformer, String sourceMimetype, String targetMimetype)
{
return Integer.parseInt(getString(transformer, sourceMimetype, targetMimetype));
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (C) 2005-2013 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.content.transform;
import static org.alfresco.repo.content.transform.TransformerConfig.ANY;
import static org.alfresco.repo.content.transform.TransformerConfig.SUMMARY_TRANSFORMER_NAME;
import java.util.HashMap;
import java.util.Map;
import org.alfresco.service.cmr.repository.MimetypeService;
/**
* Provides a place to store and access statistics about transformers, source and target mimetypes combinations.
* It also provides summaries for transformers as a whole and the system as a whole.
*
* @author Alan Davis
*/
public class TransformerConfigStatistics
{
private TransformerConfigImpl transformerConfigImpl;
private MimetypeService mimetypeService;
// Holds statistics about each transformer, sourceMimeType and targetMimetype combination.
// A null transformer is the system wide value. Null sourceMimeType and targetMimetype values are
// transformer wide summaries.
private Map<String, DoubleMap<String, String, TransformerStatistics>> statistics =
new HashMap<String, DoubleMap<String, String, TransformerStatistics>>();
public TransformerConfigStatistics(TransformerConfigImpl transformerConfigImpl,
MimetypeService mimetypeService)
{
this.transformerConfigImpl = transformerConfigImpl;
this.mimetypeService = mimetypeService;
}
public TransformerStatistics getStatistics(ContentTransformer transformer, String sourceMimetype, String targetMimetype)
{
if (sourceMimetype == null)
{
sourceMimetype = ANY;
}
if (targetMimetype == null)
{
targetMimetype = ANY;
}
TransformerStatistics transformerStatistics;
String name = (transformer == null) ? SUMMARY_TRANSFORMER_NAME : transformer.getName();
DoubleMap<String, String, TransformerStatistics> mimetypeStatistics = statistics.get(name);
if (mimetypeStatistics == null)
{
// Create the summary for the transformer as a whole
mimetypeStatistics = new DoubleMap<String, String, TransformerStatistics>(ANY, ANY);
statistics.put(name, mimetypeStatistics);
transformerStatistics = newTransformerStatistics(transformer, ANY, ANY, null);
mimetypeStatistics.put(ANY, ANY, transformerStatistics);
}
if (ANY.equals(sourceMimetype) && ANY.equals(targetMimetype))
{
transformerStatistics = mimetypeStatistics.get(ANY, ANY);
}
else
{
// Not looking for the summary, so will have to create if not found or the summary is returned
transformerStatistics = mimetypeStatistics.get(sourceMimetype, targetMimetype);
if (transformerStatistics == null || transformerStatistics.isSummary())
{
// Create individual mimetype to mimetype transformation by this transformer
transformerStatistics = newTransformerStatistics(transformer, sourceMimetype, targetMimetype, mimetypeStatistics.get(ANY, ANY));
mimetypeStatistics.put(sourceMimetype, targetMimetype, transformerStatistics);
}
}
return transformerStatistics;
}
private TransformerStatistics newTransformerStatistics(ContentTransformer transformer,
String sourceMimetype, String targetMimetype, TransformerStatistics parent)
{
long initialAverageTime = transformerConfigImpl.getInitialAverageTime(transformer, sourceMimetype, targetMimetype);
long initialCount = initialAverageTime <= 0
? 0
: transformerConfigImpl.getInitialCount(transformer, sourceMimetype, targetMimetype);
long errorTime = transformerConfigImpl.getErrorTime(transformer, sourceMimetype, targetMimetype);
TransformerStatistics transformerStatistics = new TransformerStatisticsImpl(mimetypeService, sourceMimetype, targetMimetype,
transformer, parent, errorTime, initialAverageTime, initialCount);
return transformerStatistics;
}
}

View File

@@ -0,0 +1,169 @@
/*
* Copyright (C) 2005-2013 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.content.transform;
import static org.alfresco.repo.content.transform.TransformerConfig.ANY;
import static org.alfresco.repo.content.transform.TransformerConfig.CONTENT;
import static org.alfresco.repo.content.transform.TransformerConfig.MIMETYPES_SEPARATOR;
import static org.alfresco.repo.content.transform.TransformerConfig.SUPPORTED;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.TransformationOptions;
import org.alfresco.util.Triple;
/**
* Provides access to the lists of supported and unsupported mimetype transformations
* defined via properties for all transformers.
*
* @author Alan Davis
*/
public class TransformerConfigSupported extends TransformerPropertyNameExtractor
{
// Holds configured (entries only exist if configured rather than for all possible combinations)
// of supported and unsupported mimetypes transformations for a transformer.
// SourceMimetype and targetMimetype may be 'ANY' values to act as wild cards.
private Map<String, SupportedAndUnsupportedTransformations> supported;
public TransformerConfigSupported(ChildApplicationContextFactory subsystem, MimetypeService mimetypeService)
{
setSupported(subsystem, mimetypeService);
}
/**
* Sets the supported/unsupported mimetype transformations created from system properties.
*/
private void setSupported(ChildApplicationContextFactory subsystem, MimetypeService mimetypeService)
{
supported = new HashMap<String, SupportedAndUnsupportedTransformations>();
// Gets all the supported and unsupported transformer, source and target combinations
Set<Triple<String, String, String>> transformerNamesAndMimetypes =
getTransformerNamesAndExt(MIMETYPES_SEPARATOR, Collections.singletonList(SUPPORTED), false, subsystem, mimetypeService);
// Populate the transformer values
for (Triple<String, String, String> triple: transformerNamesAndMimetypes)
{
String transformerName = triple.getFirst();
String sourceExt = triple.getSecond();
String targetExt = triple.getThird();
String sourceMimetype = ANY.equals(sourceExt) ? ANY : mimetypeService.getMimetype(sourceExt);
String targetMimetype = ANY.equals(targetExt) ? ANY : mimetypeService.getMimetype(targetExt);
SupportedAndUnsupportedTransformations supportedBytransformer = this.supported.get(transformerName);
if (supportedBytransformer == null)
{
supportedBytransformer = new SupportedAndUnsupportedTransformations();
this.supported.put(transformerName, supportedBytransformer);
}
boolean supported = getValueFromProperties(transformerName, sourceExt, targetExt, subsystem, SUPPORTED);
supportedBytransformer.put(sourceMimetype, targetMimetype, supported);
}
}
private boolean getValueFromProperties(String transformerName, String sourceExt, String targetExt,
ChildApplicationContextFactory subsystem, String propertySuffix)
{
String propertyName = CONTENT+transformerName+
(sourceExt == null ? "" : MIMETYPES_SEPARATOR+sourceExt+'.'+targetExt)+
propertySuffix;
String value = subsystem.getProperty(propertyName);
return value == null || value.equalsIgnoreCase("true");
}
/**
* See {@link TransformerConfig#isSupportedTransformation(ContentTransformer, String, String, TransformationOptions)}.
*/
public boolean isSupportedTransformation(ContentTransformer transformer, String sourceMimetype,
String targetMimetype, TransformationOptions options)
{
if (sourceMimetype == null)
{
sourceMimetype = ANY;
}
if (targetMimetype == null)
{
targetMimetype = ANY;
}
boolean isSupported = true;
String name = transformer.getName();
SupportedAndUnsupportedTransformations supportedBytransformer = supported.get(name);
if (supportedBytransformer != null)
{
isSupported = supportedBytransformer.isSupported(sourceMimetype, targetMimetype);
}
return isSupported;
}
// Class contains both supported and unsupported combinations to avoid having to
// add in an extra ANY to ANY combination which could be true or false. Having an
// extra combination might reduce understandability.
private class SupportedAndUnsupportedTransformations
{
DoubleMap<String, String, Boolean> supportedTransformations;
DoubleMap<String, String, Boolean> unsupportedTransformations;
boolean isSupported(String sourceMimetype, String targetMimetype)
{
boolean isSupported = true;
if (supportedTransformations != null)
{
Boolean sup = supportedTransformations.get(sourceMimetype, targetMimetype);
isSupported = sup != null;
}
if (isSupported && unsupportedTransformations != null)
{
Boolean sup = unsupportedTransformations.get(sourceMimetype, targetMimetype);
isSupported = sup == null;
}
return isSupported;
}
public void put(String sourceMimetype, String targetMimetype, boolean supported)
{
if (supported)
{
if (supportedTransformations == null)
{
supportedTransformations = new DoubleMap<String, String, Boolean>(ANY, ANY);
}
supportedTransformations.put(sourceMimetype, targetMimetype, supported);
}
else
{
if (unsupportedTransformations == null)
{
unsupportedTransformations = new DoubleMap<String, String, Boolean>(ANY, ANY);
}
unsupportedTransformations.put(sourceMimetype, targetMimetype, supported);
}
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2012 Alfresco Software Limited.
* Copyright (C) 2005-2013 Alfresco Software Limited.
*
* This file is part of Alfresco
*
@@ -118,6 +118,7 @@ public class TransformerDebug
private final long start;
private Call callType;
private Frame parent;
private int childId;
private Set<UnavailableTransformer> unavailableTransformers;
private String failureReason;
@@ -127,7 +128,8 @@ public class TransformerDebug
private Frame(Frame parent, String transformerName, String fromUrl, String sourceMimetype, String targetMimetype,
long sourceSize, TransformationOptions options, Call pushCall, boolean origDebugOutput)
{
this.id = parent == null ? -1 : ++parent.childId;
this.id = -1;
this.parent = parent;
this.fromUrl = fromUrl;
this.transformerName = transformerName;
this.sourceMimetype = sourceMimetype;
@@ -143,8 +145,8 @@ public class TransformerDebug
{
if (id == -1)
{
id = uniqueId.getAndIncrement();
}
id = parent == null ? uniqueId.getAndIncrement() : ++parent.childId;
}
return id;
}
@@ -223,14 +225,16 @@ public class TransformerDebug
private final NodeService nodeService;
private final MimetypeService mimetypeService;
private final TransformerConfig transformerConfig;
/**
* Constructor
*/
public TransformerDebug(NodeService nodeService, MimetypeService mimetypeService)
public TransformerDebug(NodeService nodeService, MimetypeService mimetypeService, TransformerConfig transformerConfig)
{
this.nodeService = nodeService;
this.mimetypeService = mimetypeService;
this.transformerConfig = transformerConfig;
}
/**
@@ -373,8 +377,10 @@ public class TransformerDebug
long maxSourceSizeKBytes = trans.getMaxSourceSizeKBytes(frame.sourceMimetype, frame.targetMimetype, frame.options);
String size = maxSourceSizeKBytes > 0 ? "< "+fileSize(maxSourceSizeKBytes*1024) : "";
int padSize = 10 - size.length();
log((c == 'a' ? "**" : " ") + (c++) + ") " + name + spaces(padName) +
size + spaces(padSize) + ms(trans.getTransformationTime()));
String priority = Integer.toString(transformerConfig.getPriority(trans, frame.sourceMimetype, frame.targetMimetype));
priority = spaces(2-priority.length())+priority;
log((c == 'a' ? "**" : " ") + (c++) + ") " + priority + ' ' + name + spaces(padName) +
size + spaces(padSize) + ms(trans.getTransformationTime(frame.sourceMimetype, frame.targetMimetype)));
}
if (frame.unavailableTransformers != null)
{
@@ -382,7 +388,7 @@ public class TransformerDebug
{
int pad = longestNameLength - unavailable.name.length();
String reason = "> "+fileSize(unavailable.maxSourceSizeKBytes*1024);
log("--" + (c++) + ") " + unavailable.name + spaces(pad+1) + reason, unavailable.debug);
log("--" + (c++) + ") " + unavailable.name + spaces(pad+1) + reason, unavailable.debug);
}
}
}
@@ -390,7 +396,7 @@ public class TransformerDebug
public void inactiveTransformer(ContentTransformer transformer)
{
log(getName(transformer)+' '+ms(transformer.getTransformationTime())+" INACTIVE");
log(getName(transformer)+' '+ms(transformer.getTransformationTime(null, null))+" INACTIVE");
}
public void activeTransformer(int mimetypePairCount, ContentTransformer transformer, String sourceMimetype,
@@ -398,7 +404,7 @@ public class TransformerDebug
{
if (firstMimetypePair)
{
log(getName(transformer)+' '+ms(transformer.getTransformationTime()));
log(getName(transformer)+' '+ms(transformer.getTransformationTime(sourceMimetype, targetMimetype)));
}
String i = Integer.toString(mimetypePairCount);
log(spaces(5-i.length())+mimetypePairCount+") "+getMimetypeExt(sourceMimetype)+getMimetypeExt(targetMimetype)+
@@ -416,7 +422,7 @@ public class TransformerDebug
: spaces(10);
char c = (char)('a'+transformerCount);
log(mimetypes+
" "+c+") "+getName(transformer)+' '+ms(transformer.getTransformationTime())+
" "+c+") "+getName(transformer)+' '+ms(transformer.getTransformationTime(sourceMimetype, targetMimetype))+
' '+fileSize((maxSourceSizeKBytes > 0) ? maxSourceSizeKBytes*1024 : maxSourceSizeKBytes)+
(maxSourceSizeKBytes == 0 || (explicit != null && !explicit) ? " disabled" : "")+
(explicit == null ? "" : explicit ? " EXPLICIT" : " not explicit"));
@@ -593,8 +599,8 @@ public class TransformerDebug
(fileName == null ? "" : fileName) +
(sourceSize >= 0 ? ' '+fileSize(sourceSize) : "") +
(transformerName == null ? "" : ' '+transformerName) +
(failureReason == null ? "" : ' '+failureReason) +
' '+ms;
' '+ms +
(failureReason == null ? "" : '\n'+failureReason.trim());
info.log(message, debug);
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright (C) 2005-2013 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.content.transform;
import static org.alfresco.repo.content.transform.TransformerConfig.ANY;
import static org.alfresco.repo.content.transform.TransformerConfig.CONTENT;
import static org.alfresco.repo.content.transform.TransformerConfig.PREFIX;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.util.Triple;
/**
* Provides access to transformer property names and values.
*
* @author Alan Davis
*/
public abstract class TransformerPropertyNameExtractor
{
/**
* Returns a set of transformer names, source extensions and target mimetype extensions
* from property names that defined transformation limits.
* @param separator after the transformer name and the source mimetype extension.
* Must start and end in a '.'.
* @param suffixes possible endings to the property names after the target mimetype extension.
* Must start with a '.' if there is a suffix.
* @param includeSummary if true will also look for property names without the separator,
* source mimetype and target mimetype.
* @param subsystem that provides the properties
* @param mimetypeService
*/
protected Set<Triple<String,String,String>> getTransformerNamesAndExt(String separator, Collection<String> suffixes, boolean includeSummary,
ChildApplicationContextFactory subsystem, MimetypeService mimetypeService)
{
Set<Triple<String,String,String>> transformerNamesAndExtensions =
new HashSet<Triple<String,String,String>>();
for (String propertyName: subsystem.getPropertyNames())
{
if (propertyName.startsWith(PREFIX))
{
for (String suffix: suffixes)
{
if (propertyName.endsWith(suffix))
{
String name = propertyName.substring(CONTENT.length(), propertyName.length()-suffix.length());
int i = name.lastIndexOf(separator);
if (i != -1)
{
String[] ext = name.substring(i+separator.length()).split("\\.");
if (ext.length == 2)
{
name = name.substring(0, i);
transformerNamesAndExtensions.add(
new Triple<String,String,String>(name, ext[0], ext[1]));
break;
}
}
else if (includeSummary)
{
transformerNamesAndExtensions.add(new Triple<String,String,String>(name, ANY, ANY));
break;
}
}
}
}
}
return transformerNamesAndExtensions;
}
}

View File

@@ -33,15 +33,12 @@ public interface TransformerSelector
/**
* Returns a sorted list of transformers that identifies the order in which transformers
* should be tried.
* @param transformers an unordered list of all transformers. This
* list may be modified.
* @param sourceMimetype
* @param sourceSize
* @param targetMimetype
* @param options transformation options
* @return a sorted list of transformers, with the best one first.
*/
List<ContentTransformer> selectTransformers(List<ContentTransformer> transformers,
String sourceMimetype, long sourceSize, String targetMimetype,
TransformationOptions options);
List<ContentTransformer> selectTransformers(String sourceMimetype, long sourceSize,
String targetMimetype, TransformationOptions options);
}

View File

@@ -28,94 +28,127 @@ import java.util.Map;
import org.alfresco.service.cmr.repository.TransformationOptions;
/**
* Implementation of a transformer selector that matches the code that was in place
* before a selector was introduced. It is expected that this code will be replaced.
* Default transformer selector implementation, which sorts by priority and then
* by average transform time. The transform time is only used once a threshold
* (number of transforms) has been reached. This average is maintained for each
* source target mimetype pair.<p>
*
* Prior to the introduction of this class the transformation time was only kept
* for each transformer. There was no threshold and there was a concept of
* 'Explicit' transformers, which would cause all other transformers to be discarded.
* It is still possible to disable transformers by giving adding unsupported mappings
* as has been done for transformers that would not have been used in the past as
* there existed one or more 'explicit' transformers (a concept not used by this
* TransformerSelector). By default a transformer has a priority of {@code 10}.
* Old 'Explicit' transformers have been given a priority of {@code 5}.
*
* @author Alan Davis
*/
public class TransformerSelectorImpl implements TransformerSelector
{
private TransformerConfig transformerConfig;
private ContentTransformerRegistry contentTransformerRegistry;
public void setTransformerConfig(TransformerConfig transformerConfig)
{
this.transformerConfig = transformerConfig;
}
public void setContentTransformerRegistry(ContentTransformerRegistry contentTransformerRegistry)
{
this.contentTransformerRegistry = contentTransformerRegistry;
}
@Override
public List<ContentTransformer> selectTransformers(List<ContentTransformer> transformers,
String sourceMimetype, long sourceSize, String targetMimetype,
TransformationOptions options)
public List<ContentTransformer> selectTransformers(String sourceMimetype, long sourceSize,
String targetMimetype, TransformationOptions options)
{
transformers = findTransformers(transformers, sourceMimetype, sourceSize, targetMimetype, options);
transformers = discardNonExplicitTransformers(transformers, sourceMimetype, sourceSize, targetMimetype, options);
transformers = sortTransformers(transformers, sourceMimetype, sourceSize, targetMimetype, options);
return transformers;
// TODO cache results for reuse. This was a heavy operation in the past and still is.
List<ContentTransformer> transformers = contentTransformerRegistry.getTransformers();
List<TransformerSortData> possibleTransformers = findTransformers(transformers, sourceMimetype, sourceSize, targetMimetype, options);
return sortTransformers(possibleTransformers);
}
/**
* Reduces the list of transformers down to only those capable of doing the transformation.
* Returns the list of possible transformers for the transformation.
*/
private List<ContentTransformer> findTransformers(List<ContentTransformer> allTransformers, String sourceMimetype,
private List<TransformerSortData> findTransformers(List<ContentTransformer> allTransformers, String sourceMimetype,
long sourceSize, String targetMimetype, TransformationOptions options)
{
List<ContentTransformer> transformers = new ArrayList<ContentTransformer>(2);
List<TransformerSortData> transformers = new ArrayList<TransformerSortData>(8);
for (ContentTransformer transformer : allTransformers)
{
if (transformer.isTransformable(sourceMimetype, sourceSize, targetMimetype, options) == true)
int priority = transformerConfig.getPriority(transformer, sourceMimetype, targetMimetype);
if (priority > 0 &&
transformer.isTransformable(sourceMimetype, sourceSize, targetMimetype, options) == true)
{
transformers.add(transformer);
transformers.add(new TransformerSortData(transformer, sourceMimetype, targetMimetype, priority));
}
}
return transformers;
}
/**
* Discards non explicit transformers if there are any explicit ones.
* Returns a sorted list of transformers by priority and then average time (ignored if the threshold
* has not been reached).
*/
private List<ContentTransformer> discardNonExplicitTransformers(List<ContentTransformer> allTransformers, String sourceMimetype,
long sourceSize, String targetMimetype, TransformationOptions options)
private List<ContentTransformer> sortTransformers(List<TransformerSortData> possibleTransformers)
{
List<ContentTransformer> transformers = new ArrayList<ContentTransformer>(2);
boolean foundExplicit = false;
Collections.sort(possibleTransformers);
for (ContentTransformer transformer : allTransformers)
List<ContentTransformer> transformers = new ArrayList<ContentTransformer>(possibleTransformers.size());
for (TransformerSortData possibleTransformer: possibleTransformers)
{
if (transformer.isExplicitTransformation(sourceMimetype, targetMimetype, options) == true)
{
if (foundExplicit == false)
{
transformers.clear();
foundExplicit = true;
}
transformers.add(transformer);
}
else
{
if (foundExplicit == false)
{
transformers.add(transformer);
}
}
transformers.add(possibleTransformer.transformer);
}
return transformers;
}
// sort by performance (quicker is "better")
private List<ContentTransformer> sortTransformers(List<ContentTransformer> transformers,
String sourceMimetype, long sourceSize, String targetMimetype,
TransformationOptions options)
private class TransformerSortData implements Comparable<TransformerSortData>
{
final Map<ContentTransformer,Long> activeTransformers = new HashMap<ContentTransformer, Long>();
for (ContentTransformer transformer : transformers)
private final ContentTransformer transformer;
private final int priority;
private long averageTime = -1;
TransformerSortData(ContentTransformer transformer, String sourceMimetype, String targetMimetype, int priority)
{
long transformationTime = transformer.getTransformationTime();
activeTransformers.put(transformer, transformationTime);
this.transformer = transformer;
this.priority = priority;
TransformerStatistics stats = transformerConfig.getStatistics(transformer, sourceMimetype, targetMimetype);
long threashold = transformerConfig.getThresholdCount(transformer, sourceMimetype, targetMimetype);
averageTime = (stats.getCount() < threashold) ? 0 : stats.getAverageTime();
}
List<ContentTransformer> sorted = new ArrayList<ContentTransformer>(activeTransformers.keySet());
Collections.sort(sorted, new Comparator<ContentTransformer>() {
@Override
public int compare(ContentTransformer a, ContentTransformer b)
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + (int) (averageTime ^ (averageTime >>> 32));
result = prime * result + priority;
return result;
}
@Override
public boolean equals(Object obj)
{
return transformer == ((TransformerSortData) obj).transformer;
}
@Override
public int compareTo(TransformerSortData that)
{
int relativePriority = priority - that.priority;
if (relativePriority != 0)
{
return activeTransformers.get(a).compareTo(activeTransformers.get(b));
return relativePriority;
}
});
return sorted;
long relativeTime = averageTime - that.averageTime;
return relativeTime > 0L ? 1 : relativeTime < 0L ? -1 : 0;
}
}
}

View File

@@ -0,0 +1,128 @@
/*
* Copyright (C) 2005-2012 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.content.transform;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.service.cmr.repository.TransformationOptions;
/**
* Implementation of a transformer selector that matches the code that was in place
* before a selector was introduced. Class is not used but exists to allow customers
* to maintain the previous approach if they really wish.
*
* @author Alan Davis
*/
public class TransformerSelectorImplOriginal implements TransformerSelector
{
private ContentTransformerRegistry contentTransformerRegistry;
public void setContentTransformerRegistry(ContentTransformerRegistry contentTransformerRegistry)
{
this.contentTransformerRegistry = contentTransformerRegistry;
}
@Override
public List<ContentTransformer> selectTransformers( String sourceMimetype, long sourceSize,
String targetMimetype, TransformationOptions options)
{
List<ContentTransformer> transformers = contentTransformerRegistry.getTransformers();
transformers = findTransformers(transformers, sourceMimetype, sourceSize, targetMimetype, options);
transformers = discardNonExplicitTransformers(transformers, sourceMimetype, sourceSize, targetMimetype, options);
transformers = sortTransformers(transformers, sourceMimetype, sourceSize, targetMimetype, options);
return transformers;
}
/**
* Reduces the list of transformers down to only those capable of doing the transformation.
*/
private List<ContentTransformer> findTransformers(List<ContentTransformer> allTransformers, String sourceMimetype,
long sourceSize, String targetMimetype, TransformationOptions options)
{
List<ContentTransformer> transformers = new ArrayList<ContentTransformer>(2);
for (ContentTransformer transformer : allTransformers)
{
if (transformer.isTransformable(sourceMimetype, sourceSize, targetMimetype, options) == true)
{
transformers.add(transformer);
}
}
return transformers;
}
/**
* Discards non explicit transformers if there are any explicit ones.
*/
private List<ContentTransformer> discardNonExplicitTransformers(List<ContentTransformer> allTransformers, String sourceMimetype,
long sourceSize, String targetMimetype, TransformationOptions options)
{
List<ContentTransformer> transformers = new ArrayList<ContentTransformer>(2);
boolean foundExplicit = false;
for (ContentTransformer transformer : allTransformers)
{
if (transformer.isExplicitTransformation(sourceMimetype, targetMimetype, options) == true)
{
if (foundExplicit == false)
{
transformers.clear();
foundExplicit = true;
}
transformers.add(transformer);
}
else
{
if (foundExplicit == false)
{
transformers.add(transformer);
}
}
}
return transformers;
}
// sort by performance (quicker is "better")
private List<ContentTransformer> sortTransformers(List<ContentTransformer> transformers,
String sourceMimetype, long sourceSize, String targetMimetype,
TransformationOptions options)
{
final Map<ContentTransformer,Long> activeTransformers = new HashMap<ContentTransformer, Long>();
for (ContentTransformer transformer : transformers)
{
long transformationTime = transformer.getTransformationTime(sourceMimetype, targetMimetype);
activeTransformers.put(transformer, transformationTime);
}
List<ContentTransformer> sorted = new ArrayList<ContentTransformer>(activeTransformers.keySet());
Collections.sort(sorted, new Comparator<ContentTransformer>() {
@Override
public int compare(ContentTransformer a, ContentTransformer b)
{
return activeTransformers.get(a).compareTo(activeTransformers.get(b));
}
});
return sorted;
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright (C) 2005-2012 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.content.transform;
/**
* Interface to obtain the configuration and performance data for every
* source, target and transformer combination.
*
* @author Alan Davis
*/
public interface TransformerStatistics
{
/**
* @return the extension of the source mimetype of the transformer.
*/
public String getSourceExt();
/**
* @return the extension of the target mimetype of the transformer.
*/
public String getTargetExt();
/**
* @return the name of the parent transformer.
*/
public String getTransformerName();
/**
* @return the number of time the transformer has been called.
*/
public long getCount();
/**
* @param count overrides the number of time the transformer has been called.
*/
public void setCount(long count);
/**
* @return the number of time the transformer has failed.
*/
public long getErrorCount();
/**
* @param errorCount overrides the number of time the transformer has failed.
*/
public void setErrorCount(long errorCount);
/**
* @return the average time taken by the the transformer.
*/
public long getAverageTime();
/**
* @param averageTime overrides the average time taken by the the transformer.
*/
public void setAverageTime(long averageTime);
/**
* @return <code>true</code> if this is the summary of all transformations done
* by a transformer rather than just between two specific mimetypes.
*/
public boolean isSummary();
/**
* @param transformationTime to be added to this TransformationData and its parents.
*/
public void recordTime(long transformationTime);
/**
* Adds 1 to the error count of this TransformationData and its parents.
*/
public void recordError();
}

View File

@@ -0,0 +1,296 @@
/*
* Copyright (C) 2005-2012 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.content.transform;
import static org.alfresco.repo.content.transform.TransformerConfig.ANY;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.TransformationOptionLimits;
/**
* Implementation of a {@link TransformerStatistics}.
*
* @author Alan Davis
*/
// TODO These values should be made visible via JMX.
public class TransformerStatisticsImpl implements TransformerStatistics
{
private final MimetypeService mimetypeService;
private final String sourceMimetype;
private final String targetMimetype;
private final ContentTransformer transformer;
private final TransformerStatistics parent;
private final long errorTime;
private double averageTime;
private long count = 0L;
private long errorCount = 0L;
public TransformerStatisticsImpl(MimetypeService mimetypeService, String sourceMimetype, String targetMimetype,
ContentTransformer transformer, TransformerStatistics parent, long errorTime,
long initialAverageTime, long initialCount)
{
this.mimetypeService = mimetypeService;
this.sourceMimetype = sourceMimetype;
this.targetMimetype = targetMimetype;
this.transformer = transformer;
this.parent = parent;
this.errorTime = errorTime;
averageTime = initialAverageTime;
count = initialCount;
}
@Override
public String getSourceExt()
{
return ANY.equals(sourceMimetype) ? ANY : mimetypeService.getExtension(sourceMimetype);
}
@Override
public String getTargetExt()
{
return ANY.equals(targetMimetype) ? ANY : mimetypeService.getExtension(targetMimetype);
}
@Override
public String getTransformerName()
{
return transformer == null ? TransformerConfig.SUMMARY_TRANSFORMER_NAME : transformer.getName();
}
@Override
public synchronized void recordTime(long transformationTime)
{
if (count == Long.MAX_VALUE)
{
// we have reached the max count - reduce it by half
// the average fluctuation won't be extreme
count /= 2L;
}
// adjust the average
count++;
double diffTime = ((double) transformationTime) - averageTime;
averageTime += diffTime / (double) count;
if (parent != null)
{
parent.recordTime(transformationTime);
}
}
@Override
public synchronized void recordError()
{
if (errorCount < Long.MAX_VALUE)
{
errorCount++;
}
if (errorTime > 0)
{
recordTime(errorTime);
}
if (parent != null)
{
parent.recordError();
}
}
@Override
public long getCount()
{
return count;
}
@Override
public void setCount(long count)
{
this.count = count;
}
@Override
public long getErrorCount()
{
return errorCount;
}
@Override
public void setErrorCount(long errorCount)
{
this.errorCount = errorCount;
}
@Override
public long getAverageTime()
{
return (long)averageTime;
}
@Override
public void setAverageTime(long averageTime)
{
this.averageTime = (double)averageTime;
}
public boolean isSummary()
{
return TransformerConfig.ANY.equals(sourceMimetype) && TransformerConfig.ANY.equals(targetMimetype);
}
//////////////////////////////////////// TODO Split into summary class ///////////////////////////////////
// private enum Property
// {
// priority(true)
// {
// String getValue(TransformerData bean)
// {
// return Integer.toString(bean.getPriority());
// }
// void setValue(TransformerData bean, String value)
// {
// bean.setPriority(Integer.valueOf(value));
// }
// },
//
// averageTime(false)
// {
// String getValue(TransformerData bean)
// {
// return Integer.toString(bean.getPriority());
// }
// },
//
// count(false)
// {
// String getValue(TransformerData bean)
// {
// return Integer.toString(bean.getPriority());
// }
// },
//
// errors(false)
// {
// String getValue(TransformerData bean)
// {
// return Integer.toString(bean.getPriority());
// }
// };
//
// private final boolean updatable;
//
// Property(boolean updatable)
// {
// this.updatable = updatable;
// }
//
// abstract String getValue(TransformerData bean);
//
// void setValue(TransformerData bean, String value)
// {
// }
//
// public boolean isUpdatable()
// {
// return updatable;
// }
//
// public static Set<String> getNames()
// {
// Set<String> names = new HashSet<String>();
// for (Property property: Property.class.getEnumConstants())
// {
// names.add(property.name());
// }
// return names;
// }
// };
//
// public List<String> getId()
// {
// List<String> id = super.getId();
//
// id.add(transformerName);
//
// if (TransformerConfig.ANY.equals(sourceExt))
// {
// id.add(sourceExt);
// }
//
// if (TransformerConfig.ANY.equals(targetExt))
// {
// id.add(targetExt);
// }
//
// return id;
// }
//
// public boolean isUpdateable(String name)
// {
// return Enum.valueOf(Property.class, name).isUpdatable();
// }
//
// @Override
// protected PropertyBackedBeanState createInitialState() throws IOException
// {
// return new PropertyBackedBeanState()
// {
//
// @Override
// public Set<String> getPropertyNames()
// {
// return Property.getNames();
// }
//
// @Override
// public String getProperty(String name)
// {
// return Enum.valueOf(Property.class, name).getValue(TransformerDataImpl.this);
// }
//
// @Override
// public void setProperty(String name, String value)
// {
// Enum.valueOf(Property.class, name).setValue(TransformerDataImpl.this, value);
// }
//
// @Override
// public void start()
// {
// ;
// }
//
// @Override
// public void stop()
// {
// ;
// }
// };
// }
}