diff --git a/config/alfresco/rendition-services-context.xml b/config/alfresco/rendition-services-context.xml index 00687b0609..df4f880012 100644 --- a/config/alfresco/rendition-services-context.xml +++ b/config/alfresco/rendition-services-context.xml @@ -122,15 +122,6 @@ - - - - - - - - - {http://www.alfresco.org/model/content/1.0}content diff --git a/source/java/org/alfresco/repo/rendition/executer/AbstractRenderingEngine.java b/source/java/org/alfresco/repo/rendition/executer/AbstractRenderingEngine.java index c92d950ff3..0bbbc2071c 100644 --- a/source/java/org/alfresco/repo/rendition/executer/AbstractRenderingEngine.java +++ b/source/java/org/alfresco/repo/rendition/executer/AbstractRenderingEngine.java @@ -25,69 +25,60 @@ */ package org.alfresco.repo.rendition.executer; -import static org.alfresco.service.cmr.rendition.RenditionService.PARAM_DESTINATION_PATH_TEMPLATE; -import static org.alfresco.service.cmr.rendition.RenditionService.PARAM_IS_COMPONENT_RENDITION; -import static org.alfresco.service.cmr.rendition.RenditionService.PARAM_ORPHAN_EXISTING_RENDITION; -import static org.alfresco.service.cmr.rendition.RenditionService.PARAM_RENDITION_NODETYPE; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.model.ContentModel; -import org.alfresco.model.RenditionModel; -import org.alfresco.repo.action.ParameterDefinitionImpl; -import org.alfresco.repo.action.executer.ActionExecuterAbstractBase; -import org.alfresco.repo.content.MimetypeMap; -import org.alfresco.repo.content.transform.UnimportantTransformException; -import org.alfresco.repo.nodelocator.NodeLocator; -import org.alfresco.repo.nodelocator.SelfNodeLocator; -import org.alfresco.repo.policy.BehaviourFilter; -import org.alfresco.repo.rendition.RenderingEngineDefinitionImpl; -import org.alfresco.repo.rendition.RenditionDefinitionImpl; -import org.alfresco.repo.rendition.RenditionLocation; -import org.alfresco.repo.rendition.RenditionLocationResolver; -import org.alfresco.repo.rendition.RenditionNodeManager; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; -import org.alfresco.repo.transaction.TransactionalResourceHelper; -import org.alfresco.service.cmr.action.Action; -import org.alfresco.service.cmr.action.ActionDefinition; -import org.alfresco.service.cmr.action.ActionServiceException; -import org.alfresco.service.cmr.action.ActionTrackingService; -import org.alfresco.service.cmr.action.ExecutionSummary; -import org.alfresco.service.cmr.action.ParameterDefinition; -import org.alfresco.service.cmr.attributes.AttributeService; -import org.alfresco.service.cmr.dictionary.DataTypeDefinition; -import org.alfresco.service.cmr.rendition.RenderCallback; -import org.alfresco.service.cmr.rendition.RenditionDefinition; -import org.alfresco.service.cmr.rendition.RenditionService; -import org.alfresco.service.cmr.rendition.RenditionServiceException; -import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.ContentReader; -import org.alfresco.service.cmr.repository.ContentService; -import org.alfresco.service.cmr.repository.ContentWriter; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.SerializedTransformationOptionsAccessor; -import org.alfresco.service.cmr.version.Version; -import org.alfresco.service.cmr.version.VersionService; -import org.alfresco.service.namespace.NamespaceException; -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.service.namespace.QName; -import org.alfresco.service.transaction.TransactionService; -import org.alfresco.util.GUID; -import org.alfresco.util.transaction.TransactionListener; -import org.alfresco.util.transaction.TransactionSupportUtil; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.extensions.surf.util.I18NUtil; - +import static org.alfresco.service.cmr.rendition.RenditionService.PARAM_DESTINATION_PATH_TEMPLATE; +import static org.alfresco.service.cmr.rendition.RenditionService.PARAM_IS_COMPONENT_RENDITION; +import static org.alfresco.service.cmr.rendition.RenditionService.PARAM_ORPHAN_EXISTING_RENDITION; +import static org.alfresco.service.cmr.rendition.RenditionService.PARAM_RENDITION_NODETYPE; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.model.RenditionModel; +import org.alfresco.repo.action.ParameterDefinitionImpl; +import org.alfresco.repo.action.executer.ActionExecuterAbstractBase; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.content.transform.UnimportantTransformException; +import org.alfresco.repo.nodelocator.NodeLocator; +import org.alfresco.repo.nodelocator.SelfNodeLocator; +import org.alfresco.repo.policy.BehaviourFilter; +import org.alfresco.repo.rendition.RenderingEngineDefinitionImpl; +import org.alfresco.repo.rendition.RenditionDefinitionImpl; +import org.alfresco.repo.rendition.RenditionLocation; +import org.alfresco.repo.rendition.RenditionLocationResolver; +import org.alfresco.repo.rendition.RenditionNodeManager; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ActionDefinition; +import org.alfresco.service.cmr.action.ActionServiceException; +import org.alfresco.service.cmr.action.ActionTrackingService; +import org.alfresco.service.cmr.action.ExecutionSummary; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.rendition.RenderCallback; +import org.alfresco.service.cmr.rendition.RenditionDefinition; +import org.alfresco.service.cmr.rendition.RenditionService; +import org.alfresco.service.cmr.rendition.RenditionServiceException; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.SerializedTransformationOptionsAccessor; +import org.alfresco.service.namespace.NamespaceException; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.GUID; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.surf.util.I18NUtil; + import com.sun.star.lang.NullPointerException; /** @@ -98,13 +89,11 @@ import com.sun.star.lang.NullPointerException; * @author Nick Smith * @since 3.3 */ -public abstract class AbstractRenderingEngine extends ActionExecuterAbstractBase implements TransactionListener +public abstract class AbstractRenderingEngine extends ActionExecuterAbstractBase { + /** Logger */ private static Log logger = LogFactory.getLog(AbstractRenderingEngine.class); - - private static final String RENDITIONED_CONTENT = "RENDITIONED_CONTENT"; - private static final String RENDERING_CONTEXTS = "RenderingEngine.Contexts"; protected static final String CONTENT_READER_NOT_FOUND_MESSAGE = "Cannot find Content Reader for document. Operation can't be performed"; protected static final String DEFAULT_RUN_AS_NAME = AuthenticationUtil.getSystemUserName(); @@ -155,10 +144,7 @@ public abstract class AbstractRenderingEngine extends ActionExecuterAbstractBase /* Injected Services */ protected ContentService contentService; protected MimetypeMap mimetypeMap; - protected ActionTrackingService actionTrackingService; - protected AttributeService attributeService; - protected TransactionService transactionService; - protected VersionService versionService; + protected ActionTrackingService actionTrackingService; /* Parameter names common to all Rendering Actions */ /** @@ -229,22 +215,7 @@ public abstract class AbstractRenderingEngine extends ActionExecuterAbstractBase private final NodeLocator temporaryParentNodeLocator; private final QName temporaryRenditionLinkType; - - public void setVersionService(VersionService versionService) - { - this.versionService = versionService; - } - - public void setAttributeService(AttributeService attributeService) - { - this.attributeService = attributeService; - } - - public void setTransactionService(TransactionService transactionService) - { - this.transactionService = transactionService; - } - + /** * Injects the nodeService bean. * @@ -555,13 +526,11 @@ public abstract class AbstractRenderingEngine extends ActionExecuterAbstractBase RenderingContext context = new RenderingContext(sourceNode, renditionDefinition, - targetContentProp); - - render(context); - + targetContentProp); + render(context); // This is a workaround for the fact that actions don't have return // values. - action.getParameterValues().put(PARAM_RESULT, context.getChildAssociationRef()); + action.getParameterValues().put(PARAM_RESULT, context.getChildAssociationRef()); } /** @@ -790,220 +759,111 @@ public abstract class AbstractRenderingEngine extends ActionExecuterAbstractBase result = defaultValue; return result; } - - public static String getRenderedContentKey(NodeRef sourceNode, VersionService versionService) - { - StringBuilder sb = new StringBuilder(); - sb.append(sourceNode.toString()); - sb.append("."); - Version version = versionService.getCurrentVersion(sourceNode); - sb.append(version != null ? version.getVersionLabel() : "1.0"); - return sb.toString(); - } - - protected class RenderingContext implements SerializedTransformationOptionsAccessor - { - private final String guid = GUID.generate(); - - private final NodeRef sourceNode; - private final RenditionDefinition definition; - private final QName renditionContentProperty; - private String renderedContentKey; - private ChildAssociationRef caNodeRef; - - /** - * @param sourceNode NodeRef - * @param definition RenditionDefinition - * @param renditionContentProperty QName - */ - RenderingContext(NodeRef sourceNode, RenditionDefinition definition, QName renditionContentProperty) - { - this.sourceNode = sourceNode; - this.definition = definition; - this.renditionContentProperty = renditionContentProperty; - this.renderedContentKey = AbstractRenderingEngine.getRenderedContentKey(sourceNode, versionService); - } - - public String getRenderedContentKey() - { - return renderedContentKey; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + getOuterType().hashCode(); - result = prime * result + ((guid == null) ? 0 : guid.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - RenderingContext other = (RenderingContext) obj; - if (!getOuterType().equals(other.getOuterType())) - return false; - if (guid == null) { - if (other.guid != null) - return false; - } else if (!guid.equals(other.guid)) - return false; - return true; - } - - /** - * Save an existing transformed content url for this rendition and node - * - * @param existingTransformedContentUrl - */ - void setExistingTransformedContentUrl(final String existingTransformedContentUrl) - { - // add the rendering context to the txn listener so that we can clean up - // any rendering context data saved using the AttributeService - TransactionalResourceHelper.getSet(RENDERING_CONTEXTS).add(this); - TransactionSupportUtil.bindListener(AbstractRenderingEngine.this, 0); - - if(logger.isDebugEnabled()) - { - logger.debug("setExistingTransformedContentUrl for renderedContentKey " + renderedContentKey - + ", rendition " + getDefinition().getRenditionName()); - } - - // make sure this is saved in a new transaction - transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() - { - @Override - public Void execute() throws Throwable - { - attributeService.setAttribute(existingTransformedContentUrl, RENDITIONED_CONTENT, renderedContentKey, - getDefinition().getRenditionName()); - return null; - } - }, false, true); - } - - /** - * Get an existing transformed content/rendition url for this rendition and node - * - * @return content url of transformed content/rendition - */ - String getExistingTransformedContentUrl() - { - // add the rendering context to the txn listener so that we can clean up - // any rendering context data saved using the AttributeService - TransactionalResourceHelper.getSet(RENDERING_CONTEXTS).add(this); - TransactionSupportUtil.bindListener(AbstractRenderingEngine.this, 0); - - String contentUrl = (String)attributeService.getAttribute(RENDITIONED_CONTENT, renderedContentKey, - getDefinition().getRenditionName()); - return contentUrl; - } - - /** - * @return the sourceNode - */ - public NodeRef getSourceNode() - { - return this.sourceNode; - } - - /** - * Lazily instantiation of the ChildAssociationRef - * @return ChildAssociationRef - */ - public synchronized ChildAssociationRef getChildAssociationRef() - { - if (this.caNodeRef == null) - { - this.caNodeRef = createRenditionNodeAssoc(sourceNode, definition); - } - - return this.caNodeRef; - } - - /** - * @return the destinationNode - */ - public NodeRef getDestinationNode() - { - return getChildAssociationRef().getChildRef(); - } - - /** - * @return the definition - */ - public RenditionDefinition getDefinition() - { - return this.definition; - } - - public T getCheckedParam(String paramName, Class clazz) - { - return AbstractRenderingEngine.getCheckedParam(paramName, clazz, definition); - } - - public T getParamWithDefault(String paramName, T defaultValue) - { - return AbstractRenderingEngine.getParamWithDefault(paramName, defaultValue, definition); - } - - public ContentReader makeContentReader() - { - QName srcContentProp = getParamWithDefault(PARAM_SOURCE_CONTENT_PROPERTY, DEFAULT_CONTENT_PROPERTY); - ContentReader contentReader = contentService.getReader(sourceNode, srcContentProp); - if (contentReader == null || !contentReader.exists()) - { - throw new UnimportantTransformException(CONTENT_READER_NOT_FOUND_MESSAGE); - } - return contentReader; - } - - public ContentWriter makeContentWriter() - { - ContentWriter contentWriter = contentService.getWriter(getDestinationNode(), renditionContentProperty, true); - String mimetype = getTargetMimeType(this); - contentWriter.setMimetype(mimetype); - String encoding = getTargetEncoding(this); - contentWriter.setEncoding(encoding); - return contentWriter; - } - - public int getIntegerParam(String key, int defaultValue) - { - Serializable serializable = definition.getParameterValue(key); - if (serializable == null) - return defaultValue; - else - { - Number number = (Number) serializable; - return number.intValue(); - } - } - - @Override - public String toString() - { - return "RenderingContext [sourceNode=" + sourceNode + ", renderedContentKey=" + renderedContentKey - + ", definition=" + definition + ", renditionContentProperty=" + renditionContentProperty + ", caNodeRef=" + caNodeRef - + ", actionDefinitionName=" + definition.getActionDefinitionName() - + ", renditionName=" + definition.getRenditionName() - + "]"; - } - - private AbstractRenderingEngine getOuterType() - { - return AbstractRenderingEngine.this; - } - } + protected class RenderingContext implements SerializedTransformationOptionsAccessor + { + private final NodeRef sourceNode; + private final RenditionDefinition definition; + private final QName renditionContentProperty; + + private ChildAssociationRef caNodeRef; + + /** + * @param sourceNode NodeRef + * @param definition RenditionDefinition + * @param renditionContentProperty QName + */ + public RenderingContext(NodeRef sourceNode,// + RenditionDefinition definition,// + QName renditionContentProperty) + { + this.sourceNode = sourceNode; + this.definition = definition; + this.renditionContentProperty = renditionContentProperty; + } + + /** + * @return the sourceNode + */ + public NodeRef getSourceNode() + { + return this.sourceNode; + } + + /** + * Lazily instantiation of the ChildAssociationRef + * @return ChildAssociationRef + */ + public synchronized ChildAssociationRef getChildAssociationRef() + { + if (this.caNodeRef == null) + { + this.caNodeRef = createRenditionNodeAssoc(sourceNode, definition); + } + return this.caNodeRef; + } + + /** + * @return the destinationNode + */ + public NodeRef getDestinationNode() + { + return getChildAssociationRef().getChildRef(); + } + + /** + * @return the definition + */ + public RenditionDefinition getDefinition() + { + return this.definition; + } + + public T getCheckedParam(String paramName, Class clazz) + { + return AbstractRenderingEngine.getCheckedParam(paramName, clazz, definition); + } + + public T getParamWithDefault(String paramName, T defaultValue) + { + return AbstractRenderingEngine.getParamWithDefault(paramName, defaultValue, definition); + } + + public ContentReader makeContentReader() + { + QName srcContentProp = getParamWithDefault(PARAM_SOURCE_CONTENT_PROPERTY, DEFAULT_CONTENT_PROPERTY); + ContentReader contentReader = contentService.getReader(sourceNode, srcContentProp); + if (contentReader == null || !contentReader.exists()) + { + throw new UnimportantTransformException(CONTENT_READER_NOT_FOUND_MESSAGE); + } + return contentReader; + } + + public ContentWriter makeContentWriter() + { + ContentWriter contentWriter = contentService.getWriter(getDestinationNode(), renditionContentProperty, true); + String mimetype = getTargetMimeType(this); + contentWriter.setMimetype(mimetype); + String encoding = getTargetEncoding(this); + contentWriter.setEncoding(encoding); + return contentWriter; + } + + public int getIntegerParam(String key, int defaultValue) + { + Serializable serializable = definition.getParameterValue(key); + if (serializable == null) + return defaultValue; + else + { + Number number = (Number) serializable; + return number.intValue(); + } + } + } + + protected void tagSourceNodeAsRenditioned(final RenditionDefinition renditionDef, final NodeRef actionedUponNodeRef) { // Adds the 'Renditioned' aspect to the source node if it @@ -1260,69 +1120,5 @@ public abstract class AbstractRenderingEngine extends ActionExecuterAbstractBase "multiple instances of the same action"); } return executionSummaries.iterator().next(); - } - - /** - * {@inheritDoc} - */ - @Override - public void beforeCommit(boolean readOnly) - { - } - - /** - * {@inheritDoc} - */ - @Override - public void beforeCompletion() - { - } - - /** - * {@inheritDoc} - */ - @Override - public void afterCommit() - { - // clear saved rendition data if we are successful - - final Set renderingContexts = TransactionalResourceHelper.getSet(RENDERING_CONTEXTS); - - if(logger.isDebugEnabled()) - { - logger.debug("Cleaning up " + renderingContexts.size() + " rendering contexts"); - } - - if(!renderingContexts.isEmpty()) - { - transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() - { - @Override - public Void execute() throws Throwable - { - for(RenderingContext context : renderingContexts) - { - if(logger.isDebugEnabled()) - { - logger.debug("Cleaning up rendering context for source node " + context.getSourceNode() - + ", rendition " + context.getDefinition().getRenditionName()); - } - - attributeService.removeAttributes(RENDITIONED_CONTENT, context.getRenderedContentKey(), - context.getDefinition().getRenditionName()); - } - - return null; - } - }, false, true); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void afterRollback() - { } } diff --git a/source/java/org/alfresco/repo/rendition/executer/AbstractTransformationRenderingEngine.java b/source/java/org/alfresco/repo/rendition/executer/AbstractTransformationRenderingEngine.java index 9cb74474d3..63b8bf5b20 100644 --- a/source/java/org/alfresco/repo/rendition/executer/AbstractTransformationRenderingEngine.java +++ b/source/java/org/alfresco/repo/rendition/executer/AbstractTransformationRenderingEngine.java @@ -26,50 +26,43 @@ package org.alfresco.repo.rendition.executer; -import java.io.Serializable; -import java.util.Collection; -import java.util.Date; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.FutureTask; - -import org.alfresco.repo.action.ParameterDefinitionImpl; -import org.alfresco.repo.content.ContentContext; -import org.alfresco.repo.content.ContentStore; -import org.alfresco.repo.content.filestore.FileContentStore; -import org.alfresco.repo.content.transform.ContentTransformer; -import org.alfresco.repo.content.transform.TransformerConfig; -import org.alfresco.repo.content.transform.TransformerDebug; +import java.io.Serializable; +import java.util.Collection; +import java.util.Date; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.FutureTask; + +import org.alfresco.repo.action.ParameterDefinitionImpl; +import org.alfresco.repo.content.transform.ContentTransformer; +import org.alfresco.repo.content.transform.TransformerConfig; +import org.alfresco.repo.content.transform.TransformerDebug; import org.alfresco.repo.content.transform.UnsupportedTransformationException; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.service.cmr.action.ActionServiceException; -import org.alfresco.service.cmr.action.ActionTrackingService; -import org.alfresco.service.cmr.action.ExecutionDetails; -import org.alfresco.service.cmr.action.ExecutionSummary; -import org.alfresco.service.cmr.action.ParameterDefinition; -import org.alfresco.service.cmr.dictionary.DataTypeDefinition; -import org.alfresco.service.cmr.rendition.RenditionCancelledException; -import org.alfresco.service.cmr.rendition.RenditionServiceException; -import org.alfresco.service.cmr.repository.ContentReader; -import org.alfresco.service.cmr.repository.ContentWriter; -import org.alfresco.service.cmr.repository.NoTransformerException; -import org.alfresco.service.cmr.repository.TransformationOptionLimits; -import org.alfresco.service.cmr.repository.TransformationOptions; -import org.alfresco.service.cmr.repository.TransformationSourceOptions; -import org.alfresco.service.cmr.repository.TransformationSourceOptions.TransformationSourceOptionsSerializer; -import org.alfresco.util.TempFileProvider; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.action.ActionServiceException; +import org.alfresco.service.cmr.action.ActionTrackingService; +import org.alfresco.service.cmr.action.ExecutionDetails; +import org.alfresco.service.cmr.action.ExecutionSummary; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.rendition.RenditionCancelledException; +import org.alfresco.service.cmr.rendition.RenditionServiceException; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NoTransformerException; +import org.alfresco.service.cmr.repository.TransformationOptionLimits; +import org.alfresco.service.cmr.repository.TransformationOptions; +import org.alfresco.service.cmr.repository.TransformationSourceOptions; +import org.alfresco.service.cmr.repository.TransformationSourceOptions.TransformationSourceOptionsSerializer; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; /** * @author Nick Smith */ -public abstract class AbstractTransformationRenderingEngine extends AbstractRenderingEngine implements ApplicationContextAware +public abstract class AbstractTransformationRenderingEngine extends AbstractRenderingEngine { private static Log logger = LogFactory.getLog(AbstractTransformationRenderingEngine.class); @@ -119,8 +112,6 @@ public abstract class AbstractTransformationRenderingEngine extends AbstractRend * This optional {@link String} parameter specifies the type (or use) of the rendition. */ public static final String PARAM_USE = TransformerConfig.USE.replaceAll("\\.", ""); - - private ApplicationContext applicationContext; /* Error messages */ private static final String TRANSFORMER_NOT_EXISTS_MESSAGE_PATTERN = "Transformer for '%s' source mime type and '%s' target mime type was not found. Operation can't be performed"; @@ -128,18 +119,11 @@ public abstract class AbstractTransformationRenderingEngine extends AbstractRend private static final String TRANSFORMING_ERROR_MESSAGE = "Some error occurred during document transforming. Error message: "; private Collection sourceOptionsSerializers; - - private ContentStore tempStore; - + public Collection getSourceOptionsSerializers() { return sourceOptionsSerializers; } - - public void setApplicationContext(ApplicationContext applicationContext) - { - this.applicationContext = applicationContext; - } public void setSourceOptionsSerializers(Collection sourceOptionsSerializers) { @@ -173,198 +157,166 @@ public abstract class AbstractTransformationRenderingEngine extends AbstractRend if (executorService == null) { executorService = Executors.newCachedThreadPool(); - } - - this.tempStore = new FileContentStore(this.applicationContext, TempFileProvider.getTempDir().getAbsolutePath()); + } } - - private void copyTransformedContent(ContentReader transformedContentReader, RenderingContext context) - { - // Copy content from temp writer to real writer - ContentWriter writer = context.makeContentWriter(); - writer.putContent(transformedContentReader.getContentInputStream()); - } - + /* * (non-Javadoc) * @see org.alfresco.repo.rendition.executer.AbstractRenderingEngine#render(org.alfresco.repo.rendition.executer.AbstractRenderingEngine.RenderingContext) */ @Override protected void render(RenderingContext context) - { - // check for existing transformed content and reuse if present to avoid re-rendering - String existingTransformedContentUrl = context.getExistingTransformedContentUrl(); - if(existingTransformedContentUrl != null) - { - if(logger.isDebugEnabled()) - { - logger.debug("Reusing existing rendered content " + existingTransformedContentUrl - + " for rendering context " + context); - } + { + ContentReader contentReader = context.makeContentReader(); + // There will have been an exception if there is no content data so contentReader is not null. + String sourceUrl = contentReader.getContentUrl(); + String sourceMimeType = contentReader.getMimetype(); + String targetMimeType = getTargetMimeType(context); - ContentReader existingTransformedContent = tempStore.getReader(existingTransformedContentUrl); - copyTransformedContent(existingTransformedContent, context); - } - else - { - if(logger.isDebugEnabled()) - { - logger.debug("Rendering for rendering context " + context); - } + // The child NodeRef gets created here + TransformationOptions options = getTransformOptions(context); - ContentReader contentReader = context.makeContentReader(); - // There will have been an exception if there is no content data so contentReader is not null. - String sourceUrl = contentReader.getContentUrl(); - String sourceMimeType = contentReader.getMimetype(); - String targetMimeType = getTargetMimeType(context); - - // The child NodeRef gets created here - TransformationOptions options = getTransformOptions(context); - - // Log the following getTransform() as trace so we can see the wood for the trees - ContentTransformer transformer; - boolean orig = TransformerDebug.setDebugOutput(false); + // Log the following getTransform() as trace so we can see the wood for the trees + ContentTransformer transformer; + boolean orig = TransformerDebug.setDebugOutput(false); + try + { + transformer = this.contentService.getTransformer(sourceUrl, sourceMimeType, contentReader.getSize(), targetMimeType, options); + } + finally + { + TransformerDebug.setDebugOutput(orig); + } + + if (null == transformer) + { + // There's no transformer available for the requested rendition! + throw new RenditionServiceException(String.format(TRANSFORMER_NOT_EXISTS_MESSAGE_PATTERN, sourceMimeType, + targetMimeType)); + } + + if (!transformer.isTransformable(sourceMimeType, contentReader.getSize(), targetMimeType, options)) + { + throw new RenditionServiceException(String.format(NOT_TRANSFORMABLE_MESSAGE_PATTERN, sourceMimeType, targetMimeType)); + } + + long startTime = new Date().getTime(); + boolean actionCancelled = false; + boolean actionCompleted = false; + + // Cache the execution summary to get details later + ExecutionSummary executionSummary = null; + try + { + executionSummary = getExecutionSummary(context); + } + catch (ActionServiceException e) + { + if (logger.isInfoEnabled()) + { + logger.info("Cancelling of multiple concurrent action instances " + + "currently unsupported, this action can't be cancelled"); + } + } + + // Call the transform in a different thread so we can move on if cancelled + FutureTask transformTask = new FutureTask( + new TransformationCallable(contentReader, targetMimeType, options, context, + AuthenticationUtil.getFullyAuthenticatedUser())); + getExecutorService().execute(transformTask); + + // Start checking for cancellation or timeout + while (true) + { try { - transformer = this.contentService.getTransformer(sourceUrl, sourceMimeType, contentReader.getSize(), targetMimeType, options); - } - finally - { - TransformerDebug.setDebugOutput(orig); - } - - if (null == transformer) - { - // There's no transformer available for the requested rendition! - throw new RenditionServiceException(String.format(TRANSFORMER_NOT_EXISTS_MESSAGE_PATTERN, sourceMimeType, - targetMimeType)); - } - - if (!transformer.isTransformable(sourceMimeType, contentReader.getSize(), targetMimeType, options)) - { - throw new RenditionServiceException(String.format(NOT_TRANSFORMABLE_MESSAGE_PATTERN, sourceMimeType, targetMimeType)); - } - - long startTime = new Date().getTime(); - boolean actionCancelled = false; - boolean actionCompleted = false; - - // Cache the execution summary to get details later - ExecutionSummary executionSummary = null; - try - { - executionSummary = getExecutionSummary(context); - } - catch (ActionServiceException e) - { - if (logger.isInfoEnabled()) + Thread.sleep(CANCELLED_ACTION_POLLING_INTERVAL); + if (transformTask.isDone()) { - logger.info("Cancelling of multiple concurrent action instances " + - "currently unsupported, this action can't be cancelled"); - } - } - - // Call the transform in a different thread so we can move on if cancelled - FutureTask transformTask = new FutureTask( - new TransformationCallable(contentReader, targetMimeType, options, context, - AuthenticationUtil.getFullyAuthenticatedUser())); - getExecutorService().execute(transformTask); - - // Start checking for cancellation or timeout - while (true) - { - try - { - Thread.sleep(CANCELLED_ACTION_POLLING_INTERVAL); - if (transformTask.isDone()) - { - actionCompleted = true; - break; - } - // Check timeout in case transformer doesn't obey it - if (options.getTimeoutMs() > 0 && - new Date().getTime() - startTime > (options.getTimeoutMs() + CANCELLED_ACTION_POLLING_INTERVAL)) - { - // We hit a timeout, let the transform thread continue but results will be ignored - if (logger.isDebugEnabled()) - { - logger.debug("Transformation did not obey timeout limit, " + - "rendition action is moving on"); - } - break; - } - if (executionSummary != null) - { - ExecutionDetails executionDetails = - actionTrackingService.getExecutionDetails(executionSummary); - if (executionDetails != null) - { - actionCancelled = executionDetails.isCancelRequested(); - if (actionCancelled) - { - if (logger.isDebugEnabled()) - { - logger.debug("Cancelling transformation"); - } - transformTask.cancel(true); - break; - } - } - } - } - catch (InterruptedException e) - { - // entire thread was asked to stop - actionCancelled = true; - transformTask.cancel(true); + actionCompleted = true; break; } + // Check timeout in case transformer doesn't obey it + if (options.getTimeoutMs() > 0 && + new Date().getTime() - startTime > (options.getTimeoutMs() + CANCELLED_ACTION_POLLING_INTERVAL)) + { + // We hit a timeout, let the transform thread continue but results will be ignored + if (logger.isDebugEnabled()) + { + logger.debug("Transformation did not obey timeout limit, " + + "rendition action is moving on"); + } + break; + } + if (executionSummary != null) + { + ExecutionDetails executionDetails = + actionTrackingService.getExecutionDetails(executionSummary); + if (executionDetails != null) + { + actionCancelled = executionDetails.isCancelRequested(); + if (actionCancelled) + { + if (logger.isDebugEnabled()) + { + logger.debug("Cancelling transformation"); + } + transformTask.cancel(true); + break; + } + } + } } - - if (actionCancelled) + catch (InterruptedException e) { - throw new RenditionCancelledException("Rendition action cancelled"); + // entire thread was asked to stop + actionCancelled = true; + transformTask.cancel(true); + break; } - - if (!actionCompleted && !actionCancelled) + } + + if (actionCancelled) + { + throw new RenditionCancelledException("Rendition action cancelled"); + } + + if (!actionCompleted && !actionCancelled) + { + throw new RenditionServiceException("Transformation failed to obey timeout limit"); + } + + if (actionCompleted) + { + // Copy content from temp writer to real writer + ContentWriter writer = context.makeContentWriter(); + try { - throw new RenditionServiceException("Transformation failed to obey timeout limit"); - } - - if (actionCompleted) - { - try - { - // We should not need another timeout here, things should be ready for us - ContentWriter tempTarget = transformTask.get(); - if (tempTarget == null) - { - // We should never be in this state, but just in case - throw new RenditionServiceException("Target of transformation not present"); - } - - // save transform result in case we need to retry - context.setExistingTransformedContentUrl(tempTarget.getReader().getContentUrl()); - - copyTransformedContent(tempTarget.getReader(), context); - } - catch (ExecutionException e) - { - // Unwrap our cause and throw that - Throwable transformException = e.getCause(); - if (transformException instanceof RuntimeException) - { - throw (RuntimeException) e.getCause(); - } - throw new RenditionServiceException(TRANSFORMING_ERROR_MESSAGE + e.getCause().getMessage(), e.getCause()); - } - catch (InterruptedException e) - { - // We were asked to stop - transformTask.cancel(true); - } - } - } + // We should not need another timeout here, things should be ready for us + ContentWriter tempTarget = transformTask.get(); + if (tempTarget == null) + { + // We should never be in this state, but just in case + throw new RenditionServiceException("Target of transformation not present"); + } + writer.putContent(tempTarget.getReader().getContentInputStream()); + } + catch (ExecutionException e) + { + // Unwrap our cause and throw that + Throwable transformException = e.getCause(); + if (transformException instanceof RuntimeException) + { + throw (RuntimeException) e.getCause(); + } + throw new RenditionServiceException(TRANSFORMING_ERROR_MESSAGE + e.getCause().getMessage(), e.getCause()); + } + catch (InterruptedException e) + { + // We were asked to stop + transformTask.cancel(true); + } + } } protected abstract TransformationOptions getTransformOptions(RenderingContext context); @@ -452,7 +404,7 @@ public abstract class AbstractTransformationRenderingEngine extends AbstractRend return paramList; } - + /** * Implementation of Callable for doing the work of the transformation * which returns the temporary content writer if successful. @@ -488,7 +440,7 @@ public abstract class AbstractTransformationRenderingEngine extends AbstractRend public ContentWriter doWork() throws Exception { // ALF-15715: Use temporary write to avoid operating on the real node for fear of row locking while long transforms are in progress - ContentWriter tempContentWriter = tempStore.getWriter(ContentContext.NULL_CONTEXT); + ContentWriter tempContentWriter = contentService.getTempWriter(); tempContentWriter.setMimetype(targetMimeType); try { diff --git a/source/java/org/alfresco/repo/thumbnail/ThumbnailServiceImpl.java b/source/java/org/alfresco/repo/thumbnail/ThumbnailServiceImpl.java index 9d34d4e315..65c4abd289 100644 --- a/source/java/org/alfresco/repo/thumbnail/ThumbnailServiceImpl.java +++ b/source/java/org/alfresco/repo/thumbnail/ThumbnailServiceImpl.java @@ -265,8 +265,8 @@ public class ThumbnailServiceImpl implements ThumbnailService, if (logger.isDebugEnabled()) { - logger.debug("Thumbnail created " + childAssoc + " for sourceNodeRef " + sourceNodeRef + ", thumbnail " - + thumbnailName + ", thumbnailNodeRef " + thumbnailNodeRef); + logger.debug("Thumbnail created " + childAssoc + " for sourceNodeRef " + sourceNodeRef + ", thumbnail " + thumbnailName + + ", thumbnailNodeRef " + thumbnailNodeRef); } // MNT-15135: Cache the associations between parent nodes and updated thumbnails, @@ -382,12 +382,6 @@ public class ThumbnailServiceImpl implements ThumbnailService, //We can be in a read-only transaction, so force a new transaction requiresNew = true; } - - // Get the name of the thumbnail and add to properties map - QName thumbnailQName = getThumbnailQName(thumbnailName); - final RenditionDefinition definition = createRenditionDefinition(contentProperty, mimetype, - transformationOptions, thumbnailQName, assocDetails); - return txnHelper.doInTransaction(new RetryingTransactionCallback() { @@ -398,12 +392,18 @@ public class ThumbnailServiceImpl implements ThumbnailService, { public NodeRef doWork() throws Exception { - return createThumbnailNode(node, definition, thumbnailName); + return createThumbnailNode( node, + contentProperty, + mimetype, + transformationOptions, + thumbnailName, + assocDetails); } }, AuthenticationUtil.getSystemUserName()); } }, false, requiresNew); + } private QName getThumbnailQName(String localThumbnailName) @@ -717,8 +717,14 @@ public class ThumbnailServiceImpl implements ThumbnailService, return definition; } - private NodeRef createThumbnailNode(final NodeRef node, final RenditionDefinition definition, final String thumbnailName) + private NodeRef createThumbnailNode(final NodeRef node, final QName contentProperty, + final String mimetype, final TransformationOptions transformationOptions, final String thumbnailName, + final ThumbnailParentAssociationDetails assocDetails) { + // Get the name of the thumbnail and add to properties map + QName thumbnailQName = getThumbnailQName(thumbnailName); + RenditionDefinition definition = createRenditionDefinition(contentProperty, mimetype, + transformationOptions, thumbnailQName, assocDetails); try { ChildAssociationRef thumbnailAssoc = renditionService.render(node, definition); diff --git a/source/test-java/org/alfresco/repo/rendition/RenditionServiceIntegrationTest.java b/source/test-java/org/alfresco/repo/rendition/RenditionServiceIntegrationTest.java index fc239c240e..26793fd4c2 100644 --- a/source/test-java/org/alfresco/repo/rendition/RenditionServiceIntegrationTest.java +++ b/source/test-java/org/alfresco/repo/rendition/RenditionServiceIntegrationTest.java @@ -26,75 +26,73 @@ package org.alfresco.repo.rendition; -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.Serializable; -import java.net.URL; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import javax.imageio.ImageIO; - -import org.alfresco.model.ContentModel; -import org.alfresco.model.RenditionModel; -import org.alfresco.repo.action.RuntimeActionService; -import org.alfresco.repo.action.executer.ExporterActionExecuter; -import org.alfresco.repo.content.MimetypeMap; -import org.alfresco.repo.content.transform.AbstractContentTransformerTest; -import org.alfresco.repo.content.transform.ContentTransformer; -import org.alfresco.repo.content.transform.ContentTransformerRegistry; -import org.alfresco.repo.content.transform.UnimportantTransformException; -import org.alfresco.repo.content.transform.magick.ImageTransformationOptions; -import org.alfresco.repo.jscript.ClasspathScriptLocation; -import org.alfresco.repo.model.Repository; -import org.alfresco.repo.policy.BehaviourFilter; -import org.alfresco.repo.rendition.executer.AbstractRenderingEngine; -import org.alfresco.repo.rendition.executer.FreemarkerRenderingEngine; -import org.alfresco.repo.rendition.executer.ImageRenderingEngine; -import org.alfresco.repo.rendition.executer.ReformatRenderingEngine; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.transaction.RetryingTransactionHelper; -import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; -import org.alfresco.service.cmr.action.Action; -import org.alfresco.service.cmr.attributes.AttributeService; -import org.alfresco.service.cmr.coci.CheckOutCheckInService; -import org.alfresco.service.cmr.lock.LockService; -import org.alfresco.service.cmr.lock.LockType; -import org.alfresco.service.cmr.rendition.CompositeRenditionDefinition; -import org.alfresco.service.cmr.rendition.RenderCallback; -import org.alfresco.service.cmr.rendition.RenderingEngineDefinition; -import org.alfresco.service.cmr.rendition.RenditionCancelledException; -import org.alfresco.service.cmr.rendition.RenditionDefinition; -import org.alfresco.service.cmr.rendition.RenditionService; -import org.alfresco.service.cmr.rendition.RenditionServiceException; -import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.ContentData; -import org.alfresco.service.cmr.repository.ContentIOException; -import org.alfresco.service.cmr.repository.ContentReader; -import org.alfresco.service.cmr.repository.ContentService; -import org.alfresco.service.cmr.repository.ContentWriter; -import org.alfresco.service.cmr.repository.CropSourceOptions.CropSourceOptionsSerializer; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.ScriptLocation; -import org.alfresco.service.cmr.repository.ScriptService; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.repository.TransformationOptions; -import org.alfresco.service.cmr.version.VersionService; -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.service.namespace.QName; -import org.alfresco.service.namespace.RegexQNamePattern; -import org.alfresco.service.transaction.TransactionService; -import org.alfresco.test_category.OwnJVMTestsCategory; -import org.alfresco.util.BaseAlfrescoSpringTest; -import org.alfresco.util.Pair; -import org.junit.experimental.categories.Category; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import javax.imageio.ImageIO; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.model.RenditionModel; +import org.alfresco.repo.action.RuntimeActionService; +import org.alfresco.repo.action.executer.ExporterActionExecuter; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.content.transform.AbstractContentTransformerTest; +import org.alfresco.repo.content.transform.ContentTransformer; +import org.alfresco.repo.content.transform.ContentTransformerRegistry; +import org.alfresco.repo.content.transform.UnimportantTransformException; +import org.alfresco.repo.content.transform.magick.ImageTransformationOptions; +import org.alfresco.repo.jscript.ClasspathScriptLocation; +import org.alfresco.repo.model.Repository; +import org.alfresco.repo.policy.BehaviourFilter; +import org.alfresco.repo.rendition.executer.AbstractRenderingEngine; +import org.alfresco.repo.rendition.executer.FreemarkerRenderingEngine; +import org.alfresco.repo.rendition.executer.ImageRenderingEngine; +import org.alfresco.repo.rendition.executer.ReformatRenderingEngine; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.coci.CheckOutCheckInService; +import org.alfresco.service.cmr.lock.LockService; +import org.alfresco.service.cmr.lock.LockType; +import org.alfresco.service.cmr.rendition.CompositeRenditionDefinition; +import org.alfresco.service.cmr.rendition.RenderCallback; +import org.alfresco.service.cmr.rendition.RenderingEngineDefinition; +import org.alfresco.service.cmr.rendition.RenditionCancelledException; +import org.alfresco.service.cmr.rendition.RenditionDefinition; +import org.alfresco.service.cmr.rendition.RenditionService; +import org.alfresco.service.cmr.rendition.RenditionServiceException; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.ContentIOException; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.CropSourceOptions.CropSourceOptionsSerializer; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.ScriptLocation; +import org.alfresco.service.cmr.repository.ScriptService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.repository.TransformationOptions; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.test_category.OwnJVMTestsCategory; +import org.alfresco.util.BaseAlfrescoSpringTest; +import org.alfresco.util.Pair; +import org.junit.experimental.categories.Category; import org.springframework.context.ConfigurableApplicationContext; /** @@ -1294,7 +1292,7 @@ public class RenditionServiceIntegrationTest extends BaseAlfrescoSpringTest assertNull(results.getAssoc()); assertEquals("Expected a UnimportantTransformException", UnimportantTransformException.class, results.getThrowable().getClass()); - } + } /** * This method performs an asynchronous rendition and calls back the result to the @@ -2608,22 +2606,15 @@ public class RenditionServiceIntegrationTest extends BaseAlfrescoSpringTest assertTrue("upper left should be "+topLeft, Integer.toHexString(rgbAtTopLeft).endsWith(topLeft)); int rgbAtBottomRight = img.getRGB(img.getWidth() - 1, img.getHeight() - 1); assertTrue("lower right should be "+bottomRight, Integer.toHexString(rgbAtBottomRight).endsWith(bottomRight)); - } - + } + /** * A dummy rendering engine used in testing */ private static class DummyHelloWorldRenditionEngine extends AbstractRenderingEngine { private static final String ENGINE_NAME = "helloWorldRenderingEngine"; - - public DummyHelloWorldRenditionEngine(ConfigurableApplicationContext ctx) - { - this.transactionService = (TransactionService)ctx.getBean("transactionService"); - this.attributeService = (AttributeService)ctx.getBean("attributeService"); - this.versionService = (VersionService)ctx.getBean("versionService"); - } - + /** * Loads this executor into the ApplicationContext, if it * isn't already there @@ -2633,7 +2624,7 @@ public class RenditionServiceIntegrationTest extends BaseAlfrescoSpringTest if(!ctx.containsBean(ENGINE_NAME)) { // Create, and do dependencies - DummyHelloWorldRenditionEngine hw = new DummyHelloWorldRenditionEngine(ctx); + DummyHelloWorldRenditionEngine hw = new DummyHelloWorldRenditionEngine(); hw.setRuntimeActionService( (RuntimeActionService)ctx.getBean("actionService") ); @@ -2662,8 +2653,7 @@ public class RenditionServiceIntegrationTest extends BaseAlfrescoSpringTest } @Override - protected void render(RenderingContext context) - { + protected void render(RenderingContext context) { ContentWriter contentWriter = context.makeContentWriter(); contentWriter.setMimetype("text/plain"); contentWriter.putContent( "Hello, world!" ); diff --git a/source/test-java/org/alfresco/repo/thumbnail/ThumbnailServiceImplTest.java b/source/test-java/org/alfresco/repo/thumbnail/ThumbnailServiceImplTest.java index 29be17829c..d4a592374a 100644 --- a/source/test-java/org/alfresco/repo/thumbnail/ThumbnailServiceImplTest.java +++ b/source/test-java/org/alfresco/repo/thumbnail/ThumbnailServiceImplTest.java @@ -27,15 +27,8 @@ package org.alfresco.repo.thumbnail; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import java.io.Serializable; -import java.nio.ByteBuffer; -import java.nio.channels.Channels; -import java.nio.channels.ReadableByteChannel; import java.util.ArrayList; import java.util.Collections; import java.util.Date; @@ -54,16 +47,12 @@ import org.alfresco.repo.content.transform.ContentTransformer; import org.alfresco.repo.content.transform.magick.ImageResizeOptions; import org.alfresco.repo.content.transform.magick.ImageTransformationOptions; import org.alfresco.repo.jscript.ClasspathScriptLocation; -import org.alfresco.repo.rendition.executer.AbstractRenderingEngine; -import org.alfresco.repo.rendition.executer.ImageRenderingEngine; import org.alfresco.repo.thumbnail.script.ScriptThumbnailService; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ActionCondition; -import org.alfresco.service.cmr.attributes.AttributeService; -import org.alfresco.service.cmr.rendition.RenditionDefinition; import org.alfresco.service.cmr.rendition.RenditionService; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentData; @@ -82,7 +71,6 @@ import org.alfresco.service.cmr.rule.RuleType; import org.alfresco.service.cmr.thumbnail.FailedThumbnailInfo; import org.alfresco.service.cmr.thumbnail.ThumbnailParentAssociationDetails; import org.alfresco.service.cmr.thumbnail.ThumbnailService; -import org.alfresco.service.cmr.version.VersionService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QNamePattern; @@ -92,7 +80,6 @@ import org.alfresco.test_category.OwnJVMTestsCategory; import org.alfresco.util.ApplicationContextHelper; import org.alfresco.util.BaseAlfrescoSpringTest; import org.alfresco.util.TempFileProvider; -import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.experimental.categories.Category; @@ -115,8 +102,6 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest private ScriptService scriptService; private MimetypeMap mimetypeMap; private TransactionService transactionService; - private VersionService versionService; - private AttributeService attributeService; private ServiceRegistry services; private FailureHandlingOptions failureHandlingOptions; @@ -142,10 +127,8 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest this.scriptThumbnailService = (ScriptThumbnailService) this.applicationContext.getBean("thumbnailServiceScript"); this.mimetypeMap = (MimetypeMap) this.applicationContext.getBean("mimetypeService"); this.scriptService = (ScriptService) this.applicationContext.getBean("ScriptService"); - this.attributeService = (AttributeService) this.applicationContext.getBean("attributeService"); this.services = (ServiceRegistry) this.applicationContext.getBean("ServiceRegistry"); this.transactionService = (TransactionService) this.applicationContext.getBean("transactionService"); - this.versionService = (VersionService) this.applicationContext.getBean("versionService"); this.failureHandlingOptions = (FailureHandlingOptions) this.applicationContext.getBean("standardFailureOptions"); // Create a folder and some content @@ -186,7 +169,7 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest { QName qname = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "doclib"); - final ThumbnailDefinition details = thumbnailService.getThumbnailRegistry().getThumbnailDefinition( + ThumbnailDefinition details = thumbnailService.getThumbnailRegistry().getThumbnailDefinition( qname.getLocalName()); assertEquals("doclib", details.getName()); assertEquals("image/png", details.getMimetype()); @@ -194,43 +177,21 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest checkTransformer(); - final NodeRef jpgOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_JPEG); - - setComplete(); - endTransaction(); - - final NodeRef thumbnail0 = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @Override - public NodeRef execute() throws Throwable - { - NodeRef thumbnail = thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, - MimetypeMap.MIMETYPE_IMAGE_JPEG, details.getTransformationOptions(), "doclib"); - return thumbnail; - } - }, false, true); + NodeRef jpgOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_JPEG); + NodeRef thumbnail0 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, + MimetypeMap.MIMETYPE_IMAGE_JPEG, details.getTransformationOptions(), "doclib"); assertNotNull(thumbnail0); - - transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @Override - public Void execute() throws Throwable - { - checkRenditioned(jpgOrig, Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "doclib", 1))); - checkRendition(jpgOrig, "doclib", thumbnail0); - outputThumbnailTempContentLocation(thumbnail0, "jpg", "doclib test"); - - return null; - } - }, false, true); + checkRenditioned(jpgOrig, Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "doclib", 1))); + checkRendition("doclib", thumbnail0); + outputThumbnailTempContentLocation(thumbnail0, "jpg", "doclib test"); } public void testCreateRenditionThumbnailFromPdf() throws Exception { QName qname = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "doclib"); - final ThumbnailDefinition details = thumbnailService.getThumbnailRegistry().getThumbnailDefinition( + ThumbnailDefinition details = thumbnailService.getThumbnailRegistry().getThumbnailDefinition( qname.getLocalName()); assertEquals("doclib", details.getName()); assertEquals("image/png", details.getMimetype()); @@ -238,36 +199,14 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest checkTransformer(); - final NodeRef pdfOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_PDF); - - setComplete(); - endTransaction(); - - final NodeRef thumbnail0 = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @Override - public NodeRef execute() throws Throwable - { - NodeRef thumbnail = thumbnailService.createThumbnail(pdfOrig, ContentModel.PROP_CONTENT, - MimetypeMap.MIMETYPE_IMAGE_JPEG, details.getTransformationOptions(), "doclib"); - return thumbnail; - } - }, false, true); + NodeRef pdfOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_PDF); + NodeRef thumbnail0 = this.thumbnailService.createThumbnail(pdfOrig, ContentModel.PROP_CONTENT, + MimetypeMap.MIMETYPE_IMAGE_JPEG, details.getTransformationOptions(), "doclib"); assertNotNull(thumbnail0); - - transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @Override - public Void execute() throws Throwable - { - checkRenditioned(pdfOrig, Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "doclib", 1))); - checkRendition(pdfOrig, "doclib", thumbnail0); - outputThumbnailTempContentLocation(thumbnail0, "jpg", "doclib test"); - - return null; - } - }, false, true); + checkRenditioned(pdfOrig, Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "doclib", 1))); + checkRendition("doclib", thumbnail0); + outputThumbnailTempContentLocation(thumbnail0, "jpg", "doclib test"); } public void testCreateRenditionThumbnailFromPdfPage2() throws Exception @@ -283,228 +222,119 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest checkTransformer(); - final NodeRef pdfOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_PDF); + NodeRef pdfOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_PDF); - final NodeRef thumbnail0 = this.thumbnailService.createThumbnail(pdfOrig, ContentModel.PROP_CONTENT, + NodeRef thumbnail0 = this.thumbnailService.createThumbnail(pdfOrig, ContentModel.PROP_CONTENT, MimetypeMap.MIMETYPE_IMAGE_JPEG, thumbnailDefinition.getTransformationOptions(), "doclib_2"); - - setComplete(); - endTransaction(); - assertNotNull(thumbnail0); - - transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @Override - public Void execute() throws Throwable - { - checkRenditioned(pdfOrig, Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "doclib_2", 1))); - checkRendition(pdfOrig, "doclib_2", thumbnail0); - - // Check the length - File tempFile = TempFileProvider.createTempFile("thumbnailServiceImplTest", ".jpg"); - ContentReader reader = contentService.getReader(thumbnail0, ContentModel.PROP_CONTENT); - - long size = reader.getSize(); - System.out.println("size=" + size); - assertTrue("Page 2 should be blank and less than 4500 bytes", size < 4500); - - reader.getContent(tempFile); - - return null; - } - }, false, true); + checkRenditioned(pdfOrig, Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "doclib_2", 1))); + checkRendition("doclib_2", thumbnail0); + + // Check the length + File tempFile = TempFileProvider.createTempFile("thumbnailServiceImplTest", ".jpg"); + ContentReader reader = this.contentService.getReader(thumbnail0, ContentModel.PROP_CONTENT); + + long size = reader.getSize(); + System.out.println("size=" + size); + assertTrue("Page 2 should be blank and less than 4500 bytes", size < 4500); + + reader.getContent(tempFile); + System.out.println("doclib_2 test: " + tempFile.getPath()); } public void testCreateThumbnailFromImage() throws Exception { checkTransformer(); - final NodeRef jpgOrig = createOriginalContent(folder, MimetypeMap.MIMETYPE_IMAGE_JPEG); - final NodeRef gifOrig = createOriginalContent(folder, MimetypeMap.MIMETYPE_IMAGE_GIF); - - setComplete(); - endTransaction(); + NodeRef jpgOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_JPEG); + NodeRef gifOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_GIF); // ===== small: 64x64, marked as thumbnail ==== - final ImageResizeOptions imageResizeOptions = new ImageResizeOptions(); + ImageResizeOptions imageResizeOptions = new ImageResizeOptions(); imageResizeOptions.setWidth(64); imageResizeOptions.setHeight(64); imageResizeOptions.setResizeToThumbnail(true); - final ImageTransformationOptions imageTransformationOptions = new ImageTransformationOptions(); + ImageTransformationOptions imageTransformationOptions = new ImageTransformationOptions(); imageTransformationOptions.setResizeOptions(imageResizeOptions); + // ThumbnailDetails createOptions = new ThumbnailDetails(); - final NodeRef thumbnail1 = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @Override - public NodeRef execute() throws Throwable - { - NodeRef thumbnail1 = thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, - MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions, "small"); - return thumbnail1; - } - }, false, true); - + NodeRef thumbnail1 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, + MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions, "small"); assertNotNull(thumbnail1); - - transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @Override - public Void execute() throws Throwable - { - checkRenditioned(jpgOrig, Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "small", 1))); - checkRendition(jpgOrig, "small", thumbnail1); - outputThumbnailTempContentLocation(thumbnail1, "jpg", "small - 64x64, marked as thumbnail"); - return null; - } - }, false, true); + checkRenditioned(jpgOrig, Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "small", 1))); + checkRendition("small", thumbnail1); + outputThumbnailTempContentLocation(thumbnail1, "jpg", "small - 64x64, marked as thumbnail"); // ===== small2: 64x64, aspect not maintained ==== - final ImageResizeOptions imageResizeOptions2 = new ImageResizeOptions(); + ImageResizeOptions imageResizeOptions2 = new ImageResizeOptions(); imageResizeOptions2.setWidth(64); imageResizeOptions2.setHeight(64); imageResizeOptions2.setMaintainAspectRatio(false); - final ImageTransformationOptions imageTransformationOptions2 = new ImageTransformationOptions(); + ImageTransformationOptions imageTransformationOptions2 = new ImageTransformationOptions(); imageTransformationOptions2.setResizeOptions(imageResizeOptions2); - - final NodeRef thumbnail2 = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @Override - public NodeRef execute() throws Throwable - { - NodeRef thumbnail2 = thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, - MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions2, "small2"); - return thumbnail2; - } - }, false, true); - - transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @Override - public Void execute() throws Throwable - { - checkRenditioned(jpgOrig, Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "small2", 1))); - checkRendition(jpgOrig, "small2", thumbnail2); - outputThumbnailTempContentLocation(thumbnail2, "jpg", "small2 - 64x64, aspect not maintained"); - - return null; - } - }, false, true); + // ThumbnailDetails createOptions2 = new ThumbnailDetails(); + NodeRef thumbnail2 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, + MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions2, "small2"); + checkRenditioned(jpgOrig, Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "small2", 1))); + checkRendition("small2", thumbnail2); + outputThumbnailTempContentLocation(thumbnail2, "jpg", "small2 - 64x64, aspect not maintained"); // ===== half: 50%x50 ===== - final ImageResizeOptions imageResizeOptions3 = new ImageResizeOptions(); + ImageResizeOptions imageResizeOptions3 = new ImageResizeOptions(); imageResizeOptions3.setWidth(50); imageResizeOptions3.setHeight(50); imageResizeOptions3.setPercentResize(true); - final ImageTransformationOptions imageTransformationOptions3 = new ImageTransformationOptions(); + ImageTransformationOptions imageTransformationOptions3 = new ImageTransformationOptions(); imageTransformationOptions3.setResizeOptions(imageResizeOptions3); - - final NodeRef thumbnail3 = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @Override - public NodeRef execute() throws Throwable - { - NodeRef thumbnail3 = thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, - MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions3, "half"); - return thumbnail3; - } - }, false, true); - - transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @Override - public Void execute() throws Throwable - { - checkRenditioned(jpgOrig, - Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "half", 1))); - checkRendition(jpgOrig, "half", thumbnail3); - outputThumbnailTempContentLocation(thumbnail3, "jpg", "half - 50%x50%"); - - return null; - } - }, false, true); + // ThumbnailDetails createOptions3 = new ThumbnailDetails(); + NodeRef thumbnail3 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, + MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions3, "half"); + checkRenditioned(jpgOrig, + Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "half", 1))); + checkRendition("half", thumbnail3); + outputThumbnailTempContentLocation(thumbnail3, "jpg", "half - 50%x50%"); // ===== half2: 50%x50 from gif ===== - final ImageResizeOptions imageResizeOptions4 = new ImageResizeOptions(); + ImageResizeOptions imageResizeOptions4 = new ImageResizeOptions(); imageResizeOptions4.setWidth(50); imageResizeOptions4.setHeight(50); imageResizeOptions4.setPercentResize(true); - final ImageTransformationOptions imageTransformationOptions4 = new ImageTransformationOptions(); + ImageTransformationOptions imageTransformationOptions4 = new ImageTransformationOptions(); imageTransformationOptions4.setResizeOptions(imageResizeOptions4); - - final NodeRef thumbnail4 = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @Override - public NodeRef execute() throws Throwable - { - NodeRef thumbnail4 = thumbnailService.createThumbnail(gifOrig, ContentModel.PROP_CONTENT, - MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions4, "half2"); - return thumbnail4; - } - }, false, true); - - transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @Override - public Void execute() throws Throwable - { - checkRenditioned(gifOrig, Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "half2", 1))); - checkRendition(gifOrig, "half2", thumbnail4); - outputThumbnailTempContentLocation(thumbnail4, "jpg", "half2 - 50%x50%, from gif"); - - return null; - } - }, false, true); + // ThumbnailDetails createOptions4 = new ThumbnailDetails(); + NodeRef thumbnail4 = this.thumbnailService.createThumbnail(gifOrig, ContentModel.PROP_CONTENT, + MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions4, "half2"); + checkRenditioned(gifOrig, Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "half2", 1))); + checkRendition("half2", thumbnail4); + outputThumbnailTempContentLocation(thumbnail4, "jpg", "half2 - 50%x50%, from gif"); } public void testDuplicationNames() throws Exception { checkTransformer(); - final NodeRef jpgOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_JPEG); - - setComplete(); - endTransaction(); - - final ImageResizeOptions imageResizeOptions = new ImageResizeOptions(); + NodeRef jpgOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_JPEG); + ImageResizeOptions imageResizeOptions = new ImageResizeOptions(); imageResizeOptions.setWidth(64); imageResizeOptions.setHeight(64); imageResizeOptions.setResizeToThumbnail(true); - final ImageTransformationOptions imageTransformationOptions = new ImageTransformationOptions(); + ImageTransformationOptions imageTransformationOptions = new ImageTransformationOptions(); imageTransformationOptions.setResizeOptions(imageResizeOptions); - - final NodeRef thumbnail1 = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @Override - public NodeRef execute() throws Throwable - { - NodeRef thumbnail1 = thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, - MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions, "small"); - return thumbnail1; - } - }, false, true); - + // ThumbnailDetails createOptions = new ThumbnailDetails(); + NodeRef thumbnail1 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, + MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions, "small"); assertNotNull(thumbnail1); checkRenditioned(jpgOrig, Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "small", 1))); - checkRendition(jpgOrig, "small", thumbnail1); + checkRendition("small", thumbnail1); // the origional thumbnail is returned if we are attempting to create a duplicate - final NodeRef duplicate = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @Override - public NodeRef execute() throws Throwable - { - NodeRef thumbnail = thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, MimetypeMap.MIMETYPE_IMAGE_JPEG, + NodeRef duplicate = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions, "small"); - return thumbnail; - } - }, false, true); - assertNotNull(duplicate); assertEquals(duplicate, thumbnail1); } @@ -758,96 +588,6 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest assertEquals(ContentModel.TYPE_THUMBNAIL, secureNodeService.getType(thumbnail1)); } - private boolean isEqual(InputStream i1, InputStream i2) throws IOException - { - ReadableByteChannel ch1 = Channels.newChannel(i1); - ReadableByteChannel ch2 = Channels.newChannel(i2); - - ByteBuffer buf1 = ByteBuffer.allocateDirect(1024); - ByteBuffer buf2 = ByteBuffer.allocateDirect(1024); - - try { - while (true) { - - int n1 = ch1.read(buf1); - int n2 = ch2.read(buf2); - - if (n1 == -1 || n2 == -1) return n1 == n2; - - buf1.flip(); - buf2.flip(); - - for (int i = 0; i < Math.min(n1, n2); i++) - if (buf1.get() != buf2.get()) - return false; - - buf1.compact(); - buf2.compact(); - } - - } finally { - if (i1 != null) i1.close(); - if (i2 != null) i2.close(); - } - } - - /** - * Test that multiple updates to source content generate different thumbnails. - * - * @throws Exception - */ - public void testMultipleThumbnailUpdates() throws Exception - { - checkTransformer(); - - NodeRef content = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_JPEG); - - assertEquals(MimetypeMap.MIMETYPE_IMAGE_JPEG, - contentService.getReader(content, ContentModel.PROP_CONTENT).getMimetype()); - - // Create a thumbnail - ImageResizeOptions imageResizeOptions = new ImageResizeOptions(); - imageResizeOptions.setWidth(64); - imageResizeOptions.setHeight(64); - imageResizeOptions.setResizeToThumbnail(true); - ImageTransformationOptions imageTransformationOptions = new ImageTransformationOptions(); - imageTransformationOptions.setResizeOptions(imageResizeOptions); - NodeRef thumbnail = this.thumbnailService.createThumbnail(content, ContentModel.PROP_CONTENT, - MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions, "small"); - - // Thumbnails should always be of type cm:thumbnail. - assertEquals(ContentModel.TYPE_THUMBNAIL, secureNodeService.getType(thumbnail)); - - // make a copy of the thumbnail content - ContentReader thumbnailReader = contentService.getReader(thumbnail, ContentModel.PROP_CONTENT); - InputStream thumbnailStream = thumbnailReader.getContentInputStream(); - File file = TempFileProvider.createTempFile(getClass().getName(), "jpg"); - OutputStream out = new FileOutputStream(file); - IOUtils.copy(thumbnailStream, out); - InputStream oldThumbnailStream = new FileInputStream(file); - - // update the source node content - file = AbstractContentTransformerTest.loadNamedQuickTestFile("quickGEO.jpg"); - ContentWriter writer = this.contentService.getWriter(content, ContentModel.PROP_CONTENT, true); - writer.setMimetype(MimetypeMap.MIMETYPE_IMAGE_JPEG); - writer.setEncoding("UTF-8"); - writer.putContent(file); - assertEquals(MimetypeMap.MIMETYPE_IMAGE_JPEG, - contentService.getReader(content, ContentModel.PROP_CONTENT).getMimetype()); - - // update the thumbnail - this.thumbnailService.updateThumbnail(thumbnail, imageTransformationOptions); - - // Thumbnails should always be of type cm:thumbnail. - assertEquals(ContentModel.TYPE_THUMBNAIL, secureNodeService.getType(thumbnail)); - - thumbnailReader = contentService.getReader(thumbnail, ContentModel.PROP_CONTENT); - thumbnailStream = thumbnailReader.getContentInputStream(); - - // the thumbnail content should be different - assertFalse(isEqual(oldThumbnailStream, thumbnailStream)); - } - public void testGetThumbnailByName() throws Exception { checkTransformer(); @@ -868,13 +608,10 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions, "small"); - setComplete(); - endTransaction(); - // Try and retrieve the thumbnail NodeRef result2 = this.thumbnailService.getThumbnailByName(jpgOrig, ContentModel.PROP_CONTENT, "small"); assertNotNull(result2); - checkRendition(jpgOrig, "small", result2); + checkRendition("small", result2); // Check for an other thumbnail that doesn't exist NodeRef result3 = this.thumbnailService.getThumbnailByName(jpgOrig, ContentModel.PROP_CONTENT, "anotherone"); @@ -884,23 +621,14 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest private static class ExpectedAssoc { private QNamePattern assocTypeQName; - private QNamePattern assocName; + private String assocName; private int count; public ExpectedAssoc(QNamePattern assocTypeQName, String assocName, int count) { super(); - this.assocName = assocName != null - ? QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, assocName) : null; this.assocTypeQName = assocTypeQName; - this.count = count; - } - - public ExpectedAssoc(QNamePattern assocTypeQName, QNamePattern assocName, int count) - { - super(); this.assocName = assocName; - this.assocTypeQName = assocTypeQName; this.count = count; } @@ -909,7 +637,7 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest return assocTypeQName; } - public QNamePattern getAssocName() + public String getAssocName() { return assocName; } @@ -964,21 +692,21 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest } } - private void checkRenditioned(NodeRef contentNodeRef, List expectedAssocs) - { + private void checkRenditioned(NodeRef contentNodeRef, List expectedAssocs) { assertTrue("Renditioned aspect should have been applied", this.secureNodeService.hasAspect(contentNodeRef, RenditionModel.ASPECT_RENDITIONED)); - for (ExpectedAssoc expectedAssoc : expectedAssocs) - { + for (ExpectedAssoc expectedAssoc : expectedAssocs) { + QNamePattern qNamePattern = expectedAssoc.getAssocName() != null + ? QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, expectedAssoc.getAssocName()) : null; List assocs = this.secureNodeService.getChildAssocs(contentNodeRef, - expectedAssoc.getAssocTypeQName(), expectedAssoc.getAssocName()); + expectedAssoc.getAssocTypeQName(), qNamePattern); assertNotNull(assocs); assertEquals(expectedAssoc + " association count mismatch", expectedAssoc.getCount(), assocs.size()); } } - private void checkRendition(final NodeRef sourceNode, final String thumbnailName, final NodeRef thumbnail) + private void checkRendition(String thumbnailName, NodeRef thumbnail) { // Check the thumbnail is of the correct type assertTrue("Thumbnail should have been a rendition", @@ -1004,29 +732,6 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest ContentData thumbnailData = (ContentData) secureNodeService.getProperty(thumbnail, ContentModel.PROP_CONTENT); assertNotNull("Thumbnail data was null", thumbnailData); assertTrue("Thumbnail data was empty", thumbnailData.getSize() > 0); - - if(sourceNode != null) - { - final String renderedContentKey = AbstractRenderingEngine.getRenderedContentKey(sourceNode, versionService); - QName renditionName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, thumbnailName); - final RenditionDefinition renditionDef = renditionService.createRenditionDefinition(renditionName, - ImageRenderingEngine.NAME); - - final QName thumbnailNameQName = thumbnailName != null ? - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, thumbnailName) : null; - logger.debug("checkedRendition: " + sourceNode + " " + thumbnailNameQName); - String contentUrl = transactionService.getRetryingTransactionHelper() - .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public String execute() throws Throwable - { - String contentUrl = (String)attributeService.getAttribute("RENDITIONED_CONTENT", renderedContentKey, - renditionDef.getRenditionName()); - return contentUrl; - }; - }, false, true); - assertNull("Cached rendition contentUrl was not cleaned up", contentUrl); - } } private void outputThumbnailTempContentLocation(NodeRef thumbnail, String ext, String message) throws IOException @@ -1064,18 +769,7 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest return node; } - - private void updateContent(NodeRef node, String mimetype) throws IOException - { - String ext = this.mimetypeMap.getExtension(mimetype); - File file = AbstractContentTransformerTest.loadNamedQuickTestFile("quickGEO.jpg"); - - ContentWriter writer = this.contentService.getWriter(node, ContentModel.PROP_CONTENT, true); - writer.setMimetype(mimetype); - writer.setEncoding("UTF-8"); - writer.putContent(file); - } - + private NodeRef createCorruptedContent(NodeRef parentFolder) throws IOException { // The below pdf file has been truncated such that it is identifiable as a PDF but otherwise corrupt. @@ -1176,74 +870,33 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest .getChildRef(); checkTransformer(); - final NodeRef jpgOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_JPEG); - - setComplete(); - endTransaction(); - - final ImageResizeOptions imageResizeOptions = new ImageResizeOptions(); + NodeRef jpgOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_JPEG); + + ImageResizeOptions imageResizeOptions = new ImageResizeOptions(); imageResizeOptions.setWidth(64); imageResizeOptions.setHeight(64); imageResizeOptions.setResizeToThumbnail(true); - final ImageTransformationOptions imageTransformationOptions = new ImageTransformationOptions(); + ImageTransformationOptions imageTransformationOptions = new ImageTransformationOptions(); imageTransformationOptions.setResizeOptions(imageResizeOptions); - - final NodeRef thumbnail1 = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @Override - public NodeRef execute() throws Throwable - { - // Create thumbnail - same MIME type - NodeRef thumbnail = thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, - MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions, "smallJpeg"); - return thumbnail; - } - }, false, true); - + + // Create thumbnail - same MIME type + NodeRef thumbnail1 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, + MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions, "smallJpeg"); assertNotNull(thumbnail1); - - transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @Override - public Void execute() throws Throwable - { - checkRenditioned(jpgOrig, - Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "smallJpeg", 1))); - checkRendition(jpgOrig, "smallJpeg", thumbnail1); - outputThumbnailTempContentLocation(thumbnail1, "jpg", "smallJpeg - 64x64, marked as thumbnail"); - return null; - } - }, false, true); + checkRenditioned(jpgOrig, + Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "smallJpeg", 1))); + checkRendition("smallJpeg", thumbnail1); + outputThumbnailTempContentLocation(thumbnail1, "jpg", "smallJpeg - 64x64, marked as thumbnail"); // Create thumbnail - different MIME type - final NodeRef thumbnail2 = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @Override - public NodeRef execute() throws Throwable - { - // Create thumbnail - same MIME type - NodeRef thumbnail = thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, - MimetypeMap.MIMETYPE_IMAGE_PNG, imageTransformationOptions, "smallPng"); - return thumbnail; - } - }, false, true); - - assertNotNull(thumbnail2); - - transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @Override - public Void execute() throws Throwable - { - checkRenditioned(jpgOrig, - Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "smallPng", 1))); - checkRendition(jpgOrig, "smallPng", thumbnail2); - outputThumbnailTempContentLocation(thumbnail2, "png", "smallPng - 64x64, marked as thumbnail"); - - return null; - } - }, false, true); - + thumbnail1 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, + MimetypeMap.MIMETYPE_IMAGE_PNG, imageTransformationOptions, "smallPng"); + assertNotNull(thumbnail1); + checkRenditioned(jpgOrig, + Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "smallPng", 1))); + checkRendition("smallPng", thumbnail1); + outputThumbnailTempContentLocation(thumbnail1, "png", "smallPng - 64x64, marked as thumbnail"); + // Create thumbnail - different content property // TODO @@ -1253,18 +906,8 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest try { imageTransformationOptions.setCommandOptions("-noSuchOption"); - - transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @Override - public Void execute() throws Throwable - { - // Create thumbnail - same MIME type - thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, - MimetypeMap.MIMETYPE_IMAGE_PNG, imageTransformationOptions, "smallCO"); - return null; - } - }, false, true); + thumbnail1 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, + MimetypeMap.MIMETYPE_IMAGE_PNG, imageTransformationOptions, "smallCO"); } catch (ContentIOException ciox) { x = ciox; @@ -1276,46 +919,28 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest // Create thumbnail - different target assoc details - final ThumbnailParentAssociationDetails tpad + ThumbnailParentAssociationDetails tpad = new ThumbnailParentAssociationDetails(otherFolder, QName.createQName(NamespaceService.RENDITION_MODEL_1_0_URI, "foo"), QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "bar")); - final NodeRef thumbnail4 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, + thumbnail1 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, MimetypeMap.MIMETYPE_IMAGE_PNG, imageTransformationOptions, "targetDetails", tpad); - assertNotNull(thumbnail4); - - transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @Override - public Void execute() throws Throwable - { - checkRenditioned(jpgOrig, - Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "targetDetails", 1))); - checkRendition(jpgOrig, "targetDetails", thumbnail4); - outputThumbnailTempContentLocation(thumbnail4, "png", "targetDetails - 64x64, marked as thumbnail"); - - return null; - } - }, false, true); + assertNotNull(thumbnail1); + checkRenditioned(jpgOrig, + Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "targetDetails", 1))); + checkRendition("targetDetails", thumbnail1); + outputThumbnailTempContentLocation(thumbnail1, "png", "targetDetails - 64x64, marked as thumbnail"); + + // Create thumbnail - null thumbnail name - final NodeRef thumbnail5 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, + thumbnail1 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, MimetypeMap.MIMETYPE_IMAGE_PNG, imageTransformationOptions, null); - assertNotNull(thumbnail5); - - transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @Override - public Void execute() throws Throwable - { - // we expected 4 rendition associations - checkRenditioned(jpgOrig, Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, (QNamePattern)null, 4))); - checkRendition(null, null, thumbnail5); - outputThumbnailTempContentLocation(thumbnail5, "png", "'null' - 64x64, marked as thumbnail"); - - return null; - } - }, false, true); + assertNotNull(thumbnail1); + checkRenditioned(jpgOrig, + Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, null, 5))); + checkRendition(null, thumbnail1); + outputThumbnailTempContentLocation(thumbnail1, "png", "'null' - 64x64, marked as thumbnail"); } public void testRegistry() @@ -1380,8 +1005,9 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest assertEquals(standardMediumIcon, mediumIcon); } - protected void performLongRunningThumbnailTest(final List expectedThumbnails, final List expectedAssocs, - final LongRunningConcurrentWork concurrentWork, final Integer retryPeriod, final Integer quietPeriod) throws Exception + protected void performLongRunningThumbnailTest(final List expectedThumbnails, + final List expectedAssocs, final LongRunningConcurrentWork concurrentWork, + final Integer retryPeriod, final Integer quietPeriod) throws Exception { long saveRetryPeriod = failureHandlingOptions.getRetryPeriod(); long saveQuietPeriod = failureHandlingOptions.getQuietPeriod(); @@ -1415,8 +1041,7 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest } // Create our thumbnail(s) - for (ExpectedThumbnail expectedThumbnail : expectedThumbnails) - { + for (ExpectedThumbnail expectedThumbnail : expectedThumbnails) { ThumbnailDefinition thumbnailDef = thumbnailService.getThumbnailRegistry() .getThumbnailDefinition(expectedThumbnail.getThumbnailName()); @@ -1431,15 +1056,13 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest // Thumbnailing process(es) are running in other threads, do the // concurrent work here - if (concurrentWork != null) - { + if (concurrentWork != null) { logger.debug("Starting concurrent work for " + source); concurrentWork.run(source); } // Verify our concurrent work ran successfully - if (concurrentWork != null) - { + if (concurrentWork != null) { logger.debug("Verifying concurrent work for " + source); concurrentWork.verify(source); } @@ -1448,21 +1071,17 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest // Wait for thumbnail(s) to finish long endTime = (new Date()).getTime(); - for (final ExpectedThumbnail expectedThumbnail : expectedThumbnails) - { + for (final ExpectedThumbnail expectedThumbnail : expectedThumbnails) { NodeRef thumbnail = null; - while ((endTime - startTime) < (TEST_LONG_RUNNING_TRANSFORM_TIME * numIterations)) - { + while ((endTime - startTime) < (TEST_LONG_RUNNING_TRANSFORM_TIME * numIterations)) { thumbnail = transactionService.getRetryingTransactionHelper() - .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { + .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { public NodeRef execute() throws Throwable { return thumbnailService.getThumbnailByName(source, ContentModel.PROP_CONTENT, expectedThumbnail.getThumbnailName()); } }, false, true); - if (thumbnail == null) - { + if (thumbnail == null) { Thread.sleep(200); logger.debug("Elapsed " + (endTime - startTime) + " ms of " + TEST_LONG_RUNNING_TRANSFORM_TIME * numIterations + " ms waiting for " @@ -1477,33 +1096,27 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest } transactionService.getRetryingTransactionHelper() - .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Void execute() throws Throwable - { + .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { + public Void execute() throws Throwable { // Verify that the thumbnail(s) was/were created - for (final ExpectedThumbnail expectedThumbnail : expectedThumbnails) - { + for (final ExpectedThumbnail expectedThumbnail : expectedThumbnails) { String thumbnailName = expectedThumbnail.getThumbnailName(); NodeRef thumbnailNodeRef = thumbnailService.getThumbnailByName(source, ContentModel.PROP_CONTENT, thumbnailName); - checkRendition(source, thumbnailName, thumbnailNodeRef); + checkRendition(thumbnailName, thumbnailNodeRef); } // verify associations checkRenditioned(source, expectedAssocs); return null; - }; - }); - - // we expect the transformer to run once for each thumbnail - assertEquals(expectedThumbnails.size(), transformer.getTransformCount()); + }; + }); } finally { - failureHandlingOptions.setRetryPeriod(saveRetryPeriod); - failureHandlingOptions.setQuietPeriod(saveQuietPeriod); + failureHandlingOptions.setRetryPeriod(saveRetryPeriod); + failureHandlingOptions.setQuietPeriod(saveQuietPeriod); } } @@ -1516,9 +1129,10 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest public void testLongRunningThumbnails() throws Exception { logger.debug("Starting testLongRunningThumbnails"); - performLongRunningThumbnailTest(Collections.singletonList(ExpectedThumbnail.withName("imgpreview")), - Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "imgpreview", 1)), - new EmptyLongRunningConcurrentWork(), 60, null); + performLongRunningThumbnailTest( + Collections.singletonList(ExpectedThumbnail.withName("imgpreview")), + Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "imgpreview", 1)), + new EmptyLongRunningConcurrentWork(), 60, null); } /** @@ -1552,18 +1166,22 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest } }; - // we expect an imgpreview thumbnail association but no failed thumbnail association - List expectedAssocs = new ArrayList<>(2); - expectedAssocs.add(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "imgpreview", 1)); - expectedAssocs.add(new ExpectedAssoc(ContentModel.ASSOC_FAILED_THUMBNAIL, RegexQNamePattern.MATCH_ALL, 0)); - performLongRunningThumbnailTest( - Collections.singletonList(ExpectedThumbnail.withName("imgpreview")), expectedAssocs, updatePropertyWork, 1, null); + Collections.singletonList(ExpectedThumbnail.withName("imgpreview")), + Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "imgpreview", 1)), + updatePropertyWork, 1, null); } /** * Verifies that multiple thumbnails can be successfully created. * + * Note: the current architecture of the thumbnail and rendition services can't guarantee that there + * won't be failed thumbnails for the scenario covered by this test. In particular, given long-running + * thumbnails/renditions, the concurrent creation of more than one thumbnail may fail with a primary key constraint + * exception because both transactions try to add the same aspect to the same parent content node. Whilst + * the retrying transaction handler correctly handles this scenario, the {@link org.alfresco.service.cmr.action.ActionService} + * incorrectly generates a compensating action (failed thumbnail) when in fact the thumbnail creation is recoverable. + * * @throws Exception */ public void testCreateMultipleLongRunningThumbnails() throws Exception @@ -1580,6 +1198,7 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest List expectedAssocs = new ArrayList<>(5); expectedAssocs.add(new ExpectedAssoc(RenditionModel.ASSOC_RENDITION, "imgpreview", 1)); expectedAssocs.add(new ExpectedAssoc(RenditionModel.ASSOC_RENDITION, "avatar", 1)); +// expectedAssocs.add(new ExpectedAssoc(ContentModel.ASSOC_FAILED_THUMBNAIL, null, 1)); performLongRunningThumbnailTest(expectedThumbnails, expectedAssocs, new EmptyLongRunningConcurrentWork(), 1, 1); }