mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-10-08 14:51:49 +00:00
Merged DEV/SG/MNT15135 to 5.2.N
MNT-15135 "Alfresco Media Management: Rendition Concurrency Failure on Property update" git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@130692 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -122,6 +122,15 @@
|
|||||||
<property name="contentService">
|
<property name="contentService">
|
||||||
<ref bean="ContentService" />
|
<ref bean="ContentService" />
|
||||||
</property>
|
</property>
|
||||||
|
<property name="versionService">
|
||||||
|
<ref bean="versionService" />
|
||||||
|
</property>
|
||||||
|
<property name="attributeService">
|
||||||
|
<ref bean="AttributeService" />
|
||||||
|
</property>
|
||||||
|
<property name="transactionService">
|
||||||
|
<ref bean="transactionService" />
|
||||||
|
</property>
|
||||||
<property name="applicableTypes">
|
<property name="applicableTypes">
|
||||||
<list>
|
<list>
|
||||||
<value>{http://www.alfresco.org/model/content/1.0}content</value>
|
<value>{http://www.alfresco.org/model/content/1.0}content</value>
|
||||||
|
@@ -25,60 +25,69 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.repo.rendition.executer;
|
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_DESTINATION_PATH_TEMPLATE;
|
||||||
import static org.alfresco.service.cmr.rendition.RenditionService.PARAM_IS_COMPONENT_RENDITION;
|
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_ORPHAN_EXISTING_RENDITION;
|
||||||
import static org.alfresco.service.cmr.rendition.RenditionService.PARAM_RENDITION_NODETYPE;
|
import static org.alfresco.service.cmr.rendition.RenditionService.PARAM_RENDITION_NODETYPE;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import org.alfresco.error.AlfrescoRuntimeException;
|
|
||||||
import org.alfresco.model.ContentModel;
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
import org.alfresco.model.RenditionModel;
|
import org.alfresco.model.ContentModel;
|
||||||
import org.alfresco.repo.action.ParameterDefinitionImpl;
|
import org.alfresco.model.RenditionModel;
|
||||||
import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;
|
import org.alfresco.repo.action.ParameterDefinitionImpl;
|
||||||
import org.alfresco.repo.content.MimetypeMap;
|
import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;
|
||||||
import org.alfresco.repo.content.transform.UnimportantTransformException;
|
import org.alfresco.repo.content.MimetypeMap;
|
||||||
import org.alfresco.repo.nodelocator.NodeLocator;
|
import org.alfresco.repo.content.transform.UnimportantTransformException;
|
||||||
import org.alfresco.repo.nodelocator.SelfNodeLocator;
|
import org.alfresco.repo.nodelocator.NodeLocator;
|
||||||
import org.alfresco.repo.policy.BehaviourFilter;
|
import org.alfresco.repo.nodelocator.SelfNodeLocator;
|
||||||
import org.alfresco.repo.rendition.RenderingEngineDefinitionImpl;
|
import org.alfresco.repo.policy.BehaviourFilter;
|
||||||
import org.alfresco.repo.rendition.RenditionDefinitionImpl;
|
import org.alfresco.repo.rendition.RenderingEngineDefinitionImpl;
|
||||||
import org.alfresco.repo.rendition.RenditionLocation;
|
import org.alfresco.repo.rendition.RenditionDefinitionImpl;
|
||||||
import org.alfresco.repo.rendition.RenditionLocationResolver;
|
import org.alfresco.repo.rendition.RenditionLocation;
|
||||||
import org.alfresco.repo.rendition.RenditionNodeManager;
|
import org.alfresco.repo.rendition.RenditionLocationResolver;
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
import org.alfresco.repo.rendition.RenditionNodeManager;
|
||||||
import org.alfresco.service.cmr.action.Action;
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||||
import org.alfresco.service.cmr.action.ActionDefinition;
|
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||||
import org.alfresco.service.cmr.action.ActionServiceException;
|
import org.alfresco.repo.transaction.TransactionalResourceHelper;
|
||||||
import org.alfresco.service.cmr.action.ActionTrackingService;
|
import org.alfresco.service.cmr.action.Action;
|
||||||
import org.alfresco.service.cmr.action.ExecutionSummary;
|
import org.alfresco.service.cmr.action.ActionDefinition;
|
||||||
import org.alfresco.service.cmr.action.ParameterDefinition;
|
import org.alfresco.service.cmr.action.ActionServiceException;
|
||||||
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
import org.alfresco.service.cmr.action.ActionTrackingService;
|
||||||
import org.alfresco.service.cmr.rendition.RenderCallback;
|
import org.alfresco.service.cmr.action.ExecutionSummary;
|
||||||
import org.alfresco.service.cmr.rendition.RenditionDefinition;
|
import org.alfresco.service.cmr.action.ParameterDefinition;
|
||||||
import org.alfresco.service.cmr.rendition.RenditionService;
|
import org.alfresco.service.cmr.attributes.AttributeService;
|
||||||
import org.alfresco.service.cmr.rendition.RenditionServiceException;
|
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
||||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
import org.alfresco.service.cmr.rendition.RenderCallback;
|
||||||
import org.alfresco.service.cmr.repository.ContentReader;
|
import org.alfresco.service.cmr.rendition.RenditionDefinition;
|
||||||
import org.alfresco.service.cmr.repository.ContentService;
|
import org.alfresco.service.cmr.rendition.RenditionService;
|
||||||
import org.alfresco.service.cmr.repository.ContentWriter;
|
import org.alfresco.service.cmr.rendition.RenditionServiceException;
|
||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||||
import org.alfresco.service.cmr.repository.NodeService;
|
import org.alfresco.service.cmr.repository.ContentReader;
|
||||||
import org.alfresco.service.cmr.repository.SerializedTransformationOptionsAccessor;
|
import org.alfresco.service.cmr.repository.ContentService;
|
||||||
import org.alfresco.service.namespace.NamespaceException;
|
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||||
import org.alfresco.service.namespace.NamespaceService;
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
import org.alfresco.util.GUID;
|
import org.alfresco.service.cmr.repository.SerializedTransformationOptionsAccessor;
|
||||||
import org.apache.commons.logging.Log;
|
import org.alfresco.service.cmr.version.Version;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.alfresco.service.cmr.version.VersionService;
|
||||||
import org.springframework.extensions.surf.util.I18NUtil;
|
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 com.sun.star.lang.NullPointerException;
|
import com.sun.star.lang.NullPointerException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -89,11 +98,13 @@ import com.sun.star.lang.NullPointerException;
|
|||||||
* @author Nick Smith
|
* @author Nick Smith
|
||||||
* @since 3.3
|
* @since 3.3
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractRenderingEngine extends ActionExecuterAbstractBase
|
public abstract class AbstractRenderingEngine extends ActionExecuterAbstractBase implements TransactionListener
|
||||||
{
|
{
|
||||||
|
|
||||||
/** Logger */
|
/** Logger */
|
||||||
private static Log logger = LogFactory.getLog(AbstractRenderingEngine.class);
|
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 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();
|
protected static final String DEFAULT_RUN_AS_NAME = AuthenticationUtil.getSystemUserName();
|
||||||
@@ -144,7 +155,10 @@ public abstract class AbstractRenderingEngine extends ActionExecuterAbstractBase
|
|||||||
/* Injected Services */
|
/* Injected Services */
|
||||||
protected ContentService contentService;
|
protected ContentService contentService;
|
||||||
protected MimetypeMap mimetypeMap;
|
protected MimetypeMap mimetypeMap;
|
||||||
protected ActionTrackingService actionTrackingService;
|
protected ActionTrackingService actionTrackingService;
|
||||||
|
protected AttributeService attributeService;
|
||||||
|
protected TransactionService transactionService;
|
||||||
|
protected VersionService versionService;
|
||||||
|
|
||||||
/* Parameter names common to all Rendering Actions */
|
/* Parameter names common to all Rendering Actions */
|
||||||
/**
|
/**
|
||||||
@@ -215,7 +229,22 @@ public abstract class AbstractRenderingEngine extends ActionExecuterAbstractBase
|
|||||||
|
|
||||||
private final NodeLocator temporaryParentNodeLocator;
|
private final NodeLocator temporaryParentNodeLocator;
|
||||||
private final QName temporaryRenditionLinkType;
|
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.
|
* Injects the nodeService bean.
|
||||||
*
|
*
|
||||||
@@ -526,11 +555,13 @@ public abstract class AbstractRenderingEngine extends ActionExecuterAbstractBase
|
|||||||
|
|
||||||
RenderingContext context = new RenderingContext(sourceNode,
|
RenderingContext context = new RenderingContext(sourceNode,
|
||||||
renditionDefinition,
|
renditionDefinition,
|
||||||
targetContentProp);
|
targetContentProp);
|
||||||
render(context);
|
|
||||||
|
render(context);
|
||||||
|
|
||||||
// This is a workaround for the fact that actions don't have return
|
// This is a workaround for the fact that actions don't have return
|
||||||
// values.
|
// values.
|
||||||
action.getParameterValues().put(PARAM_RESULT, context.getChildAssociationRef());
|
action.getParameterValues().put(PARAM_RESULT, context.getChildAssociationRef());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -759,111 +790,220 @@ public abstract class AbstractRenderingEngine extends ActionExecuterAbstractBase
|
|||||||
result = defaultValue;
|
result = defaultValue;
|
||||||
return result;
|
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<Void>()
|
||||||
|
{
|
||||||
|
@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> T getCheckedParam(String paramName, Class<T> clazz)
|
||||||
|
{
|
||||||
|
return AbstractRenderingEngine.getCheckedParam(paramName, clazz, definition);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> 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> T getCheckedParam(String paramName, Class<T> clazz)
|
|
||||||
{
|
|
||||||
return AbstractRenderingEngine.getCheckedParam(paramName, clazz, definition);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> 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)
|
protected void tagSourceNodeAsRenditioned(final RenditionDefinition renditionDef, final NodeRef actionedUponNodeRef)
|
||||||
{
|
{
|
||||||
// Adds the 'Renditioned' aspect to the source node if it
|
// Adds the 'Renditioned' aspect to the source node if it
|
||||||
@@ -1120,5 +1260,69 @@ public abstract class AbstractRenderingEngine extends ActionExecuterAbstractBase
|
|||||||
"multiple instances of the same action");
|
"multiple instances of the same action");
|
||||||
}
|
}
|
||||||
return executionSummaries.iterator().next();
|
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<RenderingContext> renderingContexts = TransactionalResourceHelper.getSet(RENDERING_CONTEXTS);
|
||||||
|
|
||||||
|
if(logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Cleaning up " + renderingContexts.size() + " rendering contexts");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!renderingContexts.isEmpty())
|
||||||
|
{
|
||||||
|
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Void>()
|
||||||
|
{
|
||||||
|
@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()
|
||||||
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -26,43 +26,50 @@
|
|||||||
|
|
||||||
package org.alfresco.repo.rendition.executer;
|
package org.alfresco.repo.rendition.executer;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.FutureTask;
|
import java.util.concurrent.FutureTask;
|
||||||
|
|
||||||
import org.alfresco.repo.action.ParameterDefinitionImpl;
|
import org.alfresco.repo.action.ParameterDefinitionImpl;
|
||||||
import org.alfresco.repo.content.transform.ContentTransformer;
|
import org.alfresco.repo.content.ContentContext;
|
||||||
import org.alfresco.repo.content.transform.TransformerConfig;
|
import org.alfresco.repo.content.ContentStore;
|
||||||
import org.alfresco.repo.content.transform.TransformerDebug;
|
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 org.alfresco.repo.content.transform.UnsupportedTransformationException;
|
import org.alfresco.repo.content.transform.UnsupportedTransformationException;
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||||
import org.alfresco.service.cmr.action.ActionServiceException;
|
import org.alfresco.service.cmr.action.ActionServiceException;
|
||||||
import org.alfresco.service.cmr.action.ActionTrackingService;
|
import org.alfresco.service.cmr.action.ActionTrackingService;
|
||||||
import org.alfresco.service.cmr.action.ExecutionDetails;
|
import org.alfresco.service.cmr.action.ExecutionDetails;
|
||||||
import org.alfresco.service.cmr.action.ExecutionSummary;
|
import org.alfresco.service.cmr.action.ExecutionSummary;
|
||||||
import org.alfresco.service.cmr.action.ParameterDefinition;
|
import org.alfresco.service.cmr.action.ParameterDefinition;
|
||||||
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
||||||
import org.alfresco.service.cmr.rendition.RenditionCancelledException;
|
import org.alfresco.service.cmr.rendition.RenditionCancelledException;
|
||||||
import org.alfresco.service.cmr.rendition.RenditionServiceException;
|
import org.alfresco.service.cmr.rendition.RenditionServiceException;
|
||||||
import org.alfresco.service.cmr.repository.ContentReader;
|
import org.alfresco.service.cmr.repository.ContentReader;
|
||||||
import org.alfresco.service.cmr.repository.ContentWriter;
|
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||||
import org.alfresco.service.cmr.repository.NoTransformerException;
|
import org.alfresco.service.cmr.repository.NoTransformerException;
|
||||||
import org.alfresco.service.cmr.repository.TransformationOptionLimits;
|
import org.alfresco.service.cmr.repository.TransformationOptionLimits;
|
||||||
import org.alfresco.service.cmr.repository.TransformationOptions;
|
import org.alfresco.service.cmr.repository.TransformationOptions;
|
||||||
import org.alfresco.service.cmr.repository.TransformationSourceOptions;
|
import org.alfresco.service.cmr.repository.TransformationSourceOptions;
|
||||||
import org.alfresco.service.cmr.repository.TransformationSourceOptions.TransformationSourceOptionsSerializer;
|
import org.alfresco.service.cmr.repository.TransformationSourceOptions.TransformationSourceOptionsSerializer;
|
||||||
import org.apache.commons.logging.Log;
|
import org.alfresco.util.TempFileProvider;
|
||||||
import org.apache.commons.logging.LogFactory;
|
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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Nick Smith
|
* @author Nick Smith
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractTransformationRenderingEngine extends AbstractRenderingEngine
|
public abstract class AbstractTransformationRenderingEngine extends AbstractRenderingEngine implements ApplicationContextAware
|
||||||
{
|
{
|
||||||
private static Log logger = LogFactory.getLog(AbstractTransformationRenderingEngine.class);
|
private static Log logger = LogFactory.getLog(AbstractTransformationRenderingEngine.class);
|
||||||
|
|
||||||
@@ -112,6 +119,8 @@ public abstract class AbstractTransformationRenderingEngine extends AbstractRend
|
|||||||
* This optional {@link String} parameter specifies the type (or use) of the rendition.
|
* This optional {@link String} parameter specifies the type (or use) of the rendition.
|
||||||
*/
|
*/
|
||||||
public static final String PARAM_USE = TransformerConfig.USE.replaceAll("\\.", "");
|
public static final String PARAM_USE = TransformerConfig.USE.replaceAll("\\.", "");
|
||||||
|
|
||||||
|
private ApplicationContext applicationContext;
|
||||||
|
|
||||||
/* Error messages */
|
/* 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";
|
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";
|
||||||
@@ -119,11 +128,18 @@ public abstract class AbstractTransformationRenderingEngine extends AbstractRend
|
|||||||
private static final String TRANSFORMING_ERROR_MESSAGE = "Some error occurred during document transforming. Error message: ";
|
private static final String TRANSFORMING_ERROR_MESSAGE = "Some error occurred during document transforming. Error message: ";
|
||||||
|
|
||||||
private Collection<TransformationSourceOptionsSerializer> sourceOptionsSerializers;
|
private Collection<TransformationSourceOptionsSerializer> sourceOptionsSerializers;
|
||||||
|
|
||||||
|
private ContentStore tempStore;
|
||||||
|
|
||||||
public Collection<TransformationSourceOptionsSerializer> getSourceOptionsSerializers()
|
public Collection<TransformationSourceOptionsSerializer> getSourceOptionsSerializers()
|
||||||
{
|
{
|
||||||
return sourceOptionsSerializers;
|
return sourceOptionsSerializers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setApplicationContext(ApplicationContext applicationContext)
|
||||||
|
{
|
||||||
|
this.applicationContext = applicationContext;
|
||||||
|
}
|
||||||
|
|
||||||
public void setSourceOptionsSerializers(Collection<TransformationSourceOptionsSerializer> sourceOptionsSerializers)
|
public void setSourceOptionsSerializers(Collection<TransformationSourceOptionsSerializer> sourceOptionsSerializers)
|
||||||
{
|
{
|
||||||
@@ -157,166 +173,198 @@ public abstract class AbstractTransformationRenderingEngine extends AbstractRend
|
|||||||
if (executorService == null)
|
if (executorService == null)
|
||||||
{
|
{
|
||||||
executorService = Executors.newCachedThreadPool();
|
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)
|
* (non-Javadoc)
|
||||||
* @see org.alfresco.repo.rendition.executer.AbstractRenderingEngine#render(org.alfresco.repo.rendition.executer.AbstractRenderingEngine.RenderingContext)
|
* @see org.alfresco.repo.rendition.executer.AbstractRenderingEngine#render(org.alfresco.repo.rendition.executer.AbstractRenderingEngine.RenderingContext)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void render(RenderingContext context)
|
protected void render(RenderingContext context)
|
||||||
{
|
{
|
||||||
ContentReader contentReader = context.makeContentReader();
|
// check for existing transformed content and reuse if present to avoid re-rendering
|
||||||
// There will have been an exception if there is no content data so contentReader is not null.
|
String existingTransformedContentUrl = context.getExistingTransformedContentUrl();
|
||||||
String sourceUrl = contentReader.getContentUrl();
|
if(existingTransformedContentUrl != null)
|
||||||
String sourceMimeType = contentReader.getMimetype();
|
{
|
||||||
String targetMimeType = getTargetMimeType(context);
|
if(logger.isDebugEnabled())
|
||||||
|
|
||||||
// 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);
|
|
||||||
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 " +
|
logger.debug("Reusing existing rendered content " + existingTransformedContentUrl
|
||||||
"currently unsupported, this action can't be cancelled");
|
+ " for rendering context " + context);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Call the transform in a different thread so we can move on if cancelled
|
ContentReader existingTransformedContent = tempStore.getReader(existingTransformedContentUrl);
|
||||||
FutureTask<ContentWriter> transformTask = new FutureTask<ContentWriter>(
|
copyTransformedContent(existingTransformedContent, context);
|
||||||
new TransformationCallable(contentReader, targetMimeType, options, context,
|
}
|
||||||
AuthenticationUtil.getFullyAuthenticatedUser()));
|
else
|
||||||
getExecutorService().execute(transformTask);
|
{
|
||||||
|
if(logger.isDebugEnabled())
|
||||||
// Start checking for cancellation or timeout
|
{
|
||||||
while (true)
|
logger.debug("Rendering 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);
|
||||||
|
|
||||||
|
// 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);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Thread.sleep(CANCELLED_ACTION_POLLING_INTERVAL);
|
transformer = this.contentService.getTransformer(sourceUrl, sourceMimeType, contentReader.getSize(), targetMimeType, options);
|
||||||
if (transformTask.isDone())
|
}
|
||||||
|
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())
|
||||||
{
|
{
|
||||||
actionCompleted = true;
|
logger.info("Cancelling of multiple concurrent action instances " +
|
||||||
break;
|
"currently unsupported, this action can't be cancelled");
|
||||||
}
|
}
|
||||||
// Check timeout in case transformer doesn't obey it
|
}
|
||||||
if (options.getTimeoutMs() > 0 &&
|
|
||||||
new Date().getTime() - startTime > (options.getTimeoutMs() + CANCELLED_ACTION_POLLING_INTERVAL))
|
// Call the transform in a different thread so we can move on if cancelled
|
||||||
|
FutureTask<ContentWriter> transformTask = new FutureTask<ContentWriter>(
|
||||||
|
new TransformationCallable(contentReader, targetMimeType, options, context,
|
||||||
|
AuthenticationUtil.getFullyAuthenticatedUser()));
|
||||||
|
getExecutorService().execute(transformTask);
|
||||||
|
|
||||||
|
// Start checking for cancellation or timeout
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
// We hit a timeout, let the transform thread continue but results will be ignored
|
Thread.sleep(CANCELLED_ACTION_POLLING_INTERVAL);
|
||||||
if (logger.isDebugEnabled())
|
if (transformTask.isDone())
|
||||||
{
|
{
|
||||||
logger.debug("Transformation did not obey timeout limit, " +
|
actionCompleted = true;
|
||||||
"rendition action is moving on");
|
break;
|
||||||
}
|
}
|
||||||
break;
|
// Check timeout in case transformer doesn't obey it
|
||||||
}
|
if (options.getTimeoutMs() > 0 &&
|
||||||
if (executionSummary != null)
|
new Date().getTime() - startTime > (options.getTimeoutMs() + CANCELLED_ACTION_POLLING_INTERVAL))
|
||||||
{
|
|
||||||
ExecutionDetails executionDetails =
|
|
||||||
actionTrackingService.getExecutionDetails(executionSummary);
|
|
||||||
if (executionDetails != null)
|
|
||||||
{
|
{
|
||||||
actionCancelled = executionDetails.isCancelRequested();
|
// We hit a timeout, let the transform thread continue but results will be ignored
|
||||||
if (actionCancelled)
|
if (logger.isDebugEnabled())
|
||||||
{
|
{
|
||||||
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)
|
||||||
{
|
{
|
||||||
logger.debug("Cancelling transformation");
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Cancelling transformation");
|
||||||
|
}
|
||||||
|
transformTask.cancel(true);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
transformTask.cancel(true);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
catch (InterruptedException e)
|
||||||
catch (InterruptedException e)
|
|
||||||
{
|
|
||||||
// entire thread was asked to stop
|
|
||||||
actionCancelled = true;
|
|
||||||
transformTask.cancel(true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
{
|
|
||||||
// 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
|
// entire thread was asked to stop
|
||||||
throw new RenditionServiceException("Target of transformation not present");
|
actionCancelled = true;
|
||||||
|
transformTask.cancel(true);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
writer.putContent(tempTarget.getReader().getContentInputStream());
|
|
||||||
}
|
}
|
||||||
catch (ExecutionException e)
|
|
||||||
|
if (actionCancelled)
|
||||||
{
|
{
|
||||||
// Unwrap our cause and throw that
|
throw new RenditionCancelledException("Rendition action cancelled");
|
||||||
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)
|
|
||||||
|
if (!actionCompleted && !actionCancelled)
|
||||||
{
|
{
|
||||||
// We were asked to stop
|
throw new RenditionServiceException("Transformation failed to obey timeout limit");
|
||||||
transformTask.cancel(true);
|
}
|
||||||
}
|
|
||||||
}
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract TransformationOptions getTransformOptions(RenderingContext context);
|
protected abstract TransformationOptions getTransformOptions(RenderingContext context);
|
||||||
@@ -404,7 +452,7 @@ public abstract class AbstractTransformationRenderingEngine extends AbstractRend
|
|||||||
|
|
||||||
return paramList;
|
return paramList;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of <code>Callable</code> for doing the work of the transformation
|
* Implementation of <code>Callable</code> for doing the work of the transformation
|
||||||
* which returns the temporary content writer if successful.
|
* which returns the temporary content writer if successful.
|
||||||
@@ -440,7 +488,7 @@ public abstract class AbstractTransformationRenderingEngine extends AbstractRend
|
|||||||
public ContentWriter doWork() throws Exception
|
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
|
// ALF-15715: Use temporary write to avoid operating on the real node for fear of row locking while long transforms are in progress
|
||||||
ContentWriter tempContentWriter = contentService.getTempWriter();
|
ContentWriter tempContentWriter = tempStore.getWriter(ContentContext.NULL_CONTEXT);
|
||||||
tempContentWriter.setMimetype(targetMimeType);
|
tempContentWriter.setMimetype(targetMimeType);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@@ -265,8 +265,8 @@ public class ThumbnailServiceImpl implements ThumbnailService,
|
|||||||
|
|
||||||
if (logger.isDebugEnabled())
|
if (logger.isDebugEnabled())
|
||||||
{
|
{
|
||||||
logger.debug("Thumbnail created " + childAssoc + " for sourceNodeRef " + sourceNodeRef + ", thumbnail " + thumbnailName
|
logger.debug("Thumbnail created " + childAssoc + " for sourceNodeRef " + sourceNodeRef + ", thumbnail "
|
||||||
+ ", thumbnailNodeRef " + thumbnailNodeRef);
|
+ thumbnailName + ", thumbnailNodeRef " + thumbnailNodeRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
// MNT-15135: Cache the associations between parent nodes and updated thumbnails,
|
// MNT-15135: Cache the associations between parent nodes and updated thumbnails,
|
||||||
@@ -382,6 +382,12 @@ public class ThumbnailServiceImpl implements ThumbnailService,
|
|||||||
//We can be in a read-only transaction, so force a new transaction
|
//We can be in a read-only transaction, so force a new transaction
|
||||||
requiresNew = true;
|
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<NodeRef>()
|
return txnHelper.doInTransaction(new RetryingTransactionCallback<NodeRef>()
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -392,18 +398,12 @@ public class ThumbnailServiceImpl implements ThumbnailService,
|
|||||||
{
|
{
|
||||||
public NodeRef doWork() throws Exception
|
public NodeRef doWork() throws Exception
|
||||||
{
|
{
|
||||||
return createThumbnailNode( node,
|
return createThumbnailNode(node, definition, thumbnailName);
|
||||||
contentProperty,
|
|
||||||
mimetype,
|
|
||||||
transformationOptions,
|
|
||||||
thumbnailName,
|
|
||||||
assocDetails);
|
|
||||||
}
|
}
|
||||||
}, AuthenticationUtil.getSystemUserName());
|
}, AuthenticationUtil.getSystemUserName());
|
||||||
}
|
}
|
||||||
|
|
||||||
}, false, requiresNew);
|
}, false, requiresNew);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private QName getThumbnailQName(String localThumbnailName)
|
private QName getThumbnailQName(String localThumbnailName)
|
||||||
@@ -717,14 +717,8 @@ public class ThumbnailServiceImpl implements ThumbnailService,
|
|||||||
return definition;
|
return definition;
|
||||||
}
|
}
|
||||||
|
|
||||||
private NodeRef createThumbnailNode(final NodeRef node, final QName contentProperty,
|
private NodeRef createThumbnailNode(final NodeRef node, final RenditionDefinition definition, final String thumbnailName)
|
||||||
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
|
try
|
||||||
{
|
{
|
||||||
ChildAssociationRef thumbnailAssoc = renditionService.render(node, definition);
|
ChildAssociationRef thumbnailAssoc = renditionService.render(node, definition);
|
||||||
|
@@ -26,73 +26,75 @@
|
|||||||
|
|
||||||
package org.alfresco.repo.rendition;
|
package org.alfresco.repo.rendition;
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
import org.alfresco.error.AlfrescoRuntimeException;
|
import org.alfresco.model.ContentModel;
|
||||||
import org.alfresco.model.ContentModel;
|
import org.alfresco.model.RenditionModel;
|
||||||
import org.alfresco.model.RenditionModel;
|
import org.alfresco.repo.action.RuntimeActionService;
|
||||||
import org.alfresco.repo.action.RuntimeActionService;
|
import org.alfresco.repo.action.executer.ExporterActionExecuter;
|
||||||
import org.alfresco.repo.action.executer.ExporterActionExecuter;
|
import org.alfresco.repo.content.MimetypeMap;
|
||||||
import org.alfresco.repo.content.MimetypeMap;
|
import org.alfresco.repo.content.transform.AbstractContentTransformerTest;
|
||||||
import org.alfresco.repo.content.transform.AbstractContentTransformerTest;
|
import org.alfresco.repo.content.transform.ContentTransformer;
|
||||||
import org.alfresco.repo.content.transform.ContentTransformer;
|
import org.alfresco.repo.content.transform.ContentTransformerRegistry;
|
||||||
import org.alfresco.repo.content.transform.ContentTransformerRegistry;
|
import org.alfresco.repo.content.transform.UnimportantTransformException;
|
||||||
import org.alfresco.repo.content.transform.UnimportantTransformException;
|
import org.alfresco.repo.content.transform.magick.ImageTransformationOptions;
|
||||||
import org.alfresco.repo.content.transform.magick.ImageTransformationOptions;
|
import org.alfresco.repo.jscript.ClasspathScriptLocation;
|
||||||
import org.alfresco.repo.jscript.ClasspathScriptLocation;
|
import org.alfresco.repo.model.Repository;
|
||||||
import org.alfresco.repo.model.Repository;
|
import org.alfresco.repo.policy.BehaviourFilter;
|
||||||
import org.alfresco.repo.policy.BehaviourFilter;
|
import org.alfresco.repo.rendition.executer.AbstractRenderingEngine;
|
||||||
import org.alfresco.repo.rendition.executer.AbstractRenderingEngine;
|
import org.alfresco.repo.rendition.executer.FreemarkerRenderingEngine;
|
||||||
import org.alfresco.repo.rendition.executer.FreemarkerRenderingEngine;
|
import org.alfresco.repo.rendition.executer.ImageRenderingEngine;
|
||||||
import org.alfresco.repo.rendition.executer.ImageRenderingEngine;
|
import org.alfresco.repo.rendition.executer.ReformatRenderingEngine;
|
||||||
import org.alfresco.repo.rendition.executer.ReformatRenderingEngine;
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||||
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
import org.alfresco.service.cmr.action.Action;
|
||||||
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.coci.CheckOutCheckInService;
|
||||||
import org.alfresco.service.cmr.lock.LockService;
|
import org.alfresco.service.cmr.lock.LockService;
|
||||||
import org.alfresco.service.cmr.lock.LockType;
|
import org.alfresco.service.cmr.lock.LockType;
|
||||||
import org.alfresco.service.cmr.rendition.CompositeRenditionDefinition;
|
import org.alfresco.service.cmr.rendition.CompositeRenditionDefinition;
|
||||||
import org.alfresco.service.cmr.rendition.RenderCallback;
|
import org.alfresco.service.cmr.rendition.RenderCallback;
|
||||||
import org.alfresco.service.cmr.rendition.RenderingEngineDefinition;
|
import org.alfresco.service.cmr.rendition.RenderingEngineDefinition;
|
||||||
import org.alfresco.service.cmr.rendition.RenditionCancelledException;
|
import org.alfresco.service.cmr.rendition.RenditionCancelledException;
|
||||||
import org.alfresco.service.cmr.rendition.RenditionDefinition;
|
import org.alfresco.service.cmr.rendition.RenditionDefinition;
|
||||||
import org.alfresco.service.cmr.rendition.RenditionService;
|
import org.alfresco.service.cmr.rendition.RenditionService;
|
||||||
import org.alfresco.service.cmr.rendition.RenditionServiceException;
|
import org.alfresco.service.cmr.rendition.RenditionServiceException;
|
||||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||||
import org.alfresco.service.cmr.repository.ContentData;
|
import org.alfresco.service.cmr.repository.ContentData;
|
||||||
import org.alfresco.service.cmr.repository.ContentIOException;
|
import org.alfresco.service.cmr.repository.ContentIOException;
|
||||||
import org.alfresco.service.cmr.repository.ContentReader;
|
import org.alfresco.service.cmr.repository.ContentReader;
|
||||||
import org.alfresco.service.cmr.repository.ContentService;
|
import org.alfresco.service.cmr.repository.ContentService;
|
||||||
import org.alfresco.service.cmr.repository.ContentWriter;
|
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||||
import org.alfresco.service.cmr.repository.CropSourceOptions.CropSourceOptionsSerializer;
|
import org.alfresco.service.cmr.repository.CropSourceOptions.CropSourceOptionsSerializer;
|
||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
import org.alfresco.service.cmr.repository.NodeService;
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
import org.alfresco.service.cmr.repository.ScriptLocation;
|
import org.alfresco.service.cmr.repository.ScriptLocation;
|
||||||
import org.alfresco.service.cmr.repository.ScriptService;
|
import org.alfresco.service.cmr.repository.ScriptService;
|
||||||
import org.alfresco.service.cmr.repository.StoreRef;
|
import org.alfresco.service.cmr.repository.StoreRef;
|
||||||
import org.alfresco.service.cmr.repository.TransformationOptions;
|
import org.alfresco.service.cmr.repository.TransformationOptions;
|
||||||
import org.alfresco.service.namespace.NamespaceService;
|
import org.alfresco.service.cmr.version.VersionService;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.NamespaceService;
|
||||||
import org.alfresco.service.namespace.RegexQNamePattern;
|
import org.alfresco.service.namespace.QName;
|
||||||
import org.alfresco.test_category.OwnJVMTestsCategory;
|
import org.alfresco.service.namespace.RegexQNamePattern;
|
||||||
import org.alfresco.util.BaseAlfrescoSpringTest;
|
import org.alfresco.service.transaction.TransactionService;
|
||||||
import org.alfresco.util.Pair;
|
import org.alfresco.test_category.OwnJVMTestsCategory;
|
||||||
import org.junit.experimental.categories.Category;
|
import org.alfresco.util.BaseAlfrescoSpringTest;
|
||||||
|
import org.alfresco.util.Pair;
|
||||||
|
import org.junit.experimental.categories.Category;
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1292,7 +1294,7 @@ public class RenditionServiceIntegrationTest extends BaseAlfrescoSpringTest
|
|||||||
|
|
||||||
assertNull(results.getAssoc());
|
assertNull(results.getAssoc());
|
||||||
assertEquals("Expected a UnimportantTransformException", UnimportantTransformException.class, results.getThrowable().getClass());
|
assertEquals("Expected a UnimportantTransformException", UnimportantTransformException.class, results.getThrowable().getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method performs an asynchronous rendition and calls back the result to the
|
* This method performs an asynchronous rendition and calls back the result to the
|
||||||
@@ -2606,15 +2608,22 @@ public class RenditionServiceIntegrationTest extends BaseAlfrescoSpringTest
|
|||||||
assertTrue("upper left should be "+topLeft, Integer.toHexString(rgbAtTopLeft).endsWith(topLeft));
|
assertTrue("upper left should be "+topLeft, Integer.toHexString(rgbAtTopLeft).endsWith(topLeft));
|
||||||
int rgbAtBottomRight = img.getRGB(img.getWidth() - 1, img.getHeight() - 1);
|
int rgbAtBottomRight = img.getRGB(img.getWidth() - 1, img.getHeight() - 1);
|
||||||
assertTrue("lower right should be "+bottomRight, Integer.toHexString(rgbAtBottomRight).endsWith(bottomRight));
|
assertTrue("lower right should be "+bottomRight, Integer.toHexString(rgbAtBottomRight).endsWith(bottomRight));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A dummy rendering engine used in testing
|
* A dummy rendering engine used in testing
|
||||||
*/
|
*/
|
||||||
private static class DummyHelloWorldRenditionEngine extends AbstractRenderingEngine
|
private static class DummyHelloWorldRenditionEngine extends AbstractRenderingEngine
|
||||||
{
|
{
|
||||||
private static final String ENGINE_NAME = "helloWorldRenderingEngine";
|
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
|
* Loads this executor into the ApplicationContext, if it
|
||||||
* isn't already there
|
* isn't already there
|
||||||
@@ -2624,7 +2633,7 @@ public class RenditionServiceIntegrationTest extends BaseAlfrescoSpringTest
|
|||||||
if(!ctx.containsBean(ENGINE_NAME))
|
if(!ctx.containsBean(ENGINE_NAME))
|
||||||
{
|
{
|
||||||
// Create, and do dependencies
|
// Create, and do dependencies
|
||||||
DummyHelloWorldRenditionEngine hw = new DummyHelloWorldRenditionEngine();
|
DummyHelloWorldRenditionEngine hw = new DummyHelloWorldRenditionEngine(ctx);
|
||||||
hw.setRuntimeActionService(
|
hw.setRuntimeActionService(
|
||||||
(RuntimeActionService)ctx.getBean("actionService")
|
(RuntimeActionService)ctx.getBean("actionService")
|
||||||
);
|
);
|
||||||
@@ -2653,7 +2662,8 @@ public class RenditionServiceIntegrationTest extends BaseAlfrescoSpringTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void render(RenderingContext context) {
|
protected void render(RenderingContext context)
|
||||||
|
{
|
||||||
ContentWriter contentWriter = context.makeContentWriter();
|
ContentWriter contentWriter = context.makeContentWriter();
|
||||||
contentWriter.setMimetype("text/plain");
|
contentWriter.setMimetype("text/plain");
|
||||||
contentWriter.putContent( "Hello, world!" );
|
contentWriter.putContent( "Hello, world!" );
|
||||||
|
@@ -27,8 +27,15 @@
|
|||||||
package org.alfresco.repo.thumbnail;
|
package org.alfresco.repo.thumbnail;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.io.Serializable;
|
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.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@@ -47,12 +54,16 @@ import org.alfresco.repo.content.transform.ContentTransformer;
|
|||||||
import org.alfresco.repo.content.transform.magick.ImageResizeOptions;
|
import org.alfresco.repo.content.transform.magick.ImageResizeOptions;
|
||||||
import org.alfresco.repo.content.transform.magick.ImageTransformationOptions;
|
import org.alfresco.repo.content.transform.magick.ImageTransformationOptions;
|
||||||
import org.alfresco.repo.jscript.ClasspathScriptLocation;
|
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.thumbnail.script.ScriptThumbnailService;
|
||||||
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||||
import org.alfresco.service.ServiceRegistry;
|
import org.alfresco.service.ServiceRegistry;
|
||||||
import org.alfresco.service.cmr.action.Action;
|
import org.alfresco.service.cmr.action.Action;
|
||||||
import org.alfresco.service.cmr.action.ActionCondition;
|
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.rendition.RenditionService;
|
||||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||||
import org.alfresco.service.cmr.repository.ContentData;
|
import org.alfresco.service.cmr.repository.ContentData;
|
||||||
@@ -71,6 +82,7 @@ import org.alfresco.service.cmr.rule.RuleType;
|
|||||||
import org.alfresco.service.cmr.thumbnail.FailedThumbnailInfo;
|
import org.alfresco.service.cmr.thumbnail.FailedThumbnailInfo;
|
||||||
import org.alfresco.service.cmr.thumbnail.ThumbnailParentAssociationDetails;
|
import org.alfresco.service.cmr.thumbnail.ThumbnailParentAssociationDetails;
|
||||||
import org.alfresco.service.cmr.thumbnail.ThumbnailService;
|
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.NamespaceService;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
import org.alfresco.service.namespace.QNamePattern;
|
import org.alfresco.service.namespace.QNamePattern;
|
||||||
@@ -80,6 +92,7 @@ import org.alfresco.test_category.OwnJVMTestsCategory;
|
|||||||
import org.alfresco.util.ApplicationContextHelper;
|
import org.alfresco.util.ApplicationContextHelper;
|
||||||
import org.alfresco.util.BaseAlfrescoSpringTest;
|
import org.alfresco.util.BaseAlfrescoSpringTest;
|
||||||
import org.alfresco.util.TempFileProvider;
|
import org.alfresco.util.TempFileProvider;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.junit.experimental.categories.Category;
|
import org.junit.experimental.categories.Category;
|
||||||
@@ -102,6 +115,8 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
private ScriptService scriptService;
|
private ScriptService scriptService;
|
||||||
private MimetypeMap mimetypeMap;
|
private MimetypeMap mimetypeMap;
|
||||||
private TransactionService transactionService;
|
private TransactionService transactionService;
|
||||||
|
private VersionService versionService;
|
||||||
|
private AttributeService attributeService;
|
||||||
private ServiceRegistry services;
|
private ServiceRegistry services;
|
||||||
private FailureHandlingOptions failureHandlingOptions;
|
private FailureHandlingOptions failureHandlingOptions;
|
||||||
|
|
||||||
@@ -127,8 +142,10 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
this.scriptThumbnailService = (ScriptThumbnailService) this.applicationContext.getBean("thumbnailServiceScript");
|
this.scriptThumbnailService = (ScriptThumbnailService) this.applicationContext.getBean("thumbnailServiceScript");
|
||||||
this.mimetypeMap = (MimetypeMap) this.applicationContext.getBean("mimetypeService");
|
this.mimetypeMap = (MimetypeMap) this.applicationContext.getBean("mimetypeService");
|
||||||
this.scriptService = (ScriptService) this.applicationContext.getBean("ScriptService");
|
this.scriptService = (ScriptService) this.applicationContext.getBean("ScriptService");
|
||||||
|
this.attributeService = (AttributeService) this.applicationContext.getBean("attributeService");
|
||||||
this.services = (ServiceRegistry) this.applicationContext.getBean("ServiceRegistry");
|
this.services = (ServiceRegistry) this.applicationContext.getBean("ServiceRegistry");
|
||||||
this.transactionService = (TransactionService) this.applicationContext.getBean("transactionService");
|
this.transactionService = (TransactionService) this.applicationContext.getBean("transactionService");
|
||||||
|
this.versionService = (VersionService) this.applicationContext.getBean("versionService");
|
||||||
this.failureHandlingOptions = (FailureHandlingOptions) this.applicationContext.getBean("standardFailureOptions");
|
this.failureHandlingOptions = (FailureHandlingOptions) this.applicationContext.getBean("standardFailureOptions");
|
||||||
|
|
||||||
// Create a folder and some content
|
// Create a folder and some content
|
||||||
@@ -169,7 +186,7 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
{
|
{
|
||||||
QName qname = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "doclib");
|
QName qname = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "doclib");
|
||||||
|
|
||||||
ThumbnailDefinition details = thumbnailService.getThumbnailRegistry().getThumbnailDefinition(
|
final ThumbnailDefinition details = thumbnailService.getThumbnailRegistry().getThumbnailDefinition(
|
||||||
qname.getLocalName());
|
qname.getLocalName());
|
||||||
assertEquals("doclib", details.getName());
|
assertEquals("doclib", details.getName());
|
||||||
assertEquals("image/png", details.getMimetype());
|
assertEquals("image/png", details.getMimetype());
|
||||||
@@ -177,21 +194,43 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
|
|
||||||
checkTransformer();
|
checkTransformer();
|
||||||
|
|
||||||
NodeRef jpgOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_JPEG);
|
final NodeRef jpgOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_JPEG);
|
||||||
|
|
||||||
|
setComplete();
|
||||||
|
endTransaction();
|
||||||
|
|
||||||
|
final NodeRef thumbnail0 = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<NodeRef>()
|
||||||
|
{
|
||||||
|
@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 thumbnail0 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT,
|
|
||||||
MimetypeMap.MIMETYPE_IMAGE_JPEG, details.getTransformationOptions(), "doclib");
|
|
||||||
assertNotNull(thumbnail0);
|
assertNotNull(thumbnail0);
|
||||||
checkRenditioned(jpgOrig, Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "doclib", 1)));
|
|
||||||
checkRendition("doclib", thumbnail0);
|
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>()
|
||||||
outputThumbnailTempContentLocation(thumbnail0, "jpg", "doclib test");
|
{
|
||||||
|
@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);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testCreateRenditionThumbnailFromPdf() throws Exception
|
public void testCreateRenditionThumbnailFromPdf() throws Exception
|
||||||
{
|
{
|
||||||
QName qname = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "doclib");
|
QName qname = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "doclib");
|
||||||
|
|
||||||
ThumbnailDefinition details = thumbnailService.getThumbnailRegistry().getThumbnailDefinition(
|
final ThumbnailDefinition details = thumbnailService.getThumbnailRegistry().getThumbnailDefinition(
|
||||||
qname.getLocalName());
|
qname.getLocalName());
|
||||||
assertEquals("doclib", details.getName());
|
assertEquals("doclib", details.getName());
|
||||||
assertEquals("image/png", details.getMimetype());
|
assertEquals("image/png", details.getMimetype());
|
||||||
@@ -199,14 +238,36 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
|
|
||||||
checkTransformer();
|
checkTransformer();
|
||||||
|
|
||||||
NodeRef pdfOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_PDF);
|
final NodeRef pdfOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_PDF);
|
||||||
|
|
||||||
|
setComplete();
|
||||||
|
endTransaction();
|
||||||
|
|
||||||
|
final NodeRef thumbnail0 = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<NodeRef>()
|
||||||
|
{
|
||||||
|
@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 thumbnail0 = this.thumbnailService.createThumbnail(pdfOrig, ContentModel.PROP_CONTENT,
|
|
||||||
MimetypeMap.MIMETYPE_IMAGE_JPEG, details.getTransformationOptions(), "doclib");
|
|
||||||
assertNotNull(thumbnail0);
|
assertNotNull(thumbnail0);
|
||||||
checkRenditioned(pdfOrig, Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "doclib", 1)));
|
|
||||||
checkRendition("doclib", thumbnail0);
|
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>()
|
||||||
outputThumbnailTempContentLocation(thumbnail0, "jpg", "doclib test");
|
{
|
||||||
|
@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);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testCreateRenditionThumbnailFromPdfPage2() throws Exception
|
public void testCreateRenditionThumbnailFromPdfPage2() throws Exception
|
||||||
@@ -222,119 +283,228 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
|
|
||||||
checkTransformer();
|
checkTransformer();
|
||||||
|
|
||||||
NodeRef pdfOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_PDF);
|
final NodeRef pdfOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_PDF);
|
||||||
|
|
||||||
NodeRef thumbnail0 = this.thumbnailService.createThumbnail(pdfOrig, ContentModel.PROP_CONTENT,
|
final NodeRef thumbnail0 = this.thumbnailService.createThumbnail(pdfOrig, ContentModel.PROP_CONTENT,
|
||||||
MimetypeMap.MIMETYPE_IMAGE_JPEG, thumbnailDefinition.getTransformationOptions(), "doclib_2");
|
MimetypeMap.MIMETYPE_IMAGE_JPEG, thumbnailDefinition.getTransformationOptions(), "doclib_2");
|
||||||
|
|
||||||
|
setComplete();
|
||||||
|
endTransaction();
|
||||||
|
|
||||||
assertNotNull(thumbnail0);
|
assertNotNull(thumbnail0);
|
||||||
checkRenditioned(pdfOrig, Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "doclib_2", 1)));
|
|
||||||
checkRendition("doclib_2", thumbnail0);
|
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>()
|
||||||
|
{
|
||||||
// Check the length
|
@Override
|
||||||
File tempFile = TempFileProvider.createTempFile("thumbnailServiceImplTest", ".jpg");
|
public Void execute() throws Throwable
|
||||||
ContentReader reader = this.contentService.getReader(thumbnail0, ContentModel.PROP_CONTENT);
|
{
|
||||||
|
checkRenditioned(pdfOrig, Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "doclib_2", 1)));
|
||||||
long size = reader.getSize();
|
checkRendition(pdfOrig, "doclib_2", thumbnail0);
|
||||||
System.out.println("size=" + size);
|
|
||||||
assertTrue("Page 2 should be blank and less than 4500 bytes", size < 4500);
|
// Check the length
|
||||||
|
File tempFile = TempFileProvider.createTempFile("thumbnailServiceImplTest", ".jpg");
|
||||||
reader.getContent(tempFile);
|
ContentReader reader = contentService.getReader(thumbnail0, ContentModel.PROP_CONTENT);
|
||||||
System.out.println("doclib_2 test: " + tempFile.getPath());
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testCreateThumbnailFromImage() throws Exception
|
public void testCreateThumbnailFromImage() throws Exception
|
||||||
{
|
{
|
||||||
checkTransformer();
|
checkTransformer();
|
||||||
|
|
||||||
NodeRef jpgOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_JPEG);
|
final NodeRef jpgOrig = createOriginalContent(folder, MimetypeMap.MIMETYPE_IMAGE_JPEG);
|
||||||
NodeRef gifOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_GIF);
|
final NodeRef gifOrig = createOriginalContent(folder, MimetypeMap.MIMETYPE_IMAGE_GIF);
|
||||||
|
|
||||||
|
setComplete();
|
||||||
|
endTransaction();
|
||||||
|
|
||||||
// ===== small: 64x64, marked as thumbnail ====
|
// ===== small: 64x64, marked as thumbnail ====
|
||||||
|
|
||||||
ImageResizeOptions imageResizeOptions = new ImageResizeOptions();
|
final ImageResizeOptions imageResizeOptions = new ImageResizeOptions();
|
||||||
imageResizeOptions.setWidth(64);
|
imageResizeOptions.setWidth(64);
|
||||||
imageResizeOptions.setHeight(64);
|
imageResizeOptions.setHeight(64);
|
||||||
imageResizeOptions.setResizeToThumbnail(true);
|
imageResizeOptions.setResizeToThumbnail(true);
|
||||||
ImageTransformationOptions imageTransformationOptions = new ImageTransformationOptions();
|
final ImageTransformationOptions imageTransformationOptions = new ImageTransformationOptions();
|
||||||
imageTransformationOptions.setResizeOptions(imageResizeOptions);
|
imageTransformationOptions.setResizeOptions(imageResizeOptions);
|
||||||
// ThumbnailDetails createOptions = new ThumbnailDetails();
|
|
||||||
|
|
||||||
NodeRef thumbnail1 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT,
|
final NodeRef thumbnail1 = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<NodeRef>()
|
||||||
MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions, "small");
|
{
|
||||||
|
@Override
|
||||||
|
public NodeRef execute() throws Throwable
|
||||||
|
{
|
||||||
|
NodeRef thumbnail1 = thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT,
|
||||||
|
MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions, "small");
|
||||||
|
return thumbnail1;
|
||||||
|
}
|
||||||
|
}, false, true);
|
||||||
|
|
||||||
assertNotNull(thumbnail1);
|
assertNotNull(thumbnail1);
|
||||||
checkRenditioned(jpgOrig, Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "small", 1)));
|
|
||||||
checkRendition("small", thumbnail1);
|
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>()
|
||||||
outputThumbnailTempContentLocation(thumbnail1, "jpg", "small - 64x64, marked as thumbnail");
|
{
|
||||||
|
@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);
|
||||||
|
|
||||||
// ===== small2: 64x64, aspect not maintained ====
|
// ===== small2: 64x64, aspect not maintained ====
|
||||||
|
|
||||||
ImageResizeOptions imageResizeOptions2 = new ImageResizeOptions();
|
final ImageResizeOptions imageResizeOptions2 = new ImageResizeOptions();
|
||||||
imageResizeOptions2.setWidth(64);
|
imageResizeOptions2.setWidth(64);
|
||||||
imageResizeOptions2.setHeight(64);
|
imageResizeOptions2.setHeight(64);
|
||||||
imageResizeOptions2.setMaintainAspectRatio(false);
|
imageResizeOptions2.setMaintainAspectRatio(false);
|
||||||
ImageTransformationOptions imageTransformationOptions2 = new ImageTransformationOptions();
|
final ImageTransformationOptions imageTransformationOptions2 = new ImageTransformationOptions();
|
||||||
imageTransformationOptions2.setResizeOptions(imageResizeOptions2);
|
imageTransformationOptions2.setResizeOptions(imageResizeOptions2);
|
||||||
// ThumbnailDetails createOptions2 = new ThumbnailDetails();
|
|
||||||
NodeRef thumbnail2 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT,
|
final NodeRef thumbnail2 = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<NodeRef>()
|
||||||
MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions2, "small2");
|
{
|
||||||
checkRenditioned(jpgOrig, Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "small2", 1)));
|
@Override
|
||||||
checkRendition("small2", thumbnail2);
|
public NodeRef execute() throws Throwable
|
||||||
outputThumbnailTempContentLocation(thumbnail2, "jpg", "small2 - 64x64, aspect not maintained");
|
{
|
||||||
|
NodeRef thumbnail2 = thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT,
|
||||||
|
MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions2, "small2");
|
||||||
|
return thumbnail2;
|
||||||
|
}
|
||||||
|
}, false, true);
|
||||||
|
|
||||||
|
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>()
|
||||||
|
{
|
||||||
|
@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);
|
||||||
|
|
||||||
// ===== half: 50%x50 =====
|
// ===== half: 50%x50 =====
|
||||||
|
|
||||||
ImageResizeOptions imageResizeOptions3 = new ImageResizeOptions();
|
final ImageResizeOptions imageResizeOptions3 = new ImageResizeOptions();
|
||||||
imageResizeOptions3.setWidth(50);
|
imageResizeOptions3.setWidth(50);
|
||||||
imageResizeOptions3.setHeight(50);
|
imageResizeOptions3.setHeight(50);
|
||||||
imageResizeOptions3.setPercentResize(true);
|
imageResizeOptions3.setPercentResize(true);
|
||||||
ImageTransformationOptions imageTransformationOptions3 = new ImageTransformationOptions();
|
final ImageTransformationOptions imageTransformationOptions3 = new ImageTransformationOptions();
|
||||||
imageTransformationOptions3.setResizeOptions(imageResizeOptions3);
|
imageTransformationOptions3.setResizeOptions(imageResizeOptions3);
|
||||||
// ThumbnailDetails createOptions3 = new ThumbnailDetails();
|
|
||||||
NodeRef thumbnail3 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT,
|
final NodeRef thumbnail3 = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<NodeRef>()
|
||||||
MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions3, "half");
|
{
|
||||||
checkRenditioned(jpgOrig,
|
@Override
|
||||||
Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "half", 1)));
|
public NodeRef execute() throws Throwable
|
||||||
checkRendition("half", thumbnail3);
|
{
|
||||||
outputThumbnailTempContentLocation(thumbnail3, "jpg", "half - 50%x50%");
|
NodeRef thumbnail3 = thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT,
|
||||||
|
MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions3, "half");
|
||||||
|
return thumbnail3;
|
||||||
|
}
|
||||||
|
}, false, true);
|
||||||
|
|
||||||
|
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>()
|
||||||
|
{
|
||||||
|
@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);
|
||||||
|
|
||||||
// ===== half2: 50%x50 from gif =====
|
// ===== half2: 50%x50 from gif =====
|
||||||
|
|
||||||
ImageResizeOptions imageResizeOptions4 = new ImageResizeOptions();
|
final ImageResizeOptions imageResizeOptions4 = new ImageResizeOptions();
|
||||||
imageResizeOptions4.setWidth(50);
|
imageResizeOptions4.setWidth(50);
|
||||||
imageResizeOptions4.setHeight(50);
|
imageResizeOptions4.setHeight(50);
|
||||||
imageResizeOptions4.setPercentResize(true);
|
imageResizeOptions4.setPercentResize(true);
|
||||||
ImageTransformationOptions imageTransformationOptions4 = new ImageTransformationOptions();
|
final ImageTransformationOptions imageTransformationOptions4 = new ImageTransformationOptions();
|
||||||
imageTransformationOptions4.setResizeOptions(imageResizeOptions4);
|
imageTransformationOptions4.setResizeOptions(imageResizeOptions4);
|
||||||
// ThumbnailDetails createOptions4 = new ThumbnailDetails();
|
|
||||||
NodeRef thumbnail4 = this.thumbnailService.createThumbnail(gifOrig, ContentModel.PROP_CONTENT,
|
final NodeRef thumbnail4 = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<NodeRef>()
|
||||||
MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions4, "half2");
|
{
|
||||||
checkRenditioned(gifOrig, Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "half2", 1)));
|
@Override
|
||||||
checkRendition("half2", thumbnail4);
|
public NodeRef execute() throws Throwable
|
||||||
outputThumbnailTempContentLocation(thumbnail4, "jpg", "half2 - 50%x50%, from gif");
|
{
|
||||||
|
NodeRef thumbnail4 = thumbnailService.createThumbnail(gifOrig, ContentModel.PROP_CONTENT,
|
||||||
|
MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions4, "half2");
|
||||||
|
return thumbnail4;
|
||||||
|
}
|
||||||
|
}, false, true);
|
||||||
|
|
||||||
|
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>()
|
||||||
|
{
|
||||||
|
@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);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testDuplicationNames() throws Exception
|
public void testDuplicationNames() throws Exception
|
||||||
{
|
{
|
||||||
checkTransformer();
|
checkTransformer();
|
||||||
|
|
||||||
NodeRef jpgOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_JPEG);
|
final NodeRef jpgOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_JPEG);
|
||||||
ImageResizeOptions imageResizeOptions = new ImageResizeOptions();
|
|
||||||
|
setComplete();
|
||||||
|
endTransaction();
|
||||||
|
|
||||||
|
final ImageResizeOptions imageResizeOptions = new ImageResizeOptions();
|
||||||
imageResizeOptions.setWidth(64);
|
imageResizeOptions.setWidth(64);
|
||||||
imageResizeOptions.setHeight(64);
|
imageResizeOptions.setHeight(64);
|
||||||
imageResizeOptions.setResizeToThumbnail(true);
|
imageResizeOptions.setResizeToThumbnail(true);
|
||||||
ImageTransformationOptions imageTransformationOptions = new ImageTransformationOptions();
|
final ImageTransformationOptions imageTransformationOptions = new ImageTransformationOptions();
|
||||||
imageTransformationOptions.setResizeOptions(imageResizeOptions);
|
imageTransformationOptions.setResizeOptions(imageResizeOptions);
|
||||||
// ThumbnailDetails createOptions = new ThumbnailDetails();
|
|
||||||
NodeRef thumbnail1 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT,
|
final NodeRef thumbnail1 = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<NodeRef>()
|
||||||
MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions, "small");
|
{
|
||||||
|
@Override
|
||||||
|
public NodeRef execute() throws Throwable
|
||||||
|
{
|
||||||
|
NodeRef thumbnail1 = thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT,
|
||||||
|
MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions, "small");
|
||||||
|
return thumbnail1;
|
||||||
|
}
|
||||||
|
}, false, true);
|
||||||
|
|
||||||
assertNotNull(thumbnail1);
|
assertNotNull(thumbnail1);
|
||||||
checkRenditioned(jpgOrig,
|
checkRenditioned(jpgOrig,
|
||||||
Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "small", 1)));
|
Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "small", 1)));
|
||||||
checkRendition("small", thumbnail1);
|
checkRendition(jpgOrig, "small", thumbnail1);
|
||||||
|
|
||||||
// the origional thumbnail is returned if we are attempting to create a duplicate
|
// the origional thumbnail is returned if we are attempting to create a duplicate
|
||||||
NodeRef duplicate = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, MimetypeMap.MIMETYPE_IMAGE_JPEG,
|
final NodeRef duplicate = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<NodeRef>()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public NodeRef execute() throws Throwable
|
||||||
|
{
|
||||||
|
NodeRef thumbnail = thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, MimetypeMap.MIMETYPE_IMAGE_JPEG,
|
||||||
imageTransformationOptions, "small");
|
imageTransformationOptions, "small");
|
||||||
|
return thumbnail;
|
||||||
|
}
|
||||||
|
}, false, true);
|
||||||
|
|
||||||
assertNotNull(duplicate);
|
assertNotNull(duplicate);
|
||||||
assertEquals(duplicate, thumbnail1);
|
assertEquals(duplicate, thumbnail1);
|
||||||
}
|
}
|
||||||
@@ -588,6 +758,96 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
assertEquals(ContentModel.TYPE_THUMBNAIL, secureNodeService.getType(thumbnail1));
|
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
|
public void testGetThumbnailByName() throws Exception
|
||||||
{
|
{
|
||||||
checkTransformer();
|
checkTransformer();
|
||||||
@@ -608,10 +868,13 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, MimetypeMap.MIMETYPE_IMAGE_JPEG,
|
this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, MimetypeMap.MIMETYPE_IMAGE_JPEG,
|
||||||
imageTransformationOptions, "small");
|
imageTransformationOptions, "small");
|
||||||
|
|
||||||
|
setComplete();
|
||||||
|
endTransaction();
|
||||||
|
|
||||||
// Try and retrieve the thumbnail
|
// Try and retrieve the thumbnail
|
||||||
NodeRef result2 = this.thumbnailService.getThumbnailByName(jpgOrig, ContentModel.PROP_CONTENT, "small");
|
NodeRef result2 = this.thumbnailService.getThumbnailByName(jpgOrig, ContentModel.PROP_CONTENT, "small");
|
||||||
assertNotNull(result2);
|
assertNotNull(result2);
|
||||||
checkRendition("small", result2);
|
checkRendition(jpgOrig, "small", result2);
|
||||||
|
|
||||||
// Check for an other thumbnail that doesn't exist
|
// Check for an other thumbnail that doesn't exist
|
||||||
NodeRef result3 = this.thumbnailService.getThumbnailByName(jpgOrig, ContentModel.PROP_CONTENT, "anotherone");
|
NodeRef result3 = this.thumbnailService.getThumbnailByName(jpgOrig, ContentModel.PROP_CONTENT, "anotherone");
|
||||||
@@ -621,14 +884,23 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
private static class ExpectedAssoc
|
private static class ExpectedAssoc
|
||||||
{
|
{
|
||||||
private QNamePattern assocTypeQName;
|
private QNamePattern assocTypeQName;
|
||||||
private String assocName;
|
private QNamePattern assocName;
|
||||||
private int count;
|
private int count;
|
||||||
|
|
||||||
public ExpectedAssoc(QNamePattern assocTypeQName, String assocName, int count)
|
public ExpectedAssoc(QNamePattern assocTypeQName, String assocName, int count)
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
|
this.assocName = assocName != null
|
||||||
|
? QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, assocName) : null;
|
||||||
this.assocTypeQName = assocTypeQName;
|
this.assocTypeQName = assocTypeQName;
|
||||||
|
this.count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExpectedAssoc(QNamePattern assocTypeQName, QNamePattern assocName, int count)
|
||||||
|
{
|
||||||
|
super();
|
||||||
this.assocName = assocName;
|
this.assocName = assocName;
|
||||||
|
this.assocTypeQName = assocTypeQName;
|
||||||
this.count = count;
|
this.count = count;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -637,7 +909,7 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
return assocTypeQName;
|
return assocTypeQName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAssocName()
|
public QNamePattern getAssocName()
|
||||||
{
|
{
|
||||||
return assocName;
|
return assocName;
|
||||||
}
|
}
|
||||||
@@ -692,21 +964,21 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkRenditioned(NodeRef contentNodeRef, List<ExpectedAssoc> expectedAssocs) {
|
private void checkRenditioned(NodeRef contentNodeRef, List<ExpectedAssoc> expectedAssocs)
|
||||||
|
{
|
||||||
assertTrue("Renditioned aspect should have been applied",
|
assertTrue("Renditioned aspect should have been applied",
|
||||||
this.secureNodeService.hasAspect(contentNodeRef, RenditionModel.ASPECT_RENDITIONED));
|
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<ChildAssociationRef> assocs = this.secureNodeService.getChildAssocs(contentNodeRef,
|
List<ChildAssociationRef> assocs = this.secureNodeService.getChildAssocs(contentNodeRef,
|
||||||
expectedAssoc.getAssocTypeQName(), qNamePattern);
|
expectedAssoc.getAssocTypeQName(), expectedAssoc.getAssocName());
|
||||||
assertNotNull(assocs);
|
assertNotNull(assocs);
|
||||||
assertEquals(expectedAssoc + " association count mismatch", expectedAssoc.getCount(), assocs.size());
|
assertEquals(expectedAssoc + " association count mismatch", expectedAssoc.getCount(), assocs.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkRendition(String thumbnailName, NodeRef thumbnail)
|
private void checkRendition(final NodeRef sourceNode, final String thumbnailName, final NodeRef thumbnail)
|
||||||
{
|
{
|
||||||
// Check the thumbnail is of the correct type
|
// Check the thumbnail is of the correct type
|
||||||
assertTrue("Thumbnail should have been a rendition",
|
assertTrue("Thumbnail should have been a rendition",
|
||||||
@@ -732,6 +1004,29 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
ContentData thumbnailData = (ContentData) secureNodeService.getProperty(thumbnail, ContentModel.PROP_CONTENT);
|
ContentData thumbnailData = (ContentData) secureNodeService.getProperty(thumbnail, ContentModel.PROP_CONTENT);
|
||||||
assertNotNull("Thumbnail data was null", thumbnailData);
|
assertNotNull("Thumbnail data was null", thumbnailData);
|
||||||
assertTrue("Thumbnail data was empty", thumbnailData.getSize() > 0);
|
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<String>()
|
||||||
|
{
|
||||||
|
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
|
private void outputThumbnailTempContentLocation(NodeRef thumbnail, String ext, String message) throws IOException
|
||||||
@@ -769,7 +1064,18 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
|
|
||||||
return node;
|
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
|
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.
|
// The below pdf file has been truncated such that it is identifiable as a PDF but otherwise corrupt.
|
||||||
@@ -870,33 +1176,74 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
.getChildRef();
|
.getChildRef();
|
||||||
|
|
||||||
checkTransformer();
|
checkTransformer();
|
||||||
NodeRef jpgOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_JPEG);
|
final NodeRef jpgOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_JPEG);
|
||||||
|
|
||||||
ImageResizeOptions imageResizeOptions = new ImageResizeOptions();
|
setComplete();
|
||||||
|
endTransaction();
|
||||||
|
|
||||||
|
final ImageResizeOptions imageResizeOptions = new ImageResizeOptions();
|
||||||
imageResizeOptions.setWidth(64);
|
imageResizeOptions.setWidth(64);
|
||||||
imageResizeOptions.setHeight(64);
|
imageResizeOptions.setHeight(64);
|
||||||
imageResizeOptions.setResizeToThumbnail(true);
|
imageResizeOptions.setResizeToThumbnail(true);
|
||||||
ImageTransformationOptions imageTransformationOptions = new ImageTransformationOptions();
|
final ImageTransformationOptions imageTransformationOptions = new ImageTransformationOptions();
|
||||||
imageTransformationOptions.setResizeOptions(imageResizeOptions);
|
imageTransformationOptions.setResizeOptions(imageResizeOptions);
|
||||||
|
|
||||||
// Create thumbnail - same MIME type
|
final NodeRef thumbnail1 = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<NodeRef>()
|
||||||
NodeRef thumbnail1 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT,
|
{
|
||||||
MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions, "smallJpeg");
|
@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);
|
||||||
|
|
||||||
assertNotNull(thumbnail1);
|
assertNotNull(thumbnail1);
|
||||||
checkRenditioned(jpgOrig,
|
|
||||||
Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "smallJpeg", 1)));
|
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>()
|
||||||
checkRendition("smallJpeg", thumbnail1);
|
{
|
||||||
outputThumbnailTempContentLocation(thumbnail1, "jpg", "smallJpeg - 64x64, marked as thumbnail");
|
@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);
|
||||||
|
|
||||||
// Create thumbnail - different MIME type
|
// Create thumbnail - different MIME type
|
||||||
thumbnail1 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT,
|
final NodeRef thumbnail2 = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<NodeRef>()
|
||||||
MimetypeMap.MIMETYPE_IMAGE_PNG, imageTransformationOptions, "smallPng");
|
{
|
||||||
assertNotNull(thumbnail1);
|
@Override
|
||||||
checkRenditioned(jpgOrig,
|
public NodeRef execute() throws Throwable
|
||||||
Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "smallPng", 1)));
|
{
|
||||||
checkRendition("smallPng", thumbnail1);
|
// Create thumbnail - same MIME type
|
||||||
outputThumbnailTempContentLocation(thumbnail1, "png", "smallPng - 64x64, marked as thumbnail");
|
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<Void>()
|
||||||
|
{
|
||||||
|
@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);
|
||||||
|
|
||||||
// Create thumbnail - different content property
|
// Create thumbnail - different content property
|
||||||
// TODO
|
// TODO
|
||||||
|
|
||||||
@@ -906,8 +1253,18 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
imageTransformationOptions.setCommandOptions("-noSuchOption");
|
imageTransformationOptions.setCommandOptions("-noSuchOption");
|
||||||
thumbnail1 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT,
|
|
||||||
MimetypeMap.MIMETYPE_IMAGE_PNG, imageTransformationOptions, "smallCO");
|
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>()
|
||||||
|
{
|
||||||
|
@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);
|
||||||
} catch (ContentIOException ciox)
|
} catch (ContentIOException ciox)
|
||||||
{
|
{
|
||||||
x = ciox;
|
x = ciox;
|
||||||
@@ -919,28 +1276,46 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
|
|
||||||
|
|
||||||
// Create thumbnail - different target assoc details
|
// Create thumbnail - different target assoc details
|
||||||
ThumbnailParentAssociationDetails tpad
|
final ThumbnailParentAssociationDetails tpad
|
||||||
= new ThumbnailParentAssociationDetails(otherFolder,
|
= new ThumbnailParentAssociationDetails(otherFolder,
|
||||||
QName.createQName(NamespaceService.RENDITION_MODEL_1_0_URI, "foo"),
|
QName.createQName(NamespaceService.RENDITION_MODEL_1_0_URI, "foo"),
|
||||||
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "bar"));
|
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "bar"));
|
||||||
thumbnail1 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT,
|
final NodeRef thumbnail4 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT,
|
||||||
MimetypeMap.MIMETYPE_IMAGE_PNG, imageTransformationOptions, "targetDetails", tpad);
|
MimetypeMap.MIMETYPE_IMAGE_PNG, imageTransformationOptions, "targetDetails", tpad);
|
||||||
assertNotNull(thumbnail1);
|
assertNotNull(thumbnail4);
|
||||||
checkRenditioned(jpgOrig,
|
|
||||||
Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "targetDetails", 1)));
|
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>()
|
||||||
checkRendition("targetDetails", thumbnail1);
|
{
|
||||||
outputThumbnailTempContentLocation(thumbnail1, "png", "targetDetails - 64x64, marked as thumbnail");
|
@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);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Create thumbnail - null thumbnail name
|
// Create thumbnail - null thumbnail name
|
||||||
thumbnail1 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT,
|
final NodeRef thumbnail5 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT,
|
||||||
MimetypeMap.MIMETYPE_IMAGE_PNG, imageTransformationOptions, null);
|
MimetypeMap.MIMETYPE_IMAGE_PNG, imageTransformationOptions, null);
|
||||||
assertNotNull(thumbnail1);
|
assertNotNull(thumbnail5);
|
||||||
checkRenditioned(jpgOrig,
|
|
||||||
Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, null, 5)));
|
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>()
|
||||||
checkRendition(null, thumbnail1);
|
{
|
||||||
outputThumbnailTempContentLocation(thumbnail1, "png", "'null' - 64x64, marked as thumbnail");
|
@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);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testRegistry()
|
public void testRegistry()
|
||||||
@@ -1005,9 +1380,8 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
assertEquals(standardMediumIcon, mediumIcon);
|
assertEquals(standardMediumIcon, mediumIcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void performLongRunningThumbnailTest(final List<ExpectedThumbnail> expectedThumbnails,
|
protected void performLongRunningThumbnailTest(final List<ExpectedThumbnail> expectedThumbnails, final List<ExpectedAssoc> expectedAssocs,
|
||||||
final List<ExpectedAssoc> expectedAssocs, final LongRunningConcurrentWork concurrentWork,
|
final LongRunningConcurrentWork concurrentWork, final Integer retryPeriod, final Integer quietPeriod) throws Exception
|
||||||
final Integer retryPeriod, final Integer quietPeriod) throws Exception
|
|
||||||
{
|
{
|
||||||
long saveRetryPeriod = failureHandlingOptions.getRetryPeriod();
|
long saveRetryPeriod = failureHandlingOptions.getRetryPeriod();
|
||||||
long saveQuietPeriod = failureHandlingOptions.getQuietPeriod();
|
long saveQuietPeriod = failureHandlingOptions.getQuietPeriod();
|
||||||
@@ -1041,7 +1415,8 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create our thumbnail(s)
|
// Create our thumbnail(s)
|
||||||
for (ExpectedThumbnail expectedThumbnail : expectedThumbnails) {
|
for (ExpectedThumbnail expectedThumbnail : expectedThumbnails)
|
||||||
|
{
|
||||||
ThumbnailDefinition thumbnailDef = thumbnailService.getThumbnailRegistry()
|
ThumbnailDefinition thumbnailDef = thumbnailService.getThumbnailRegistry()
|
||||||
.getThumbnailDefinition(expectedThumbnail.getThumbnailName());
|
.getThumbnailDefinition(expectedThumbnail.getThumbnailName());
|
||||||
|
|
||||||
@@ -1056,13 +1431,15 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
|
|
||||||
// Thumbnailing process(es) are running in other threads, do the
|
// Thumbnailing process(es) are running in other threads, do the
|
||||||
// concurrent work here
|
// concurrent work here
|
||||||
if (concurrentWork != null) {
|
if (concurrentWork != null)
|
||||||
|
{
|
||||||
logger.debug("Starting concurrent work for " + source);
|
logger.debug("Starting concurrent work for " + source);
|
||||||
concurrentWork.run(source);
|
concurrentWork.run(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify our concurrent work ran successfully
|
// Verify our concurrent work ran successfully
|
||||||
if (concurrentWork != null) {
|
if (concurrentWork != null)
|
||||||
|
{
|
||||||
logger.debug("Verifying concurrent work for " + source);
|
logger.debug("Verifying concurrent work for " + source);
|
||||||
concurrentWork.verify(source);
|
concurrentWork.verify(source);
|
||||||
}
|
}
|
||||||
@@ -1071,17 +1448,21 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
|
|
||||||
// Wait for thumbnail(s) to finish
|
// Wait for thumbnail(s) to finish
|
||||||
long endTime = (new Date()).getTime();
|
long endTime = (new Date()).getTime();
|
||||||
for (final ExpectedThumbnail expectedThumbnail : expectedThumbnails) {
|
for (final ExpectedThumbnail expectedThumbnail : expectedThumbnails)
|
||||||
|
{
|
||||||
NodeRef thumbnail = null;
|
NodeRef thumbnail = null;
|
||||||
while ((endTime - startTime) < (TEST_LONG_RUNNING_TRANSFORM_TIME * numIterations)) {
|
while ((endTime - startTime) < (TEST_LONG_RUNNING_TRANSFORM_TIME * numIterations))
|
||||||
|
{
|
||||||
thumbnail = transactionService.getRetryingTransactionHelper()
|
thumbnail = transactionService.getRetryingTransactionHelper()
|
||||||
.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<NodeRef>() {
|
.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<NodeRef>()
|
||||||
|
{
|
||||||
public NodeRef execute() throws Throwable {
|
public NodeRef execute() throws Throwable {
|
||||||
return thumbnailService.getThumbnailByName(source, ContentModel.PROP_CONTENT,
|
return thumbnailService.getThumbnailByName(source, ContentModel.PROP_CONTENT,
|
||||||
expectedThumbnail.getThumbnailName());
|
expectedThumbnail.getThumbnailName());
|
||||||
}
|
}
|
||||||
}, false, true);
|
}, false, true);
|
||||||
if (thumbnail == null) {
|
if (thumbnail == null)
|
||||||
|
{
|
||||||
Thread.sleep(200);
|
Thread.sleep(200);
|
||||||
logger.debug("Elapsed " + (endTime - startTime) + " ms of "
|
logger.debug("Elapsed " + (endTime - startTime) + " ms of "
|
||||||
+ TEST_LONG_RUNNING_TRANSFORM_TIME * numIterations + " ms waiting for "
|
+ TEST_LONG_RUNNING_TRANSFORM_TIME * numIterations + " ms waiting for "
|
||||||
@@ -1096,27 +1477,33 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
transactionService.getRetryingTransactionHelper()
|
transactionService.getRetryingTransactionHelper()
|
||||||
.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>() {
|
.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>()
|
||||||
public Void execute() throws Throwable {
|
{
|
||||||
|
public Void execute() throws Throwable
|
||||||
|
{
|
||||||
// Verify that the thumbnail(s) was/were created
|
// Verify that the thumbnail(s) was/were created
|
||||||
for (final ExpectedThumbnail expectedThumbnail : expectedThumbnails) {
|
for (final ExpectedThumbnail expectedThumbnail : expectedThumbnails)
|
||||||
|
{
|
||||||
String thumbnailName = expectedThumbnail.getThumbnailName();
|
String thumbnailName = expectedThumbnail.getThumbnailName();
|
||||||
NodeRef thumbnailNodeRef = thumbnailService.getThumbnailByName(source,
|
NodeRef thumbnailNodeRef = thumbnailService.getThumbnailByName(source,
|
||||||
ContentModel.PROP_CONTENT, thumbnailName);
|
ContentModel.PROP_CONTENT, thumbnailName);
|
||||||
checkRendition(thumbnailName, thumbnailNodeRef);
|
checkRendition(source, thumbnailName, thumbnailNodeRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify associations
|
// verify associations
|
||||||
checkRenditioned(source, expectedAssocs);
|
checkRenditioned(source, expectedAssocs);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// we expect the transformer to run once for each thumbnail
|
||||||
|
assertEquals(expectedThumbnails.size(), transformer.getTransformCount());
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
failureHandlingOptions.setRetryPeriod(saveRetryPeriod);
|
failureHandlingOptions.setRetryPeriod(saveRetryPeriod);
|
||||||
failureHandlingOptions.setQuietPeriod(saveQuietPeriod);
|
failureHandlingOptions.setQuietPeriod(saveQuietPeriod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1129,10 +1516,9 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
public void testLongRunningThumbnails() throws Exception
|
public void testLongRunningThumbnails() throws Exception
|
||||||
{
|
{
|
||||||
logger.debug("Starting testLongRunningThumbnails");
|
logger.debug("Starting testLongRunningThumbnails");
|
||||||
performLongRunningThumbnailTest(
|
performLongRunningThumbnailTest(Collections.singletonList(ExpectedThumbnail.withName("imgpreview")),
|
||||||
Collections.singletonList(ExpectedThumbnail.withName("imgpreview")),
|
Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "imgpreview", 1)),
|
||||||
Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "imgpreview", 1)),
|
new EmptyLongRunningConcurrentWork(), 60, null);
|
||||||
new EmptyLongRunningConcurrentWork(), 60, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1166,22 +1552,18 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// we expect an imgpreview thumbnail association but no failed thumbnail association
|
||||||
|
List<ExpectedAssoc> 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(
|
performLongRunningThumbnailTest(
|
||||||
Collections.singletonList(ExpectedThumbnail.withName("imgpreview")),
|
Collections.singletonList(ExpectedThumbnail.withName("imgpreview")), expectedAssocs, updatePropertyWork, 1, null);
|
||||||
Collections.singletonList(new ExpectedAssoc(RegexQNamePattern.MATCH_ALL, "imgpreview", 1)),
|
|
||||||
updatePropertyWork, 1, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies that multiple thumbnails can be successfully created.
|
* 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
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public void testCreateMultipleLongRunningThumbnails() throws Exception
|
public void testCreateMultipleLongRunningThumbnails() throws Exception
|
||||||
@@ -1198,7 +1580,6 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
List<ExpectedAssoc> expectedAssocs = new ArrayList<>(5);
|
List<ExpectedAssoc> expectedAssocs = new ArrayList<>(5);
|
||||||
expectedAssocs.add(new ExpectedAssoc(RenditionModel.ASSOC_RENDITION, "imgpreview", 1));
|
expectedAssocs.add(new ExpectedAssoc(RenditionModel.ASSOC_RENDITION, "imgpreview", 1));
|
||||||
expectedAssocs.add(new ExpectedAssoc(RenditionModel.ASSOC_RENDITION, "avatar", 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);
|
performLongRunningThumbnailTest(expectedThumbnails, expectedAssocs, new EmptyLongRunningConcurrentWork(), 1, 1);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user