REPO-4099 Change of mimetype to unsupported rendition hangs Share (#298)

An exception was being thrown if there was no supported rendition. In the case where a rendition already existed, RenditionService2 should have removed the content and hashcode on the rendition node and updated the source nodes thumbnailLastModified value. This is what happens if there is a transform failure when there is a rendition available. Also found that changing the mimetype nut not the content was not resulting in a new rendition as the hashcode did not include the mimetype.
This commit is contained in:
alandavis
2018-12-13 16:34:31 +00:00
committed by GitHub
parent d3a00e3f91
commit 544e40ec54
10 changed files with 186 additions and 112 deletions

View File

@@ -38,7 +38,7 @@
<dependency.alfresco-legacy-lucene.version>6.2</dependency.alfresco-legacy-lucene.version> <dependency.alfresco-legacy-lucene.version>6.2</dependency.alfresco-legacy-lucene.version>
<dependency.alfresco-core.version>7.5</dependency.alfresco-core.version> <dependency.alfresco-core.version>7.5</dependency.alfresco-core.version>
<dependency.alfresco-greenmail.version>6.1</dependency.alfresco-greenmail.version> <dependency.alfresco-greenmail.version>6.1</dependency.alfresco-greenmail.version>
<dependency.alfresco-data-model.version>8.22</dependency.alfresco-data-model.version> <dependency.alfresco-data-model.version>8.25</dependency.alfresco-data-model.version>
<dependency.alfresco-jlan.version>7.1</dependency.alfresco-jlan.version> <dependency.alfresco-jlan.version>7.1</dependency.alfresco-jlan.version>
<dependency.alfresco-pdf-renderer.version>1.1</dependency.alfresco-pdf-renderer.version> <dependency.alfresco-pdf-renderer.version>1.1</dependency.alfresco-pdf-renderer.version>
<dependency.alfresco-hb-data-sender.version>1.0.9</dependency.alfresco-hb-data-sender.version> <dependency.alfresco-hb-data-sender.version>1.0.9</dependency.alfresco-hb-data-sender.version>

View File

@@ -271,6 +271,7 @@ public class RenditionServiceImpl implements
public ChildAssociationRef render(NodeRef sourceNode, RenditionDefinition definition) public ChildAssociationRef render(NodeRef sourceNode, RenditionDefinition definition)
{ {
checkSourceNodeForPreventionClass(sourceNode); checkSourceNodeForPreventionClass(sourceNode);
log.debug("Original RenditionService render no callback START");
ChildAssociationRef result = executeRenditionAction(sourceNode, definition, false); ChildAssociationRef result = executeRenditionAction(sourceNode, definition, false);
@@ -279,6 +280,7 @@ public class RenditionServiceImpl implements
log.debug("Produced rendition " + result); log.debug("Produced rendition " + result);
} }
log.debug("Original RenditionService render no callback END");
return result; return result;
} }
@@ -286,14 +288,14 @@ public class RenditionServiceImpl implements
RenderCallback callback) RenderCallback callback)
{ {
checkSourceNodeForPreventionClass(sourceNode); checkSourceNodeForPreventionClass(sourceNode);
log.debug("Original RenditionService render callback START");
// The asynchronous render can't return a ChildAssociationRef as it is created // The asynchronous render can't return a ChildAssociationRef as it is created
// asynchronously after this method returns. // asynchronously after this method returns.
definition.setCallback(callback); definition.setCallback(callback);
executeRenditionAction(sourceNode, definition, true); executeRenditionAction(sourceNode, definition, true);
log.debug("Original RenditionService render callback END");
return;
} }
/* /*

View File

@@ -128,7 +128,7 @@ public class LegacyLocalTransformClient extends AbstractTransformClient implemen
} }
@Override @Override
public void transform(NodeRef sourceNodeRef, RenditionDefinition2 renditionDefinition, String user, int sourceContentUrlHashCode) public void transform(NodeRef sourceNodeRef, RenditionDefinition2 renditionDefinition, String user, int sourceContentHashCode)
{ {
executorService.submit(() -> executorService.submit(() ->
{ {
@@ -155,7 +155,7 @@ public class LegacyLocalTransformClient extends AbstractTransformClient implemen
contentService.transform(reader, writer, transformationOptions); contentService.transform(reader, writer, transformationOptions);
InputStream inputStream = writer.getReader().getContentInputStream(); InputStream inputStream = writer.getReader().getContentInputStream();
renditionService2.consume(sourceNodeRef, inputStream, renditionDefinition, sourceContentUrlHashCode); renditionService2.consume(sourceNodeRef, inputStream, renditionDefinition, sourceContentHashCode);
} }
catch (Exception e) catch (Exception e)
{ {
@@ -164,7 +164,7 @@ public class LegacyLocalTransformClient extends AbstractTransformClient implemen
String renditionName = renditionDefinition.getRenditionName(); String renditionName = renditionDefinition.getRenditionName();
logger.debug("Rendition of "+renditionName+" failed", e); logger.debug("Rendition of "+renditionName+" failed", e);
} }
renditionService2.failure(sourceNodeRef, renditionDefinition, sourceContentUrlHashCode); renditionService2.failure(sourceNodeRef, renditionDefinition, sourceContentHashCode);
throw e; throw e;
} }
return null; return null;

View File

@@ -61,9 +61,10 @@ 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 java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.alfresco.model.ContentModel.PROP_CONTENT; import static org.alfresco.model.ContentModel.PROP_CONTENT;
import static org.alfresco.model.RenditionModel.PROP_RENDITION_CONTENT_URL_HASH_CODE; import static org.alfresco.model.RenditionModel.PROP_RENDITION_CONTENT_HASH_CODE;
import static org.alfresco.service.namespace.QName.createQName; import static org.alfresco.service.namespace.QName.createQName;
/** /**
@@ -209,39 +210,68 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
logger.debug("Request transform for rendition " + renditionName + " on " +sourceNodeRef); logger.debug("Request transform for rendition " + renditionName + " on " +sourceNodeRef);
} }
AtomicBoolean supported = new AtomicBoolean(true);
ContentData contentData = (ContentData) nodeService.getProperty(sourceNodeRef, ContentModel.PROP_CONTENT); ContentData contentData = (ContentData) nodeService.getProperty(sourceNodeRef, ContentModel.PROP_CONTENT);
if (contentData != null && contentData.getContentUrl() != null) if (contentData != null && contentData.getContentUrl() != null)
{ {
String contentUrl = contentData.getContentUrl(); String contentUrl = contentData.getContentUrl();
String sourceMimetype = contentData.getMimetype(); String sourceMimetype = contentData.getMimetype();
long size = contentData.getSize(); long size = contentData.getSize();
transformClient.checkSupported(sourceNodeRef, renditionDefinition, sourceMimetype, size, contentUrl); try
{
transformClient.checkSupported(sourceNodeRef, renditionDefinition, sourceMimetype, size, contentUrl);
}
catch (UnsupportedOperationException e)
{
NodeRef renditionNode = getRenditionNode(sourceNodeRef, renditionName);
if (renditionNode == null)
{
throw e;
}
supported.set(false);
}
} }
String user = AuthenticationUtil.getRunAsUser(); String user = AuthenticationUtil.getRunAsUser();
RetryingTransactionHelper.RetryingTransactionCallback callback = () -> RetryingTransactionHelper.RetryingTransactionCallback callback = () ->
{ {
// Avoid doing extra transforms that have already been done. int sourceContentHashCode = getSourceContentHashCode(sourceNodeRef);
int sourceContentUrlHashCode = getSourceContentUrlHashCode(sourceNodeRef); if (!supported.get())
NodeRef renditionNode = getRenditionNode(sourceNodeRef, renditionName);
int renditionContentUrlHashCode = getRenditionContentUrlHashCode(renditionNode);
if (renditionContentUrlHashCode == sourceContentUrlHashCode)
{
throw new IllegalStateException("The rendition " + renditionName + " has already been created.");
}
// If source node has content
if (sourceContentUrlHashCode != SOURCE_HAS_NO_CONTENT)
{
transformClient.transform(sourceNodeRef, renditionDefinition, user, sourceContentUrlHashCode);
}
else
{ {
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
logger.debug("Rendition of "+renditionName+" had no content."); logger.debug("Rendition of " + renditionName + " is no longer supported. " +
"The mimetype might have changed or the content is now too big.");
}
failure(sourceNodeRef, renditionDefinition, sourceContentHashCode);
}
else
{
// Avoid doing extra transforms that have already been done.
NodeRef renditionNode = getRenditionNode(sourceNodeRef, renditionName);
int renditionContentHashCode = getRenditionContentHashCode(renditionNode);
if (logger.isDebugEnabled())
{
logger.debug("Render: Source " + sourceContentHashCode + " rendition " + renditionContentHashCode+ " hashCodes");
}
if (renditionContentHashCode == sourceContentHashCode)
{
throw new IllegalStateException("The rendition " + renditionName + " has already been created.");
}
// If source node has content
if (sourceContentHashCode != SOURCE_HAS_NO_CONTENT)
{
transformClient.transform(sourceNodeRef, renditionDefinition, user, sourceContentHashCode);
}
else
{
if (logger.isDebugEnabled())
{
logger.debug("Rendition of " + renditionName + " had no content.");
}
failure(sourceNodeRef, renditionDefinition, sourceContentHashCode);
} }
failure(sourceNodeRef, renditionDefinition, sourceContentUrlHashCode);
} }
return null; return null;
}; };
@@ -254,13 +284,13 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
} }
} }
public void failure(NodeRef sourceNodeRef, RenditionDefinition2 renditionDefinition, int transformContentUrlHashCode) public void failure(NodeRef sourceNodeRef, RenditionDefinition2 renditionDefinition, int transformContentHashCode)
{ {
// The original transaction may have already have failed // The original transaction may have already have failed
AuthenticationUtil.runAsSystem((AuthenticationUtil.RunAsWork<Void>) () -> AuthenticationUtil.runAsSystem((AuthenticationUtil.RunAsWork<Void>) () ->
transactionService.getRetryingTransactionHelper().doInTransaction(() -> transactionService.getRetryingTransactionHelper().doInTransaction(() ->
{ {
consume(sourceNodeRef, null, renditionDefinition, transformContentUrlHashCode); consume(sourceNodeRef, null, renditionDefinition, transformContentHashCode);
return null; return null;
}, false, true)); }, false, true));
} }
@@ -271,12 +301,15 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
* If the transformInputStream is null, this is taken to be a transform failure. * If the transformInputStream is null, this is taken to be a transform failure.
*/ */
public void consume(NodeRef sourceNodeRef, InputStream transformInputStream, RenditionDefinition2 renditionDefinition, public void consume(NodeRef sourceNodeRef, InputStream transformInputStream, RenditionDefinition2 renditionDefinition,
int transformContentUrlHashCode) int transformContentHashCode)
{ {
String renditionName = renditionDefinition.getRenditionName(); String renditionName = renditionDefinition.getRenditionName();
int sourceContentUrlHashCode = getSourceContentUrlHashCode(sourceNodeRef); int sourceContentHashCode = getSourceContentHashCode(sourceNodeRef);
if (logger.isDebugEnabled())
if (transformContentUrlHashCode != sourceContentUrlHashCode) {
logger.debug("Consume: Source " + sourceContentHashCode + " and transform's source " + transformContentHashCode+" hashcodes");
}
if (transformContentHashCode != sourceContentHashCode)
{ {
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
@@ -321,7 +354,11 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
logger.debug("Added rendition2 aspect to rendition " + renditionName + " on " + sourceNodeRef); logger.debug("Added rendition2 aspect to rendition " + renditionName + " on " + sourceNodeRef);
} }
} }
nodeService.setProperty(renditionNode, RenditionModel.PROP_RENDITION_CONTENT_URL_HASH_CODE, transformContentUrlHashCode); if (logger.isDebugEnabled())
{
logger.debug("Set rendition hashcode " + transformContentHashCode + " and ThumbnailLastModified for " + renditionName);
}
nodeService.setProperty(renditionNode, RenditionModel.PROP_RENDITION_CONTENT_HASH_CODE, transformContentHashCode);
setThumbnailLastModified(sourceNodeRef, renditionName); setThumbnailLastModified(sourceNodeRef, renditionName);
if (transformInputStream != null) if (transformInputStream != null)
@@ -348,6 +385,11 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
if (content != null) if (content != null)
{ {
nodeService.removeProperty(renditionNode, PROP_CONTENT); nodeService.removeProperty(renditionNode, PROP_CONTENT);
nodeService.removeProperty(renditionNode, PROP_RENDITION_CONTENT_HASH_CODE);
if (logger.isDebugEnabled())
{
logger.debug("Cleared rendition content and hashcode");
}
} }
} }
@@ -445,16 +487,17 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
* Returns the hash code of the source node's content url. As transformations may be returned in a different * Returns the hash code of the source node's content url. As transformations may be returned in a different
* sequences to which they were requested, this is used work out if a rendition should be replaced. * sequences to which they were requested, this is used work out if a rendition should be replaced.
*/ */
private int getSourceContentUrlHashCode(NodeRef sourceNodeRef) private int getSourceContentHashCode(NodeRef sourceNodeRef)
{ {
int hashCode = SOURCE_HAS_NO_CONTENT; int hashCode = SOURCE_HAS_NO_CONTENT;
ContentData contentData = DefaultTypeConverter.INSTANCE.convert(ContentData.class, nodeService.getProperty(sourceNodeRef, PROP_CONTENT)); ContentData contentData = DefaultTypeConverter.INSTANCE.convert(ContentData.class, nodeService.getProperty(sourceNodeRef, PROP_CONTENT));
if (contentData != null) if (contentData != null)
{ {
String contentUrl = contentData.getContentUrl(); // Originally we used the contentData URL, but that is not enough if the mimetype changes.
if (contentUrl != null) String contentString = contentData.toString();
if (contentString != null)
{ {
hashCode = contentUrl.hashCode(); hashCode = contentString.hashCode();
} }
} }
return hashCode; return hashCode;
@@ -463,13 +506,19 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
/** /**
* Returns the hash code of source node's content url on the rendition node (node may be null) if it does not exist. * Returns the hash code of source node's content url on the rendition node (node may be null) if it does not exist.
* Used work out if a rendition should be replaced. {@code -2} is returned if the rendition does not exist or was * Used work out if a rendition should be replaced. {@code -2} is returned if the rendition does not exist or was
* not created by RenditionService2. * not created by RenditionService2. {@code -1} is returned if there was no source content or the rendition failed.
*/ */
private int getRenditionContentUrlHashCode(NodeRef renditionNode) private int getRenditionContentHashCode(NodeRef renditionNode)
{ {
return renditionNode == null || !nodeService.hasAspect(renditionNode, RenditionModel.ASPECT_RENDITION2) if ( renditionNode == null || !nodeService.hasAspect(renditionNode, RenditionModel.ASPECT_RENDITION2))
? RENDITION2_DOES_NOT_EXIST {
: (int)nodeService.getProperty(renditionNode, PROP_RENDITION_CONTENT_URL_HASH_CODE); return RENDITION2_DOES_NOT_EXIST;
}
Serializable hashCode = nodeService.getProperty(renditionNode, PROP_RENDITION_CONTENT_HASH_CODE);
return hashCode == null
? SOURCE_HAS_NO_CONTENT
: (int)hashCode;
} }
private NodeRef getRenditionNode(NodeRef sourceNodeRef, String renditionName) private NodeRef getRenditionNode(NodeRef sourceNodeRef, String renditionName)
@@ -567,7 +616,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
/** /**
* Indicates if the rendition is available. Failed renditions (there was an error) don't have a contentUrl * Indicates if the rendition is available. Failed renditions (there was an error) don't have a contentUrl
* and out of date renditions or those still being created don't have a matching contentUrlHashCode. * and out of date renditions or those still being created don't have a matching contentHashCode.
*/ */
public boolean isRenditionAvailable(NodeRef sourceNodeRef, NodeRef renditionNode) public boolean isRenditionAvailable(NodeRef sourceNodeRef, NodeRef renditionNode)
{ {
@@ -581,9 +630,13 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
} }
else else
{ {
int sourceContentUrlHashCode = getSourceContentUrlHashCode(sourceNodeRef); int sourceContentHashCode = getSourceContentHashCode(sourceNodeRef);
int renditionContentUrlHashCode = getRenditionContentUrlHashCode(renditionNode); int renditionContentHashCode = getRenditionContentHashCode(renditionNode);
if (sourceContentUrlHashCode != renditionContentUrlHashCode) if (logger.isDebugEnabled())
{
logger.debug("isRenditionAvailable source " + sourceContentHashCode + " and rendition " + renditionContentHashCode+" hashcodes");
}
if (sourceContentHashCode != renditionContentHashCode)
{ {
available = false; available = false;
} }

View File

@@ -62,15 +62,15 @@ public class SwitchingTransformClient implements TransformClient
} }
@Override @Override
public void transform(NodeRef sourceNodeRef, RenditionDefinition2 renditionDefinition, String user, int sourceContentUrlHashCode) public void transform(NodeRef sourceNodeRef, RenditionDefinition2 renditionDefinition, String user, int sourceContentHashCode)
{ {
if (usePrimary.get()) if (usePrimary.get())
{ {
primary.transform(sourceNodeRef, renditionDefinition, user, sourceContentUrlHashCode); primary.transform(sourceNodeRef, renditionDefinition, user, sourceContentHashCode);
} }
else else
{ {
secondary.transform(sourceNodeRef, renditionDefinition, user, sourceContentUrlHashCode); secondary.transform(sourceNodeRef, renditionDefinition, user, sourceContentHashCode);
} }
} }
} }

View File

@@ -53,7 +53,7 @@ public interface TransformClient
* @param sourceNodeRef the source node * @param sourceNodeRef the source node
* @param renditionDefinition which rendition to perform * @param renditionDefinition which rendition to perform
* @param user that requested the transform. * @param user that requested the transform.
* @param sourceContentUrlHashCode the hash code of the source node's content URL. Used to check the transform result * @param sourceContentHashCode the hash code of the source node's content URL. Used to check the transform result
*/ */
void transform(NodeRef sourceNodeRef, RenditionDefinition2 renditionDefinition, String user, int sourceContentUrlHashCode); void transform(NodeRef sourceNodeRef, RenditionDefinition2 renditionDefinition, String user, int sourceContentHashCode);
} }

View File

@@ -153,7 +153,7 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
{ {
NodeRef sourceNodeRef = createSource(ADMIN, testFileName); NodeRef sourceNodeRef = createSource(ADMIN, testFileName);
render(ADMIN, sourceNodeRef, renditionName); render(ADMIN, sourceNodeRef, renditionName);
waitForRendition(ADMIN, sourceNodeRef, renditionName); waitForRendition(ADMIN, sourceNodeRef, renditionName, true);
if (!expectedToPass) if (!expectedToPass)
{ {
fail("The " + renditionName + " rendition should NOT be supported for " + testFileName); fail("The " + renditionName + " rendition should NOT be supported for " + testFileName);
@@ -241,11 +241,11 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
} }
// As a given user waitForRendition for a rendition to appear. Creates new transactions to do this. // As a given user waitForRendition for a rendition to appear. Creates new transactions to do this.
protected NodeRef waitForRendition(String user, NodeRef sourceNodeRef, String renditionName) throws AssertionFailedError protected NodeRef waitForRendition(String user, NodeRef sourceNodeRef, String renditionName, boolean shouldExist) throws AssertionFailedError
{ {
try try
{ {
return AuthenticationUtil.runAs(() -> waitForRendition(sourceNodeRef, renditionName), user); return AuthenticationUtil.runAs(() -> waitForRendition(sourceNodeRef, renditionName, shouldExist), user);
} }
catch (RuntimeException e) catch (RuntimeException e)
{ {
@@ -259,7 +259,7 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
} }
// As the current user waitForRendition for a rendition to appear. Creates new transactions to do this. // As the current user waitForRendition for a rendition to appear. Creates new transactions to do this.
private NodeRef waitForRendition(NodeRef sourceNodeRef, String renditionName) throws InterruptedException private NodeRef waitForRendition(NodeRef sourceNodeRef, String renditionName, boolean shouldExist) throws InterruptedException
{ {
long maxMillis = 10000; long maxMillis = 10000;
ChildAssociationRef assoc = null; ChildAssociationRef assoc = null;
@@ -275,8 +275,16 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
logger.debug("RenditionService2.getRenditionByName(...) sleep "+i); logger.debug("RenditionService2.getRenditionByName(...) sleep "+i);
sleep(1000); sleep(1000);
} }
assertNotNull("Rendition " + renditionName + " failed", assoc); if (shouldExist)
return assoc.getChildRef(); {
assertNotNull("Rendition " + renditionName + " failed", assoc);
return assoc.getChildRef();
}
else
{
assertNull("Rendition " + renditionName + " did not fail", assoc);
return null;
}
} }
protected String getTestFileName(String sourceMimetype) throws FileNotFoundException protected String getTestFileName(String sourceMimetype) throws FileNotFoundException

View File

@@ -137,10 +137,10 @@ public class LegacyLocalTransformClientIntegrationTest extends AbstractRendition
// split into separate transactions as the client is async // split into separate transactions as the client is async
NodeRef sourceNode = transactionService.getRetryingTransactionHelper().doInTransaction(() -> NodeRef sourceNode = transactionService.getRetryingTransactionHelper().doInTransaction(() ->
createContentNodeFromQuickFile(testFileName)); createContentNodeFromQuickFile(testFileName));
int sourceContentUrlHash = DefaultTypeConverter.INSTANCE.convert( int sourceContentHashCode = DefaultTypeConverter.INSTANCE.convert(
ContentData.class, ContentData.class,
nodeService.getProperty(sourceNode, PROP_CONTENT)) nodeService.getProperty(sourceNode, PROP_CONTENT))
.getContentUrl().hashCode(); .toString().hashCode();
transactionService.getRetryingTransactionHelper().doInTransaction(() -> transactionService.getRetryingTransactionHelper().doInTransaction(() ->
{ {
RenditionDefinition2 renditionDefinition = RenditionDefinition2 renditionDefinition =
@@ -149,7 +149,7 @@ public class LegacyLocalTransformClientIntegrationTest extends AbstractRendition
sourceNode, sourceNode,
renditionDefinition, renditionDefinition,
AuthenticationUtil.getAdminUserName(), AuthenticationUtil.getAdminUserName(),
sourceContentUrlHash); sourceContentHashCode);
return null; return null;
}); });
ChildAssociationRef childAssociationRef = null; ChildAssociationRef childAssociationRef = null;

View File

@@ -25,36 +25,23 @@
*/ */
package org.alfresco.repo.rendition2; package org.alfresco.repo.rendition2;
import java.util.List;
import junit.framework.AssertionFailedError;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.model.RenditionModel; import org.alfresco.model.RenditionModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.repo.thumbnail.ThumbnailRegistry;
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.ContentWriter; import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.security.PermissionService;
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.util.ApplicationContextHelper;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ResourceUtils;
import java.io.File; import java.util.List;
import java.io.FileNotFoundException;
import static java.lang.Thread.sleep;
import static org.alfresco.model.ContentModel.PROP_CONTENT; import static org.alfresco.model.ContentModel.PROP_CONTENT;
import static org.alfresco.repo.content.MimetypeMap.EXTENSION_BINARY; import static org.junit.Assert.assertNotEquals;
/** /**
* Integration tests for {@link RenditionService2} * Integration tests for {@link RenditionService2}
@@ -148,7 +135,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
{ {
NodeRef sourceNodeRef = createSource(ADMIN, "quick.jpg"); NodeRef sourceNodeRef = createSource(ADMIN, "quick.jpg");
render(ADMIN, sourceNodeRef, DOC_LIB); render(ADMIN, sourceNodeRef, DOC_LIB);
waitForRendition(ADMIN, sourceNodeRef, DOC_LIB); waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true);
} }
@Test @Test
@@ -156,12 +143,13 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
{ {
NodeRef sourceNodeRef = createSource(ADMIN, "quick.jpg"); NodeRef sourceNodeRef = createSource(ADMIN, "quick.jpg");
render(ADMIN, sourceNodeRef, DOC_LIB); render(ADMIN, sourceNodeRef, DOC_LIB);
waitForRendition(ADMIN, sourceNodeRef, DOC_LIB); waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true);
clearContent(ADMIN, sourceNodeRef); clearContent(ADMIN, sourceNodeRef);
render(ADMIN, sourceNodeRef, DOC_LIB); render(ADMIN, sourceNodeRef, DOC_LIB);
ChildAssociationRef assoc = AuthenticationUtil.runAs(() -> ChildAssociationRef assoc = AuthenticationUtil.runAs(() ->
renditionService2.getRenditionByName(sourceNodeRef, DOC_LIB), ADMIN); renditionService2.getRenditionByName(sourceNodeRef, DOC_LIB), ADMIN);
waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, false);
assertNull("There should be no rendition as there was no content", assoc); assertNull("There should be no rendition as there was no content", assoc);
} }
@@ -170,16 +158,35 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
{ {
NodeRef sourceNodeRef = createSource(ADMIN, "quick.jpg"); NodeRef sourceNodeRef = createSource(ADMIN, "quick.jpg");
render(ADMIN, sourceNodeRef, DOC_LIB); render(ADMIN, sourceNodeRef, DOC_LIB);
waitForRendition(ADMIN, sourceNodeRef, DOC_LIB); NodeRef rendition1 = waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true);
ContentData contentData1 = DefaultTypeConverter.INSTANCE.convert(ContentData.class, nodeService.getProperty(rendition1, PROP_CONTENT));
updateContent(ADMIN, sourceNodeRef, "quick.png");
render(ADMIN, sourceNodeRef, DOC_LIB);
NodeRef rendition2 = waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true);
ContentData contentData2 = DefaultTypeConverter.INSTANCE.convert(ContentData.class, nodeService.getProperty(rendition2, PROP_CONTENT));
assertEquals("The rendition node should not change", rendition1, rendition2);
assertNotEquals("The content should have change", contentData1.toString(), contentData2.toString());
}
@Test
public void changedSourceFromNull()
{
NodeRef sourceNodeRef = createSource(ADMIN, "quick.jpg");
render(ADMIN, sourceNodeRef, DOC_LIB);
waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true);
clearContent(ADMIN, sourceNodeRef); clearContent(ADMIN, sourceNodeRef);
render(ADMIN, sourceNodeRef, DOC_LIB); render(ADMIN, sourceNodeRef, DOC_LIB);
ChildAssociationRef assoc = AuthenticationUtil.runAs(() -> ChildAssociationRef assoc = AuthenticationUtil.runAs(() ->
renditionService2.getRenditionByName(sourceNodeRef, DOC_LIB), ADMIN); renditionService2.getRenditionByName(sourceNodeRef, DOC_LIB), ADMIN);
waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, false);
assertNull("There should be no rendition as there was no content", assoc); assertNull("There should be no rendition as there was no content", assoc);
updateContent(ADMIN, sourceNodeRef, "quick.png"); updateContent(ADMIN, sourceNodeRef, "quick.png");
waitForRendition(ADMIN, sourceNodeRef, DOC_LIB); render(ADMIN, sourceNodeRef, DOC_LIB);
waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true);
} }
@Test @Test
@@ -188,7 +195,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
String userName = createRandomUser(); String userName = createRandomUser();
NodeRef sourceNodeRef = createSource(userName, "quick.jpg"); NodeRef sourceNodeRef = createSource(userName, "quick.jpg");
render(userName, sourceNodeRef, DOC_LIB); render(userName, sourceNodeRef, DOC_LIB);
NodeRef renditionNodeRef = waitForRendition(userName, sourceNodeRef, DOC_LIB); NodeRef renditionNodeRef = waitForRendition(userName, sourceNodeRef, DOC_LIB, true);
assertNotNull("The rendition was not generated for non-admin user", renditionNodeRef); assertNotNull("The rendition was not generated for non-admin user", renditionNodeRef);
} }
@@ -204,9 +211,9 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
return null; return null;
}); });
render(ownerUserName, sourceNodeRef, DOC_LIB); render(ownerUserName, sourceNodeRef, DOC_LIB);
NodeRef renditionNodeRef = waitForRendition(ownerUserName, sourceNodeRef, DOC_LIB); NodeRef renditionNodeRef = waitForRendition(ownerUserName, sourceNodeRef, DOC_LIB, true);
assertNotNull("The rendition is not visible for owner of source node", renditionNodeRef); assertNotNull("The rendition is not visible for owner of source node", renditionNodeRef);
renditionNodeRef = waitForRendition(otherUserName, sourceNodeRef, DOC_LIB); renditionNodeRef = waitForRendition(otherUserName, sourceNodeRef, DOC_LIB, true);
assertNotNull("The rendition is not visible for non-owner user with read permissions", renditionNodeRef); assertNotNull("The rendition is not visible for non-owner user with read permissions", renditionNodeRef);
assertEquals("The creator of the rendition is not correct", assertEquals("The creator of the rendition is not correct",
ownerUserName, nodeService.getProperty(sourceNodeRef, ContentModel.PROP_CREATOR)); ownerUserName, nodeService.getProperty(sourceNodeRef, ContentModel.PROP_CREATOR));
@@ -224,9 +231,9 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
return null; return null;
}); });
render(otherUserName, sourceNodeRef, DOC_LIB); render(otherUserName, sourceNodeRef, DOC_LIB);
NodeRef renditionNodeRef = waitForRendition(ownerUserName, sourceNodeRef, DOC_LIB); NodeRef renditionNodeRef = waitForRendition(ownerUserName, sourceNodeRef, DOC_LIB, true);
assertNotNull("The rendition is not visible for owner of source node", renditionNodeRef); assertNotNull("The rendition is not visible for owner of source node", renditionNodeRef);
renditionNodeRef = waitForRendition(otherUserName, sourceNodeRef, DOC_LIB); renditionNodeRef = waitForRendition(otherUserName, sourceNodeRef, DOC_LIB, true);
assertNotNull("The rendition is not visible for owner of rendition node", renditionNodeRef); assertNotNull("The rendition is not visible for owner of rendition node", renditionNodeRef);
assertEquals("The creator of the rendition is not correct", assertEquals("The creator of the rendition is not correct",
ownerUserName, nodeService.getProperty(sourceNodeRef, ContentModel.PROP_CREATOR)); ownerUserName, nodeService.getProperty(sourceNodeRef, ContentModel.PROP_CREATOR));
@@ -246,7 +253,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
}); });
try try
{ {
waitForRendition(noPermissionsUser, sourceNodeRef, DOC_LIB); waitForRendition(noPermissionsUser, sourceNodeRef, DOC_LIB, true);
fail("The rendition should not be visible for user with no permissions"); fail("The rendition should not be visible for user with no permissions");
} }
catch (AccessDeniedException ade) catch (AccessDeniedException ade)
@@ -272,7 +279,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
AuthenticationUtil.runAs(() -> nodeService.hasAspect(oldRendition, RenditionModel.ASPECT_RENDITION2), ownerUserName)); AuthenticationUtil.runAs(() -> nodeService.hasAspect(oldRendition, RenditionModel.ASPECT_RENDITION2), ownerUserName));
updateContent(ownerUserName, sourceNodeRef, "quick.png"); updateContent(ownerUserName, sourceNodeRef, "quick.png");
NodeRef newRendition = waitForRendition(ownerUserName, sourceNodeRef, DOC_LIB); NodeRef newRendition = waitForRendition(ownerUserName, sourceNodeRef, DOC_LIB, true);
assertNotNull("The rendition should be reported via RenditionService2", newRendition); assertNotNull("The rendition should be reported via RenditionService2", newRendition);
Thread.sleep(200); Thread.sleep(200);
boolean hasRenditionedAspect = false; boolean hasRenditionedAspect = false;
@@ -307,7 +314,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
.doInTransaction(() -> .doInTransaction(() ->
AuthenticationUtil.runAs(() -> AuthenticationUtil.runAs(() ->
renditionService.render(sourceNodeRef, doclibRendDefQName), ADMIN)); renditionService.render(sourceNodeRef, doclibRendDefQName), ADMIN));
assertNotNull("The old renditions service did not render", waitForRendition(ADMIN, sourceNodeRef, DOC_LIB)); assertNotNull("The old renditions service did not render", waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true));
List<String> lastThumbnailModification = transactionService.getRetryingTransactionHelper() List<String> lastThumbnailModification = transactionService.getRetryingTransactionHelper()
.doInTransaction(() -> .doInTransaction(() ->
AuthenticationUtil.runAs(() -> AuthenticationUtil.runAs(() ->
@@ -349,14 +356,14 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
{ {
NodeRef sourceNodeRef = createSource(ADMIN, "quick.jpg"); NodeRef sourceNodeRef = createSource(ADMIN, "quick.jpg");
render(ADMIN, sourceNodeRef, DOC_LIB); render(ADMIN, sourceNodeRef, DOC_LIB);
waitForRendition(ADMIN, sourceNodeRef, DOC_LIB); waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true);
renditionService2.setEnabled(false); renditionService2.setEnabled(false);
try try
{ {
updateContent(ADMIN, sourceNodeRef, "quick.png"); updateContent(ADMIN, sourceNodeRef, "quick.png");
Thread.sleep(200); Thread.sleep(200);
NodeRef renditionNodeRef = waitForRendition(ADMIN, sourceNodeRef, DOC_LIB); NodeRef renditionNodeRef = waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true);
boolean hasRendition2Aspect = true; boolean hasRendition2Aspect = true;
for (int i = 0; i < 5; i++) for (int i = 0; i < 5; i++)
{ {
@@ -394,12 +401,12 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
.doInTransaction(() -> .doInTransaction(() ->
AuthenticationUtil.runAs(() -> AuthenticationUtil.runAs(() ->
renditionService.render(sourceNodeRef, doclibRendDefQName), ADMIN)); renditionService.render(sourceNodeRef, doclibRendDefQName), ADMIN));
waitForRendition(ADMIN, sourceNodeRef, DOC_LIB); waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true);
renditionService2.setEnabled(true); renditionService2.setEnabled(true);
updateContent(ADMIN, sourceNodeRef, "quick.png"); updateContent(ADMIN, sourceNodeRef, "quick.png");
NodeRef renditionNodeRef = waitForRendition(ADMIN, sourceNodeRef, DOC_LIB); NodeRef renditionNodeRef = waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true);
boolean hasAspect = nodeService.hasAspect(renditionNodeRef, RenditionModel.ASPECT_RENDITION2); boolean hasAspect = nodeService.hasAspect(renditionNodeRef, RenditionModel.ASPECT_RENDITION2);
assertFalse("Should have switched to the old rendition service", hasAspect); assertFalse("Should have switched to the old rendition service", hasAspect);
} }
@@ -419,7 +426,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
String ownerUserName = createRandomUser(); String ownerUserName = createRandomUser();
NodeRef sourceNodeRef = createSource(ownerUserName, "quick.jpg"); NodeRef sourceNodeRef = createSource(ownerUserName, "quick.jpg");
render(ownerUserName, sourceNodeRef, DOC_LIB); render(ownerUserName, sourceNodeRef, DOC_LIB);
NodeRef newRendition = waitForRendition(ownerUserName, sourceNodeRef, DOC_LIB); NodeRef newRendition = waitForRendition(ownerUserName, sourceNodeRef, DOC_LIB, true);
boolean hasRendition2Aspect = AuthenticationUtil.runAs(() -> nodeService.hasAspect(newRendition, RenditionModel.ASPECT_RENDITION2), ownerUserName); boolean hasRendition2Aspect = AuthenticationUtil.runAs(() -> nodeService.hasAspect(newRendition, RenditionModel.ASPECT_RENDITION2), ownerUserName);
assertTrue("The source should have the old renditioned aspect", assertTrue("The source should have the old renditioned aspect",
AuthenticationUtil.runAs(() -> nodeService.hasAspect(sourceNodeRef, RenditionModel.ASPECT_RENDITIONED), ownerUserName)); AuthenticationUtil.runAs(() -> nodeService.hasAspect(sourceNodeRef, RenditionModel.ASPECT_RENDITIONED), ownerUserName));
@@ -428,7 +435,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
{ {
renditionService2.setEnabled(false); renditionService2.setEnabled(false);
updateContent(ownerUserName, sourceNodeRef, "quick.png"); updateContent(ownerUserName, sourceNodeRef, "quick.png");
NodeRef oldRendition = waitForRendition(ownerUserName, sourceNodeRef, DOC_LIB); NodeRef oldRendition = waitForRendition(ownerUserName, sourceNodeRef, DOC_LIB, true);
Thread.sleep(200); Thread.sleep(200);
hasRendition2Aspect = false; hasRendition2Aspect = false;
for (int i = 0; i < 5; i++) for (int i = 0; i < 5; i++)
@@ -467,12 +474,12 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
.doInTransaction(() -> .doInTransaction(() ->
AuthenticationUtil.runAs(() -> AuthenticationUtil.runAs(() ->
renditionService.render(sourceNodeRef, doclibRendDefQName), ADMIN)); renditionService.render(sourceNodeRef, doclibRendDefQName), ADMIN));
waitForRendition(ADMIN, sourceNodeRef, DOC_LIB); waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true);
renditionService2.setEnabled(true); renditionService2.setEnabled(true);
render(ADMIN, sourceNodeRef, DOC_LIB); render(ADMIN, sourceNodeRef, DOC_LIB);
Thread.sleep(200); Thread.sleep(200);
NodeRef renditionNodeRef = waitForRendition(ADMIN, sourceNodeRef, DOC_LIB); NodeRef renditionNodeRef = waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true);
boolean hasRendition2Aspect = false; boolean hasRendition2Aspect = false;
for (int i = 0; i < 5; i++) for (int i = 0; i < 5; i++)
{ {

View File

@@ -26,17 +26,6 @@
package org.alfresco.repo.thumbnail; package org.alfresco.repo.thumbnail;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.content.transform.magick.ImageResizeOptions; import org.alfresco.repo.content.transform.magick.ImageResizeOptions;
@@ -45,9 +34,9 @@ import org.alfresco.repo.rendition.MockedTestServiceRegistry;
import org.alfresco.repo.rendition.RenditionServiceImpl; import org.alfresco.repo.rendition.RenditionServiceImpl;
import org.alfresco.repo.rendition.executer.AbstractRenderingEngine; import org.alfresco.repo.rendition.executer.AbstractRenderingEngine;
import org.alfresco.repo.rendition.executer.ImageRenderingEngine; import org.alfresco.repo.rendition.executer.ImageRenderingEngine;
import org.alfresco.repo.rendition2.RenditionService2Impl;
import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.TransactionServiceImpl; import org.alfresco.repo.transaction.TransactionServiceImpl;
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.action.ActionService; import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.rendition.RenditionDefinition; import org.alfresco.service.cmr.rendition.RenditionDefinition;
@@ -67,6 +56,18 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/** /**
* Thumbnail service implementation unit test * Thumbnail service implementation unit test
* *
@@ -79,6 +80,7 @@ public class ThumbnailServiceImplParameterTest
{ {
// Mocked services. // Mocked services.
private ActionService mockActionService = mock(ActionService.class); private ActionService mockActionService = mock(ActionService.class);
private RenditionService2Impl mockRenditionService2 = mock(RenditionService2Impl.class);
// Real services - backed by mocked services. // Real services - backed by mocked services.
private RenditionServiceImpl renditionService; private RenditionServiceImpl renditionService;
@@ -103,6 +105,8 @@ public class ThumbnailServiceImplParameterTest
renditionService.setActionService(mockActionService); renditionService.setActionService(mockActionService);
renditionService.setServiceRegistry(new MockedTestServiceRegistry()); renditionService.setServiceRegistry(new MockedTestServiceRegistry());
renditionService.setRenditionService2(mockRenditionService2);
when(mockRenditionService2.isCreatedByRenditionService2(any(), any())).thenReturn(false);
ThumbnailServiceImpl thumbs = new ThumbnailServiceImpl() ThumbnailServiceImpl thumbs = new ThumbnailServiceImpl()
{ {