REPO-4334 Base changes for metadata extraction to T-Engines (#114)

Addition of support of async metadata extraction via T-Engines.
Still needs support for RM to control what is extracted in emails.
Still includes OOTB metadata extractors. To be removed.
Still needs removal of legacy transformers and 3rd party libraries they use.
This commit is contained in:
Alan Davis
2020-10-31 08:54:07 +00:00
committed by GitHub
parent cbe6a5f2cc
commit b894bd95d2
51 changed files with 2372 additions and 575 deletions

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2017 Alfresco Software Limited
* Copyright (C) 2005 - 2020 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -45,9 +45,12 @@ import org.junit.runners.Suite;
org.alfresco.repo.action.evaluator.HasAspectEvaluatorTest.class,
org.alfresco.repo.action.executer.SetPropertyValueActionExecuterTest.class,
org.alfresco.repo.action.executer.AddFeaturesActionExecuterTest.class,
org.alfresco.repo.action.executer.ContentMetadataExtracterTest.class,
org.alfresco.repo.action.executer.ContentMetadataExtracterTagMappingTest.class,
org.alfresco.repo.action.executer.ContentMetadataEmbedderTest.class,
org.alfresco.repo.action.executer.AsynchronousExtractorTest.class,
org.alfresco.repo.rule.RuleLinkTest.class,
org.alfresco.repo.rule.RuleServiceCoverageTest.class,
org.alfresco.repo.rule.RuleServiceImplTest.class,

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* Copyright (C) 2005 - 2020 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -25,22 +25,11 @@
*/
package org.alfresco.repo.action.executer;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.ActionImpl;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.content.metadata.AbstractMappingMetadataExtracter;
import org.alfresco.repo.content.metadata.MetadataExtracterRegistry;
import org.alfresco.repo.content.metadata.TikaPoweredMetadataExtracter;
import org.alfresco.repo.content.transform.AbstractContentTransformerTest;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.service.cmr.dictionary.DictionaryService;
@@ -55,15 +44,25 @@ import org.alfresco.service.namespace.QName;
import org.alfresco.util.BaseSpringTest;
import org.alfresco.util.GUID;
import org.apache.tika.embedder.Embedder;
import org.apache.tika.exception.TikaException;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.mime.MediaType;
import org.apache.tika.parser.ParseContext;
import org.apache.tika.parser.Parser;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.transaction.annotation.Transactional;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Test of the ActionExecuter for embedding metadata
*
@@ -94,7 +93,9 @@ public class ContentMetadataEmbedderTest extends BaseSpringTest
this.dictionaryService = (DictionaryService) this.applicationContext.getBean("dictionaryService");
this.mimetypeService = (MimetypeService) this.applicationContext.getBean("mimetypeService");
this.metadataExtracterRegistry = (MetadataExtracterRegistry) this.applicationContext.getBean("metadataExtracterRegistry");
metadataExtracterRegistry.setAsyncExtractEnabled(false);
metadataExtracterRegistry.setAsyncEmbedEnabled(false);
AuthenticationComponent authenticationComponent = (AuthenticationComponent)applicationContext.getBean("authenticationComponent");
authenticationComponent.setSystemUserAsCurrentUser();
@@ -123,15 +124,21 @@ public class ContentMetadataEmbedderTest extends BaseSpringTest
this.executer.setApplicableTypes(new String[] { ContentModel.TYPE_CONTENT.toString() });
}
@After
public void after()
{
metadataExtracterRegistry.setAsyncExtractEnabled(true);
metadataExtracterRegistry.setAsyncEmbedEnabled(true);
}
/**
* Test that a failing embedder does not destroy the original content
*/
@Test
public void testFailingEmbedder()
{
MetadataExtracterRegistry registry = (MetadataExtracterRegistry) applicationContext.getBean("metadataExtracterRegistry");
FailingEmbedder embedder = new FailingEmbedder(Arrays.asList(MimetypeMap.MIMETYPE_PDF));
embedder.setRegistry(registry);
AbstractMappingMetadataExtracter embedder = new FailingMappingMetadataEmbedder(Arrays.asList(MimetypeMap.MIMETYPE_PDF));
embedder.setRegistry(metadataExtracterRegistry);
embedder.setDictionaryService(this.dictionaryService);
embedder.setMimetypeService(this.mimetypeService);
embedder.register();
@@ -158,17 +165,16 @@ public class ContentMetadataEmbedderTest extends BaseSpringTest
}
/**
* Tika-powered embedder which fails upon calling embed on its {@link FailingTikaEmbedder}
* Embedder which fails upon calling embed on its {@link FailingEmbedder}
*/
private class FailingEmbedder extends TikaPoweredMetadataExtracter
private class FailingMappingMetadataEmbedder extends AbstractMappingMetadataExtracter
{
/**
* Constructor for setting supported extract and embed mimetypes
*
* @param mimetypes the supported extract and embed mimetypes
*/
public FailingEmbedder(Collection<String> mimetypes)
public FailingMappingMetadataEmbedder(Collection<String> mimetypes)
{
super(
new HashSet<String>(mimetypes),
@@ -176,15 +182,26 @@ public class ContentMetadataEmbedderTest extends BaseSpringTest
}
@Override
protected Parser getParser()
protected void embedInternal(Map<String, Serializable> metadata, ContentReader reader, ContentWriter writer) throws Throwable
{
return null;
Embedder embedder = getEmbedder();
if (embedder == null)
{
return;
}
Map<String, String> metadataAsStrings = convertMetadataToStrings(metadata);
Metadata metadataToEmbed = new Metadata();
metadataAsStrings.forEach((k,v)->metadataToEmbed.add(k, v));
InputStream inputStream = reader.getContentInputStream();
OutputStream outputStream = writer.getContentOutputStream();
embedder.embed(metadataToEmbed, null, outputStream, null);
}
@Override
protected Embedder getEmbedder()
{
return new FailingTikaEmbedder();
return new FailingEmbedder();
}
@Override
@@ -202,12 +219,18 @@ public class ContentMetadataEmbedderTest extends BaseSpringTest
mapping.put("author", qnames);
return mapping;
}
@Override
protected Map<String, Serializable> extractRaw(ContentReader reader) throws Throwable
{
return null;
}
}
/**
* Tika metadata embedder which fails on a call to embed.
* Metadata embedder which fails on a call to embed.
*/
private class FailingTikaEmbedder implements Embedder
private class FailingEmbedder implements Embedder
{
private static final long serialVersionUID = -4954679684941467571L;
@@ -219,7 +242,7 @@ public class ContentMetadataEmbedderTest extends BaseSpringTest
@Override
public void embed(Metadata metadata, InputStream originalStream, OutputStream outputStream, ParseContext context)
throws IOException, TikaException
throws IOException
{
throw new IOException("Forced failure");
}

View File

@@ -45,7 +45,6 @@ import org.alfresco.repo.action.AsynchronousActionExecutionQueuePolicies;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.content.metadata.AbstractMappingMetadataExtracter;
import org.alfresco.repo.content.metadata.MetadataExtracterRegistry;
import org.alfresco.repo.content.metadata.TikaPoweredMetadataExtracter;
import org.alfresco.repo.content.transform.AbstractContentTransformerTest;
import org.alfresco.repo.policy.Behaviour.NotificationFrequency;
import org.alfresco.repo.policy.JavaBehaviour;
@@ -74,8 +73,6 @@ import org.alfresco.util.GUID;
import org.alfresco.util.testing.category.LuceneTests;
import org.alfresco.util.testing.category.RedundantTests;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.parser.Parser;
import org.apache.tika.parser.jpeg.JpegParser;
import org.junit.experimental.categories.Category;
import org.springframework.context.ConfigurableApplicationContext;
@@ -112,6 +109,7 @@ public class ContentMetadataExtracterTagMappingTest extends TestCase
private TaggingService taggingService;
private NodeService nodeService;
private ContentService contentService;
private MetadataExtracterRegistry metadataExtracterRegistry;
private AuditService auditService;
private TransactionService transactionService;
private AuthenticationComponent authenticationComponent;
@@ -144,7 +142,10 @@ public class ContentMetadataExtracterTagMappingTest extends TestCase
this.taggingService = (TaggingService)ctx.getBean("TaggingService");
this.nodeService = (NodeService) ctx.getBean("NodeService");
this.contentService = (ContentService) ctx.getBean("ContentService");
this.metadataExtracterRegistry = (MetadataExtracterRegistry) ctx.getBean("metadataExtracterRegistry");
metadataExtracterRegistry.setAsyncExtractEnabled(false);
metadataExtracterRegistry.setAsyncEmbedEnabled(false);
this.transactionService = (TransactionService)ctx.getBean("transactionComponent");
this.auditService = (AuditService)ctx.getBean("auditService");
this.authenticationComponent = (AuthenticationComponent)ctx.getBean("authenticationComponent");
@@ -208,6 +209,9 @@ public class ContentMetadataExtracterTagMappingTest extends TestCase
@Override
protected void tearDown() throws Exception
{
metadataExtracterRegistry.setAsyncExtractEnabled(true);
metadataExtracterRegistry.setAsyncEmbedEnabled(true);
if (AlfrescoTransactionSupport.getTransactionReadState() != TxnReadState.TXN_NONE)
{
fail("Test is not transaction-safe. Fix up transaction handling and re-test.");
@@ -297,7 +301,7 @@ public class ContentMetadataExtracterTagMappingTest extends TestCase
});
}
private static class TagMappingMetadataExtracter extends TikaPoweredMetadataExtracter
private static class TagMappingMetadataExtracter extends AbstractMappingMetadataExtracter
{
private String existingTagNodeRef;
@@ -329,16 +333,10 @@ public class ContentMetadataExtracterTagMappingTest extends TestCase
return sourceMimetype.equals(MimetypeMap.MIMETYPE_IMAGE_JPEG);
}
@Override
protected Parser getParser()
{
return new JpegParser();
}
@SuppressWarnings("unchecked")
public Map<String, Serializable> extractRaw(ContentReader reader) throws Throwable
{
Map<String, Serializable> rawMap = super.extractRaw(reader);
Map<String, Serializable> rawMap = newRawMap();
// Add some test keywords to those actually extracted from the file including a nodeRef
List<String> keywords = new ArrayList<String>(Arrays.asList(

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* Copyright (C) 2005 - 2020 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -43,16 +43,11 @@
*/
package org.alfresco.repo.action.executer;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.ActionImpl;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.content.metadata.AbstractMappingMetadataExtracter;
import org.alfresco.repo.content.metadata.AsynchronousExtractor;
import org.alfresco.repo.content.metadata.MetadataExtracterRegistry;
import org.alfresco.repo.content.transform.AbstractContentTransformerTest;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
@@ -67,11 +62,18 @@ import org.alfresco.service.namespace.QName;
import org.alfresco.test_category.BaseSpringTestsCategory;
import org.alfresco.util.BaseSpringTest;
import org.alfresco.util.GUID;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.springframework.transaction.annotation.Transactional;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* Test of the ActionExecuter for extracting metadata. Note: This test makes
* assumptions about the PDF test data for PdfBoxExtracter.
@@ -88,6 +90,7 @@ public class ContentMetadataExtracterTest extends BaseSpringTest
private NodeService nodeService;
private ContentService contentService;
private MetadataExtracterRegistry registry;
private StoreRef testStoreRef;
private NodeRef rootNodeRef;
private NodeRef nodeRef;
@@ -101,7 +104,10 @@ public class ContentMetadataExtracterTest extends BaseSpringTest
{
this.nodeService = (NodeService) this.applicationContext.getBean("nodeService");
this.contentService = (ContentService) this.applicationContext.getBean("contentService");
registry = (MetadataExtracterRegistry) applicationContext.getBean("metadataExtracterRegistry");
registry.setAsyncExtractEnabled(false);
registry.setAsyncEmbedEnabled(false);
AuthenticationComponent authenticationComponent = (AuthenticationComponent)applicationContext.getBean("authenticationComponent");
authenticationComponent.setSystemUserAsCurrentUser();
@@ -126,6 +132,13 @@ public class ContentMetadataExtracterTest extends BaseSpringTest
this.executer = (ContentMetadataExtracter) this.applicationContext.getBean("extract-metadata");
}
@After
public void after()
{
registry.setAsyncExtractEnabled(true);
registry.setAsyncEmbedEnabled(true);
}
/**
* Test execution of the extraction itself
*/
@@ -189,7 +202,6 @@ public class ContentMetadataExtracterTest extends BaseSpringTest
@Test
public void testUnknownProperties()
{
MetadataExtracterRegistry registry = (MetadataExtracterRegistry) applicationContext.getBean("metadataExtracterRegistry");
TestUnknownMetadataExtracter extracterUnknown = new TestUnknownMetadataExtracter();
extracterUnknown.setRegistry(registry);
extracterUnknown.register();
@@ -247,7 +259,6 @@ public class ContentMetadataExtracterTest extends BaseSpringTest
@Test
public void testNullExtractedValues_ALF1823()
{
MetadataExtracterRegistry registry = (MetadataExtracterRegistry) applicationContext.getBean("metadataExtracterRegistry");
TestNullPropMetadataExtracter extractor = new TestNullPropMetadataExtracter();
extractor.setRegistry(registry);
extractor.register();

View File

@@ -63,11 +63,13 @@ import org.junit.Ignore;
import org.springframework.context.ApplicationContext;
/**
*
* @deprecated The JodConverterMetadataExtracter has not been in use since 6.0.1 and the transformer is also deprecated.
*
* @author Neil McErlean
* @since 3.3
*/
@Ignore("This is an abstract class so don't instaniate it or run it in Junit")
@Deprecated
public abstract class AbstractJodConverterBasedTest
{
private static Log log = LogFactory.getLog(AbstractJodConverterBasedTest.class);

View File

@@ -0,0 +1,559 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2020 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.action.executer;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.ActionImpl;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.content.metadata.AbstractMappingMetadataExtracter;
import org.alfresco.repo.content.metadata.AsynchronousExtractor;
import org.alfresco.repo.content.metadata.MetadataExtracterRegistry;
import org.alfresco.repo.content.transform.AbstractContentTransformerTest;
import org.alfresco.repo.content.transform.TransformerDebug;
import org.alfresco.repo.content.transform.UnsupportedTransformationException;
import org.alfresco.repo.rendition2.RenditionDefinition2;
import org.alfresco.repo.rendition2.RenditionService2Impl;
import org.alfresco.repo.rendition2.TransformClient;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.ContentIOException;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.tagging.TaggingService;
import org.alfresco.service.namespace.NamespacePrefixResolver;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.test_category.BaseSpringTestsCategory;
import org.alfresco.transform.client.registry.TransformServiceRegistry;
import org.alfresco.util.BaseSpringTest;
import org.alfresco.util.GUID;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.StringJoiner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static java.util.Arrays.asList;
import static org.alfresco.model.ContentModel.PROP_CONTENT;
import static org.alfresco.model.ContentModel.PROP_CREATED;
import static org.alfresco.model.ContentModel.PROP_CREATOR;
import static org.alfresco.model.ContentModel.PROP_MODIFIED;
import static org.alfresco.model.ContentModel.PROP_MODIFIER;
import static org.alfresco.repo.rendition2.RenditionService2Impl.SOURCE_HAS_NO_CONTENT;
/**
* Tests the asynchronous extract and embed of metadata. This is normally performed in a T-Engine, but in this test
* class is mocked using a separate Thread that returns well known values. What makes the AsynchronousExtractor
* different from other {@link AbstractMappingMetadataExtracter} sub classes is that the calling Thread does not
* do the work of updating properties or the content, as the T-Engine will reply at some later point.
*
* @author adavis
*/
@Category(BaseSpringTestsCategory.class)
public class AsynchronousExtractorTest extends BaseSpringTest
{
private final static String ID = GUID.generate();
private static final String AFTER_CALLING_EXECUTE = "after calling execute";
private static final String AFTER_THE_TRANSFORM = "after the transform";
private static final Integer UNCHANGED_HASHCODE = null;
private static final Integer CHANGED_HASHCODE = 1234;
private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy");
private static final ExecutorService executorService = Executors.newCachedThreadPool();
private NodeService nodeService;
private ContentService contentService;
private DictionaryService dictionaryService;
private MimetypeService mimetypeService;
private MetadataExtracterRegistry metadataExtracterRegistry;
private StoreRef testStoreRef;
private NodeRef rootNodeRef;
private NodeRef nodeRef;
private AsynchronousExtractor asynchronousExtractor;
private NamespacePrefixResolver namespacePrefixResolver;
private TransformerDebug transformerDebug;
private TransactionService transactionService;
private TransformServiceRegistry transformServiceRegistry;
private TaggingService taggingService;
private ContentMetadataExtracter contentMetadataExtracter;
private ContentMetadataEmbedder contentMetadataEmbedder;
private RenditionService2Impl renditionService2;
private TransformClient transformClient;
private long origSize;
private Map<QName, Serializable> origProperties;
private Map<QName, Serializable> expectedProperties;
private Map<QName, Serializable> properties;
private class TestAsynchronousExtractor extends AsynchronousExtractor
{
private final String mockResult;
private final Integer changedHashcode;
private final Random random = new Random();
private boolean finished;
TransformClient mockTransformClient = new TransformClient()
{
@Override
public void checkSupported(NodeRef sourceNodeRef, RenditionDefinition2 renditionDefinition, String sourceMimetype, long sourceSizeInBytes, String contentUrl)
{
}
@Override
public void transform(NodeRef sourceNodeRef, RenditionDefinition2 renditionDefinition, String user, int sourceContentHashCode)
throws UnsupportedTransformationException, ContentIOException
{
mockTransform(sourceNodeRef, renditionDefinition, sourceContentHashCode);
}
};
/**
* Creates an AsynchronousExtractor that simulates a extract or embed.
*
* @param mockResult if specified indicates a value was returned. The result is read as a resource from
* the classpath.
* @param changedHashcode if specified indicates that the source node content changed or was deleted between
* the request to extract or embed and the response.
*/
TestAsynchronousExtractor(String mockResult, Integer changedHashcode)
{
this.mockResult = mockResult;
this.changedHashcode = changedHashcode;
setNodeService(nodeService);
setNamespacePrefixResolver(namespacePrefixResolver);
setTransformerDebug(transformerDebug);
setRenditionService2(renditionService2);
setContentService(contentService);
setTransactionService(transactionService);
setTransformServiceRegistry(transformServiceRegistry);
setTaggingService(taggingService);
setRegistry(metadataExtracterRegistry);
setMimetypeService(mimetypeService);
setDictionaryService(dictionaryService);
setExecutorService(executorService);
register();
renditionService2.setTransformClient(mockTransformClient);
}
@Override
public boolean isSupported(String sourceMimetype, long sourceSizeInBytes)
{
return true;
}
@Override
public boolean isEmbedderSupported(String sourceMimetype, long sourceSizeInBytes)
{
return true;
}
private void mockTransform(NodeRef sourceNodeRef, RenditionDefinition2 renditionDefinition, int sourceContentHashCode)
{
try
{
transformerDebug.pushMisc();
wait(50, 700);
}
finally
{
transformerDebug.popMisc();
}
int transformContentHashCode = changedHashcode == null ? sourceContentHashCode : changedHashcode;
if (mockResult != null)
{
try (InputStream transformInputStream = getClass().getClassLoader().getResourceAsStream(mockResult))
{
renditionService2.consume(sourceNodeRef, transformInputStream, renditionDefinition, transformContentHashCode);
}
catch (IOException e)
{
throw new RuntimeException("Could not read '" + mockResult + "' from the classpath.", e);
}
}
else
{
renditionService2.failure(sourceNodeRef, renditionDefinition, transformContentHashCode);
}
synchronized (this)
{
finished = true;
notifyAll();
}
}
/**
* Wait for a few milliseconds or until the finished flag is set.
*
* @param from inclusive lower bound. If negative, there is only an upper bound.
* @param to exclusive upper bound.
* @return the wait.
*/
public synchronized void wait(int from, int to)
{
long start = System.currentTimeMillis();
long end = start + (from < 0 ? to : from + random.nextInt(to - from));
while (!finished && System.currentTimeMillis() < end)
{
try
{
long ms = end - System.currentTimeMillis();
if (ms > 0)
{
wait(ms);
}
}
catch (InterruptedException ignore)
{
}
}
}
}
@Before
public void before() throws Exception
{
nodeService = (NodeService) applicationContext.getBean("nodeService");
contentService = (ContentService) applicationContext.getBean("contentService");
dictionaryService = (DictionaryService) applicationContext.getBean("dictionaryService");
mimetypeService = (MimetypeService) applicationContext.getBean("mimetypeService");
namespacePrefixResolver = (NamespacePrefixResolver) applicationContext.getBean("namespaceService");
transformerDebug = (TransformerDebug) applicationContext.getBean("transformerDebug");
renditionService2 = (RenditionService2Impl) applicationContext.getBean("renditionService2");
transactionService = (TransactionService) applicationContext.getBean("transactionService");
transformServiceRegistry = (TransformServiceRegistry) applicationContext.getBean("transformServiceRegistry");
taggingService = (TaggingService) applicationContext.getBean("taggingService");
transformClient = (TransformClient) applicationContext.getBean("transformClient");
// Create an empty metadata extractor registry, so that if we add one it will be used
metadataExtracterRegistry = new MetadataExtracterRegistry();
contentMetadataExtracter = new ContentMetadataExtracter();
contentMetadataExtracter.setNodeService(nodeService);
contentMetadataExtracter.setContentService(contentService);
contentMetadataExtracter.setDictionaryService(dictionaryService);
contentMetadataExtracter.setMetadataExtracterRegistry(metadataExtracterRegistry);
contentMetadataExtracter.setApplicableTypes(new String[]{ContentModel.TYPE_CONTENT.toString()});
contentMetadataExtracter.setCarryAspectProperties(true);
contentMetadataEmbedder = new ContentMetadataEmbedder();
contentMetadataEmbedder.setNodeService(nodeService);
contentMetadataEmbedder.setContentService(contentService);
contentMetadataEmbedder.setMetadataExtracterRegistry(metadataExtracterRegistry);
contentMetadataEmbedder.setApplicableTypes(new String[]{ContentModel.TYPE_CONTENT.toString()});
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>()
{
@Override
public Void execute() throws Throwable
{
AuthenticationComponent authenticationComponent = (AuthenticationComponent) applicationContext.getBean("authenticationComponent");
authenticationComponent.setSystemUserAsCurrentUser();
// Create the store and get the root node
testStoreRef = nodeService.createStore(
StoreRef.PROTOCOL_WORKSPACE,
"Test_" + System.currentTimeMillis());
rootNodeRef = nodeService.getRootNode(testStoreRef);
// Create the node used for tests
nodeRef = nodeService.createNode(
rootNodeRef, ContentModel.ASSOC_CHILDREN,
QName.createQName("{test}testnode"),
ContentModel.TYPE_CONTENT).getChildRef();
// Authenticate as the system user
authenticationComponent.setSystemUserAsCurrentUser();
ContentWriter cw = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true);
cw.setMimetype(MimetypeMap.MIMETYPE_PDF);
cw.putContent(AbstractContentTransformerTest.loadQuickTestFile("pdf"));
origProperties = nodeService.getProperties(nodeRef);
nodeService.setProperties(nodeRef, origProperties);
origProperties = new HashMap<>(origProperties); // just in case the contents changed.
expectedProperties = new HashMap<>(origProperties); // ready to be modified.
origSize = getSize(nodeRef);
return null;
}
});
}
@After
public void after() throws Exception
{
renditionService2.setTransformClient(transformClient);
}
private void assertAsyncMetadataExecute(ActionExecuterAbstractBase executor, String mockResult,
Integer changedHashcode, long expectedSize,
Map<QName, Serializable> expectedProperties,
QName... ignoreProperties) throws Exception
{
TestAsynchronousExtractor extractor = new TestAsynchronousExtractor(mockResult, changedHashcode);
executeAction(executor, extractor);
assertContentSize(nodeRef, origSize, AFTER_CALLING_EXECUTE);
assertProperties(nodeRef, origProperties, AFTER_CALLING_EXECUTE, ignoreProperties);
extractor.wait(-1, 10000);
assertContentSize(nodeRef, expectedSize, AFTER_THE_TRANSFORM);
assertProperties(nodeRef, expectedProperties, AFTER_THE_TRANSFORM, ignoreProperties);
}
private void executeAction(ActionExecuterAbstractBase extractor, TestAsynchronousExtractor asynchronousExtractor)
throws SystemException, NotSupportedException, HeuristicRollbackException, HeuristicMixedException, RollbackException
{
UserTransaction txn = transactionService.getUserTransaction();
txn.begin();
ActionImpl action = new ActionImpl(null, ID, SetPropertyValueActionExecuter.NAME, null);
extractor.execute(action, nodeRef);
txn.commit();
}
void assertContentSize(NodeRef nodeRef, long expectSize, String state)
{
long size = getSize(nodeRef);
if (expectSize == origSize)
{
assertEquals("The content should remain unchanged " + state, origSize, size);
}
else
{
assertEquals("The content should have changed " + state, expectSize, size);
}
}
private long getSize(NodeRef nodeRef)
{
ContentReader reader = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT);
return reader.getSize();
}
private void assertProperties(NodeRef nodeRef, Map<QName, Serializable> expectProperties, String state,
QName[] ignoreProperties)
{
properties = nodeService.getProperties(nodeRef);
// Work out the difference in a human readable form and ignore the 5 system set properties (as they always
// change) plus any the caller has requested.
StringJoiner sj = new StringJoiner("\n");
List<QName> ignoreKeys = new ArrayList<>(asList(PROP_MODIFIED, PROP_MODIFIER, PROP_CONTENT, PROP_CREATED, PROP_CREATOR));
ignoreKeys.addAll(asList(ignoreProperties));
for (Map.Entry<QName, Serializable> entry : expectProperties.entrySet())
{
QName k = entry.getKey();
Serializable v = entry.getValue();
Serializable actual = properties.get(k);
if (!ignoreKeys.contains(k) && !v.equals(actual))
{
sj.add(k + "\n Expected: " + v + "\n Was: " + actual);
}
}
for (QName k : properties.keySet())
{
Serializable actual = properties.get(k);
if (!ignoreKeys.contains(k) && !expectProperties.containsKey(k))
{
sj.add(k + "\n Expected: null\n Was: " + actual);
}
}
if (sj.length() != 0)
{
if (expectProperties.equals(origProperties))
{
fail("The properties should remain unchanged " + state + "\n" + sj);
}
else
{
fail("The properties should have changed " + state + "\n" + sj);
}
}
}
@Test
public void testExtractHtml() throws Exception
{
expectedProperties.put(QName.createQName("cm:author", namespacePrefixResolver), "Nevin Nollop");
expectedProperties.put(QName.createQName("cm:description", namespacePrefixResolver), "Gym class featuring a brown fox and lazy dog");
expectedProperties.put(QName.createQName("cm:title", namespacePrefixResolver), "The quick brown fox jumps over the lazy dog");
assertAsyncMetadataExecute(contentMetadataExtracter, "quick/quick.html_metadata.json",
UNCHANGED_HASHCODE, origSize, expectedProperties);
}
@Test
public void testExtractNodeDeleted() throws Exception
{
assertAsyncMetadataExecute(contentMetadataExtracter, "quick/quick.html_metadata.json",
SOURCE_HAS_NO_CONTENT, origSize, origProperties);
}
@Test
public void testExtractContentChanged() throws Exception
{
assertAsyncMetadataExecute(contentMetadataExtracter, "quick/quick.html_metadata.json",
1234, origSize, origProperties);
}
@Test
public void testExtractTransformFailure() throws Exception
{
assertAsyncMetadataExecute(contentMetadataExtracter, null,
UNCHANGED_HASHCODE, origSize, origProperties);
}
@Test
public void testExtractTransformCorrupt() throws Exception
{
assertAsyncMetadataExecute(contentMetadataExtracter, "quick.html", // not json
UNCHANGED_HASHCODE, origSize, origProperties);
}
@Test
public void testUnknownNamespaceInResponse() throws Exception
{
// "sys:overwritePolicy": "PRAGMATIC" - is used
// "{http://www.unknown}name": "ignored" - is reported in an ERROR log
expectedProperties.put(QName.createQName("cm:author", namespacePrefixResolver), "Used");
assertAsyncMetadataExecute(contentMetadataExtracter, "quick/unknown_namespace_metadata.json",
UNCHANGED_HASHCODE, origSize, expectedProperties);
}
@Test
public void testExtractMsg() throws Exception // has dates as RFC822
{
expectedProperties.put(QName.createQName("cm:addressee", namespacePrefixResolver), "mark.rogers@alfresco.com");
expectedProperties.put(QName.createQName("cm:description", namespacePrefixResolver), "This is a quick test");
expectedProperties.put(QName.createQName("cm:addressees", namespacePrefixResolver),
new ArrayList<>(asList("mark.rogers@alfresco.com", "speedy@quick.com", "mrquick@nowhere.com")));
expectedProperties.put(QName.createQName("cm:sentdate", namespacePrefixResolver), SIMPLE_DATE_FORMAT.parse("Fri Jan 18 13:44:20 GMT 2013")); // 2013-01-18T13:44:20Z
expectedProperties.put(QName.createQName("cm:subjectline", namespacePrefixResolver), "This is a quick test");
expectedProperties.put(QName.createQName("cm:author", namespacePrefixResolver), "Mark Rogers");
expectedProperties.put(QName.createQName("cm:originator", namespacePrefixResolver), "Mark Rogers");
assertAsyncMetadataExecute(contentMetadataExtracter, "quick/quick.msg_metadata.json",
UNCHANGED_HASHCODE, origSize, expectedProperties);
Serializable sentDate = properties.get(QName.createQName("cm:sentdate", namespacePrefixResolver));
}
@Test
public void testExtractEml() throws Exception // has dates as longs since 1970
{
expectedProperties.put(QName.createQName("cm:addressee", namespacePrefixResolver), "Nevin Nollop <nevin.nollop@gmail.com>");
expectedProperties.put(QName.createQName("cm:description", namespacePrefixResolver), "The quick brown fox jumps over the lazy dog");
expectedProperties.put(QName.createQName("cm:addressees", namespacePrefixResolver),
new ArrayList<>(asList("Nevin Nollop <nevinn@alfresco.com>")));
expectedProperties.put(QName.createQName("imap:dateSent", namespacePrefixResolver), SIMPLE_DATE_FORMAT.parse("Fri Jun 04 13:23:22 BST 2004"));
expectedProperties.put(QName.createQName("imap:messageTo", namespacePrefixResolver), "Nevin Nollop <nevin.nollop@gmail.com>");
expectedProperties.put(QName.createQName("imap:messageId", namespacePrefixResolver), "<20040604122322.GV1905@phoenix.home>");
expectedProperties.put(QName.createQName("cm:title", namespacePrefixResolver), "The quick brown fox jumps over the lazy dog");
expectedProperties.put(QName.createQName("imap:messageSubject", namespacePrefixResolver), "The quick brown fox jumps over the lazy dog");
expectedProperties.put(QName.createQName("imap:messageCc", namespacePrefixResolver), "Nevin Nollop <nevinn@alfresco.com>");
expectedProperties.put(QName.createQName("cm:sentdate", namespacePrefixResolver), SIMPLE_DATE_FORMAT.parse("Fri Jun 04 13:23:22 BST 2004"));
expectedProperties.put(QName.createQName("cm:subjectline", namespacePrefixResolver), "The quick brown fox jumps over the lazy dog");
expectedProperties.put(QName.createQName("imap:messageFrom", namespacePrefixResolver), "Nevin Nollop <nevin.nollop@alfresco.com>");
expectedProperties.put(QName.createQName("cm:originator", namespacePrefixResolver), "Nevin Nollop <nevin.nollop@alfresco.com>");
// Note: As the metadata is for eml, an aspect gets added resulting in a second extract because of
// ImapContentPolicy.onAddAspect. I cannot see a good way to avoid this.
assertAsyncMetadataExecute(contentMetadataExtracter, "quick/quick.eml_metadata.json",
UNCHANGED_HASHCODE, origSize, expectedProperties,
// cm:author is not in the quick.eml_metadata.json but is being added by the second extract which thinks
// the source mimetype is MimetypeMap.MIMETYPE_PDF, because that is what the before() method sets the
// content to. As a result the PdfBox metadata extractor is called, which extracts cm:author. Given that
// we don't know when this will take place, we simply ignore this property. We could fix this up, but it
// does not add anything to the test.
QName.createQName("cm:author", namespacePrefixResolver));
}
@Test
public void testEmbed() throws Exception
{
assertAsyncMetadataExecute(contentMetadataEmbedder, "quick/quick.html", // just replace the pdf with html!
UNCHANGED_HASHCODE, 428, expectedProperties);
}
@Test
public void testEmbedNodeDeleted() throws Exception
{
assertAsyncMetadataExecute(contentMetadataEmbedder, "quick/quick.html",
SOURCE_HAS_NO_CONTENT, origSize, origProperties);
}
@Test
public void testEmbedContentChanged() throws Exception
{
assertAsyncMetadataExecute(contentMetadataEmbedder, "quick/quick.html",
1234, origSize, origProperties);
}
@Test
public void testEmbedTransformFailure() throws Exception
{
assertAsyncMetadataExecute(contentMetadataEmbedder, null,
UNCHANGED_HASHCODE, origSize, origProperties);
}
// TODO Write tests for: overwritePolicy, enableStringTagging and carryAspectProperties.
// Values are set in AsynchronousExtractor.setMetadata(...) but make use of original code within
// MetadataExtracter and AbstractMappingMetadataExtracter.
// As the tests for exiting extractors are to be removed in ACS 7.0, it is possible that they were being used
// to test these values.
}

View File

@@ -177,5 +177,5 @@ public class DWGMetadataExtracterTest extends AbstractMetadataExtracterTest
assertEquals("Custom DWG property not found", "valueforcustomprop1", properties.get(TIKA_CUSTOM_TEST_PROPERTY));
}
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* Copyright (C) 2005 - 2020 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -102,6 +102,8 @@ import com.sun.mail.imap.protocol.RFC822DATA;
import com.sun.mail.imap.protocol.UID;
import com.sun.mail.util.ASCIIUtility;
import static org.alfresco.model.ContentModel.PROP_MODIFIED;
@Category({OwnJVMTestsCategory.class, LuceneTests.class})
public class ImapMessageTest extends TestCase
{
@@ -523,15 +525,30 @@ public class ImapMessageTest extends TestCase
messageHelper.addCc(address);
// Creating the message node in the repository
UserTransaction txn = transactionService.getUserTransaction();
txn.begin();
String name = AlfrescoImapConst.MESSAGE_PREFIX + GUID.generate();
FileInfo messageFile = fileFolderService.create(testImapFolderNodeRef, name, ContentModel.TYPE_CONTENT);
// Writing a content.
NodeRef nodeRef = messageFile.getNodeRef();
Serializable origModified = getModified(nodeRef);
new IncomingImapMessage(messageFile, serviceRegistry, message);
txn.commit();
// Calls to new IncomingImapMessage(...) only takes place when a new nodeRef is being created.
// No other code will be changing the nodeRef. An ImapModel.ASPECT_IMAP_CONTENT is added, which
// triggers a metadata extract to take place in a post commit method. Previously this would have been a
// synchronous process. This is no longer true as it may now take place in a T-Engine. So, we need to wait
// for the extract to take place. There does not
long end = System.currentTimeMillis()+10000;
while (System.currentTimeMillis() <= end && origModified.equals(getModified(nodeRef)))
{
Thread.currentThread().sleep(1000);
}
// Getting the transformed properties from the repository
// cm:originator, cm:addressee, cm:addressees, imap:messageFrom, imap:messageTo, imap:messageCc
Map<QName, Serializable> properties = nodeService.getProperties(messageFile.getNodeRef());
Map<QName, Serializable> properties = nodeService.getProperties(nodeRef);
String cmOriginator = (String) properties.get(ContentModel.PROP_ORIGINATOR);
String cmAddressee = (String) properties.get(ContentModel.PROP_ADDRESSEE);
@SuppressWarnings("unchecked")
@@ -555,6 +572,12 @@ public class ImapMessageTest extends TestCase
assertEquals(decodedAddress, imapMessageCc);
}
private Serializable getModified(NodeRef nodeRef)
{
Map<QName, Serializable> origProperties = nodeService.getProperties(nodeRef);
return origProperties.get(PROP_MODIFIED);
}
@Category(RedundantTests.class)
public void testEightBitMessage() throws Exception
{

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* Copyright (C) 2005 - 2020 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -26,10 +26,6 @@
package org.alfresco.repo.rendition;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.model.RenditionModel;
import org.alfresco.repo.content.MimetypeMap;
@@ -40,19 +36,18 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.thumbnail.ThumbnailDefinition;
import org.alfresco.repo.thumbnail.ThumbnailHelper;
import org.alfresco.repo.thumbnail.ThumbnailRegistry;
import org.alfresco.repo.thumbnail.ThumbnailServiceImplTest;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.rendition.RenditionDefinition;
import org.alfresco.service.cmr.rendition.RenditionService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentIOException;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.TransformationOptions;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.site.SiteVisibility;
import org.alfresco.service.namespace.NamespaceService;
@@ -76,9 +71,16 @@ import org.junit.experimental.categories.Category;
import org.junit.rules.RuleChain;
import org.springframework.test.context.ContextConfiguration;
import java.io.Serializable;
import java.util.List;
import static org.alfresco.repo.rendition2.TestSynchronousTransformClient.EXPECTED_USER;
import static org.alfresco.repo.rendition2.TestSynchronousTransformClient.TEST_USER_MIME_TYPE;
import static org.junit.Assert.*;
import static org.alfresco.repo.thumbnail.ThumbnailServiceImplTest.TEST_THUMBNAIL;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* @author Neil McErlean
@@ -99,7 +101,7 @@ public class RenditionServicePermissionsTest
public static ApplicationContextInit APP_CONTEXT_INIT =
ApplicationContextInit.createStandardContextWithOverrides("classpath:/test/alfresco/test-renditions-context.xml",
"classpath:org/alfresco/repo/rendition2/test-transform-context.xml");
// JUnit Rules to create test users.
public static AlfrescoPerson TEST_USER1 = new AlfrescoPerson(APP_CONTEXT_INIT, EXPECTED_USER);
@@ -151,6 +153,7 @@ public class RenditionServicePermissionsTest
transactionHelper = (RetryingTransactionHelper) APP_CONTEXT_INIT.getApplicationContext().getBean("retryingTransactionHelper");
services = (ServiceRegistry) APP_CONTEXT_INIT.getApplicationContext().getBean("ServiceRegistry");
thumbnailRegistry = (ThumbnailRegistry) APP_CONTEXT_INIT.getApplicationContext().getBean("thumbnailRegistry");
ThumbnailServiceImplTest.createTestThumbnail(thumbnailRegistry);
}
@Before public void initNonStaticData() throws Exception
@@ -319,7 +322,8 @@ public class RenditionServicePermissionsTest
{
final String siteConsumer = testSiteInfo.siteConsumer;
// Let's trigger the creation of a doclib thumbnail for the broken JPG node.
// Let's trigger the creation of a TEST_THUMBNAIL for the broken JPG node. Previously it was doclib, but the
// newer RenditionService2 knows how to create that and does not do failure recovery needed by this test.
// We know this cannot succeed. We also know the user triggering it does not have write permissions for the node.
AuthenticationUtil.setFullyAuthenticatedUser(siteConsumer);
@@ -328,7 +332,7 @@ public class RenditionServicePermissionsTest
public Void execute() throws Throwable
{
// This is what ScriptNode.createThumbnail does
ThumbnailDefinition details = thumbnailRegistry.getThumbnailDefinition("doclib");
ThumbnailDefinition details = thumbnailRegistry.getThumbnailDefinition(TEST_THUMBNAIL);
Action action = ThumbnailHelper.createCreateThumbnailAction(details, services);
// Queue async creation of thumbnail

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2019 Alfresco Software Limited
* Copyright (C) 2005 - 2020 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -27,6 +27,7 @@ package org.alfresco.repo.rendition2;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.metadata.AsynchronousExtractor;
import org.alfresco.repo.policy.BehaviourFilter;
import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.repo.rendition.RenditionPreventionRegistry;
@@ -46,11 +47,9 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.quartz.CronExpression;
import java.io.IOException;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@@ -61,7 +60,6 @@ import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;
/**
@@ -90,6 +88,7 @@ public class RenditionService2Test
@Mock private RuleService ruleService;
@Mock private TransformServiceRegistryImpl transformServiceRegistry;
@Mock private TransformReplyProvider transformReplyProvider;
@Mock private AsynchronousExtractor asynchronousExtractor;
private NodeRef nodeRef = new NodeRef("workspace://spacesStore/test-id");
private NodeRef nodeRefMissing = new NodeRef("workspace://spacesStore/bad-test-id");
@@ -154,6 +153,7 @@ public class RenditionService2Test
renditionService2.setTransformReplyProvider(transformReplyProvider);
renditionService2.setEnabled(true);
renditionService2.setThumbnailsEnabled(true);
renditionService2.setAsynchronousExtractor(asynchronousExtractor);
renditionDefinitionRegistry2.setRenditionConfigDir("alfresco/renditions/test");
renditionDefinitionRegistry2.afterPropertiesSet();

View File

@@ -0,0 +1,98 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2019 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.rendition2;
import org.alfresco.repo.content.transform.UnsupportedTransformationException;
import org.alfresco.service.cmr.repository.ContentIOException;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import java.io.ByteArrayInputStream;
import static org.alfresco.model.ContentModel.PROP_CONTENT;
import static org.alfresco.repo.rendition2.TestSynchronousTransformClient.doTest;
import static org.alfresco.repo.rendition2.TestSynchronousTransformClient.isATest;
/**
* @author adavis
*/
public class TestAsynchronousTransformClient<T> implements TransformClient
{
private ContentService contentService;
private TransformClient delegate;
private RenditionService2Impl renditionService2;
public TestAsynchronousTransformClient(ContentService contentService, TransformClient delegate,
RenditionService2Impl renditionService2)
{
this.contentService = contentService;
this.delegate = delegate;
this.renditionService2 = renditionService2;
}
@Override
public void checkSupported(NodeRef sourceNodeRef, RenditionDefinition2 renditionDefinition, String sourceMimetype,
long sourceSizeInBytes, String contentUrl)
{
String targetMimetype = renditionDefinition.getTargetMimetype();
if (!isATest(sourceMimetype, targetMimetype))
{
delegate.checkSupported(sourceNodeRef, renditionDefinition, sourceMimetype, sourceSizeInBytes, contentUrl);
}
}
@Override
public void transform(NodeRef sourceNodeRef, RenditionDefinition2 renditionDefinition, String user,
int sourceContentHashCode)
throws UnsupportedTransformationException, ContentIOException
{
ContentReader reader = contentService.getReader(sourceNodeRef, PROP_CONTENT);
String sourceMimetype = reader.getMimetype();
String targetMimetype = renditionDefinition.getTargetMimetype();
if (isATest(sourceMimetype, targetMimetype))
{
ContentWriter writer = contentService.getTempWriter();
writer.setMimetype(targetMimetype);
doTest(sourceMimetype, targetMimetype, writer,
new TestSynchronousTransformClient.TestTransformClientCallback()
{
@Override
public void successfulTransform(ContentWriter writer)
{
ByteArrayInputStream inputStream = new ByteArrayInputStream("SUCCESS".getBytes());
renditionService2.consume(sourceNodeRef, inputStream, renditionDefinition,
sourceContentHashCode);
}
});
}
else
{
delegate.transform(sourceNodeRef, renditionDefinition, user, sourceContentHashCode);
}
}
}

View File

@@ -59,8 +59,7 @@ public class TestSynchronousTransformClient<T> implements SynchronousTransformCl
Map<String, String> actualOptions, String transformName, NodeRef sourceNodeRef)
{
boolean supported = true;
if (!sourceMimetype.equals(TEST_FAILING_MIME_TYPE) && !sourceMimetype.equals(TEST_LONG_RUNNING_MIME_TYPE) &&
!targetMimetype.equals(TEST_FAILING_MIME_TYPE) && !targetMimetype.equals(TEST_LONG_RUNNING_MIME_TYPE))
if (!isATest(sourceMimetype, targetMimetype))
{
supported = delegate.isSupported(sourceMimetype, sourceSizeInBytes, contentUrl, targetMimetype, actualOptions,
transformName, sourceNodeRef);
@@ -73,11 +72,46 @@ public class TestSynchronousTransformClient<T> implements SynchronousTransformCl
{
String sourceMimetype = reader.getMimetype();
String targetMimetype = writer.getMimetype();
if (sourceMimetype.equals(TEST_FAILING_MIME_TYPE) || targetMimetype.equals(TEST_FAILING_MIME_TYPE))
if (isATest(sourceMimetype, targetMimetype))
{
doTest(sourceMimetype, targetMimetype, writer, new TestTransformClientCallback());
}
else
{
delegate.transform(reader, writer, actualOptions, transformName, sourceNodeRef);
}
}
static boolean isATest(String sourceMimetype, String targetMimetype)
{
return isFailingTest(sourceMimetype, targetMimetype) ||
isLongRunningTest(sourceMimetype, targetMimetype) ||
isUserTest(sourceMimetype, targetMimetype);
}
static boolean isFailingTest(String sourceMimetype, String targetMimetype)
{
return sourceMimetype.equals(TEST_FAILING_MIME_TYPE) || targetMimetype.equals(TEST_FAILING_MIME_TYPE);
}
static boolean isLongRunningTest(String sourceMimetype, String targetMimetype)
{
return sourceMimetype.equals(TEST_LONG_RUNNING_MIME_TYPE) || targetMimetype.equals(TEST_LONG_RUNNING_MIME_TYPE);
}
static boolean isUserTest(String sourceMimetype, String targetMimetype)
{
return sourceMimetype.equals(TEST_USER_MIME_TYPE) || targetMimetype.equals(TEST_USER_MIME_TYPE);
}
static void doTest(String sourceMimetype, String targetMimetype, ContentWriter writer,
TestTransformClientCallback callback)
{
if (isFailingTest(sourceMimetype, targetMimetype))
{
throw new ContentServiceTransientException("Transformation intentionally failed for test purposes.");
}
else if (sourceMimetype.equals(TEST_LONG_RUNNING_MIME_TYPE) || targetMimetype.equals(TEST_LONG_RUNNING_MIME_TYPE))
else if (isLongRunningTest(sourceMimetype, targetMimetype))
{
try
{
@@ -87,9 +121,9 @@ public class TestSynchronousTransformClient<T> implements SynchronousTransformCl
{
e.printStackTrace();
}
writer.putContent("SUCCESS");
callback.successfulTransform(writer);
}
else if (sourceMimetype.equals(TEST_USER_MIME_TYPE) || targetMimetype.equals(TEST_USER_MIME_TYPE))
else if (isUserTest(sourceMimetype, targetMimetype))
{
String username = AuthenticationUtil.getFullyAuthenticatedUser();
if (!EXPECTED_USER.equals(username))
@@ -97,11 +131,7 @@ public class TestSynchronousTransformClient<T> implements SynchronousTransformCl
throw new ContentIOException(
"Expected username '" + EXPECTED_USER + "' but found '" + username + "'");
}
writer.putContent("SUCCESS");
}
else
{
delegate.transform(reader, writer, actualOptions, transformName, sourceNodeRef);
callback.successfulTransform(writer);
}
}
@@ -110,4 +140,12 @@ public class TestSynchronousTransformClient<T> implements SynchronousTransformCl
{
return delegate.getName();
}
static class TestTransformClientCallback
{
public void successfulTransform(ContentWriter writer)
{
writer.putContent("SUCCESS");
}
}
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2019 Alfresco Software Limited
* Copyright (C) 2005 - 2020 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -125,6 +125,11 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
{
private static Log logger = LogFactory.getLog(ThumbnailServiceImplTest.class);
/**
* A test Thumbnail that is not know to the new RenditionService2, so is processed the very old way.
*/
public static final String TEST_THUMBNAIL = "testThumbnail";
private NodeService secureNodeService;
private RenditionService renditionService;
private ThumbnailService thumbnailService;
@@ -171,8 +176,26 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
this.folder = this.secureNodeService.createNode(this.rootNodeRef, ContentModel.ASSOC_CHILDREN,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "testFolder"), ContentModel.TYPE_FOLDER)
.getChildRef();
ThumbnailRegistry thumbnailRegistry = thumbnailService.getThumbnailRegistry();
createTestThumbnail(thumbnailRegistry);
}
public static void createTestThumbnail(ThumbnailRegistry thumbnailRegistry)
{
// Create a thumbnail that RenditionService2 knows nothing about so cannot process.
if (thumbnailRegistry.getThumbnailDefinition(TEST_THUMBNAIL) == null)
{
ThumbnailDefinition doclib = thumbnailRegistry.getThumbnailDefinition("doclib");
ThumbnailDefinition testThumbnailDefinition = new ThumbnailDefinition(doclib.getMimetype(), doclib.getTransformationOptions(), TEST_THUMBNAIL);
testThumbnailDefinition.setFailureHandlingOptions(doclib.getFailureHandlingOptions());
testThumbnailDefinition.setPlaceHolderResourcePath(doclib.getPlaceHolderResourcePath());
testThumbnailDefinition.setMimeAwarePlaceHolderResourcePath(doclib.getMimeAwarePlaceHolderResourcePath());
testThumbnailDefinition.setRunAs(doclib.getRunAs());
thumbnailRegistry.addThumbnailDefinition(testThumbnailDefinition);
}
}
private void checkTransformer()
{
if (!synchronousTransformClient.isSupported(MimetypeMap.MIMETYPE_IMAGE_JPEG, -1, null,
@@ -387,8 +410,8 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
{
public Void execute() throws Throwable
{
ThumbnailDefinition thumbnailDef = thumbnailService.getThumbnailRegistry().getThumbnailDefinition("doclib");
ThumbnailDefinition thumbnailDef = thumbnailService.getThumbnailRegistry().getThumbnailDefinition(TEST_THUMBNAIL);
Action createThumbnailAction = ThumbnailHelper.createCreateThumbnailAction(thumbnailDef, services);
actionService.executeAction(createThumbnailAction, corruptNode, true, true);
return null;
@@ -409,17 +432,17 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
Map<String, FailedThumbnailInfo> failedThumbnails = thumbnailService.getFailedThumbnails(corruptNode);
assertEquals("Wrong number of failed thumbnails", 1, failedThumbnails.size());
assertTrue("Missing QName for failed thumbnail", failedThumbnails.containsKey("doclib"));
final FailedThumbnailInfo doclibFailureInfo = failedThumbnails.get("doclib");
assertTrue("Missing QName for failed thumbnail", failedThumbnails.containsKey(TEST_THUMBNAIL));
final FailedThumbnailInfo doclibFailureInfo = failedThumbnails.get(TEST_THUMBNAIL);
assertNotNull("Failure info was null", doclibFailureInfo);
assertEquals("Failure count was wrong.", 1, doclibFailureInfo.getFailureCount());
assertEquals("thumbnail name was wrong.", "doclib", doclibFailureInfo.getThumbnailDefinitionName());
assertEquals("thumbnail name was wrong.", TEST_THUMBNAIL, doclibFailureInfo.getThumbnailDefinitionName());
return null;
}
});
// If you uncomment this line and set the timeout to a value greater than ${system.thumbnail.minimum.retry.period} * 1000.
// If you uncomment this line and set the timeout to a value greater than ${system.thumbnail.retryPeriod} * 1000.
// Then the retry period will have passed, the below re-thumbnail attempt will be made and the test will fail with a
// failureCount == 2.
//
@@ -431,7 +454,7 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
{
public Void execute() throws Throwable
{
ThumbnailDefinition thumbnailDef = thumbnailService.getThumbnailRegistry().getThumbnailDefinition("doclib");
ThumbnailDefinition thumbnailDef = thumbnailService.getThumbnailRegistry().getThumbnailDefinition(TEST_THUMBNAIL);
Action createThumbnailAction = ThumbnailHelper.createCreateThumbnailAction(thumbnailDef, services);
actionService.executeAction(createThumbnailAction, corruptNode, true, true);
@@ -448,11 +471,11 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
Map<String, FailedThumbnailInfo> failedThumbnails = thumbnailService.getFailedThumbnails(corruptNode);
assertEquals("Wrong number of failed thumbnails", 1, failedThumbnails.size());
assertTrue("Missing QName for failed thumbnail", failedThumbnails.containsKey("doclib"));
final FailedThumbnailInfo doclibFailureInfo = failedThumbnails.get("doclib");
assertTrue("Missing QName for failed thumbnail", failedThumbnails.containsKey(TEST_THUMBNAIL));
final FailedThumbnailInfo doclibFailureInfo = failedThumbnails.get(TEST_THUMBNAIL);
assertNotNull("Failure info was null", doclibFailureInfo);
assertEquals("Failure count was wrong.", 1, doclibFailureInfo.getFailureCount());
assertEquals("thumbnail name was wrong.", "doclib", doclibFailureInfo.getThumbnailDefinitionName());
assertEquals("thumbnail name was wrong.", TEST_THUMBNAIL, doclibFailureInfo.getThumbnailDefinitionName());
return null;
}
@@ -498,7 +521,7 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
{
public Void execute() throws Throwable
{
ThumbnailDefinition thumbnailDef = thumbnailService.getThumbnailRegistry().getThumbnailDefinition("doclib");
ThumbnailDefinition thumbnailDef = thumbnailService.getThumbnailRegistry().getThumbnailDefinition(TEST_THUMBNAIL);
Action createThumbnailAction = ThumbnailHelper.createCreateThumbnailAction(thumbnailDef, services);
actionService.executeAction(createThumbnailAction, corruptNode, true, true);
return null;
@@ -518,8 +541,8 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
Map<String, FailedThumbnailInfo> failedThumbnails = thumbnailService.getFailedThumbnails(corruptNode);
assertEquals("Wrong number of failed thumbnails", 1, failedThumbnails.size());
assertTrue("Missing QName for failed thumbnail", failedThumbnails.containsKey("doclib"));
final FailedThumbnailInfo doclibFailureInfo = failedThumbnails.get("doclib");
assertTrue("Missing QName for failed thumbnail", failedThumbnails.containsKey(TEST_THUMBNAIL));
final FailedThumbnailInfo doclibFailureInfo = failedThumbnails.get(TEST_THUMBNAIL);
assertNotNull("Failure info was null", doclibFailureInfo);
return doclibFailureInfo.getFailedThumbnailNode();
@@ -573,7 +596,7 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
{
public Void execute() throws Throwable
{
ThumbnailDefinition thumbnailDef = thumbnailService.getThumbnailRegistry().getThumbnailDefinition("doclib");
ThumbnailDefinition thumbnailDef = thumbnailService.getThumbnailRegistry().getThumbnailDefinition(TEST_THUMBNAIL);
Action createThumbnailAction = ThumbnailHelper.createCreateThumbnailAction(thumbnailDef, services);
actionService.executeAction(createThumbnailAction, testNode, true, true);
@@ -1337,24 +1360,24 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
concurrentWork.verify(source);
}
final int numIterations = 20;
final int multiples = 5;
// Wait for thumbnail(s) to finish
long endTime = (new Date()).getTime();
for (final ExpectedThumbnail expectedThumbnail : expectedThumbnails) {
NodeRef thumbnail = null;
while ((endTime - startTime) < (TEST_LONG_RUNNING_TRANSFORM_TIME * numIterations)) {
while ((endTime - startTime) < (TEST_LONG_RUNNING_TRANSFORM_TIME * multiples)) {
thumbnail = transactionService.getRetryingTransactionHelper()
.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<NodeRef>() {
public NodeRef execute() throws Throwable {
return thumbnailService.getThumbnailByName(source, ContentModel.PROP_CONTENT,
expectedThumbnail.getThumbnailName());
}
}, false, true);
}, true, true);
if (thumbnail == null) {
Thread.sleep(200);
logger.debug("Elapsed " + (endTime - startTime) + " ms of "
+ TEST_LONG_RUNNING_TRANSFORM_TIME * numIterations + " ms waiting for "
+ TEST_LONG_RUNNING_TRANSFORM_TIME * multiples + " ms waiting for "
+ expectedThumbnail.getThumbnailName());
endTime = (new Date()).getTime();
} else {

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2019 Alfresco Software Limited
* Copyright (C) 2005 - 2020 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -242,10 +242,13 @@ public class LocalTransformServiceRegistryConfigTest extends TransformServiceReg
targetMimetype.add("image/gif");
targetMimetype.add("image/tiff");
imagemagickSupportedTransformation.put("image/tiff", targetMimetype);
targetMimetype = new ArrayList<>(targetMimetype);
targetMimetype.add("image/png");
targetMimetype.add("image/jpeg");
imagemagickSupportedTransformation.put("image/gif", targetMimetype);
imagemagickSupportedTransformation.put("image/jpeg", targetMimetype);
targetMimetype = new ArrayList<>(targetMimetype);
targetMimetype.add("alfresco-metadata-extract"); // Metadata extract and embed types should be excluded from pipeline cartesian products
imagemagickSupportedTransformation.put("image/png", targetMimetype);
targetMimetype = new ArrayList<>();
targetMimetype.add("target1");
@@ -333,8 +336,7 @@ public class LocalTransformServiceRegistryConfigTest extends TransformServiceReg
@Override
public void testJsonConfig() throws IOException
{
// Not 60, 60 as we have added source->target1..3 to three transformers
internalTestJsonConfig(63, 69);
internalTestJsonConfig(64, 70);
}
@Test
@@ -368,7 +370,7 @@ public class LocalTransformServiceRegistryConfigTest extends TransformServiceReg
switch (t.transformer.getTransformerName())
{
case "imagemagick":
assertEquals(t.transformer.getTransformerName() + " incorrect number of supported transform", 17, t.transformer.getSupportedSourceAndTargetList().size());
assertEquals(t.transformer.getTransformerName() + " incorrect number of supported transform", 18, t.transformer.getSupportedSourceAndTargetList().size());
assertEquals( t.transformer.getTransformerName() + "incorrect number of transform option names", 1, t.transformer.getTransformOptions().size());
assertEquals( t.transformer.getTransformerName() + "incorrect number of transform options", 6, countTopLevelOptions(t.transformer.getTransformOptions()));
assertEquals(t.transformer.getTransformerName() + " expected to not be a transformer pipeline", t.transformer.getTransformerPipeline().size(), 0);
@@ -428,6 +430,7 @@ public class LocalTransformServiceRegistryConfigTest extends TransformServiceReg
break;
case "officeToImageViaPdf":
// Note we will get 35 entries in getSupportedSourceAndTargetList() if the metadata transforms are not excluded
assertEquals(t.transformer.getTransformerName() + " incorrect number of supported transform", 28, t.transformer.getSupportedSourceAndTargetList().size());
assertEquals( t.transformer.getTransformerName() + "incorrect number of transform option names", 2, t.transformer.getTransformOptions().size());
assertEquals( t.transformer.getTransformerName() + "incorrect number of transform options", 11, countTopLevelOptions(t.transformer.getTransformOptions()));