mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-31 17:39:05 +00:00
Merged BRANCHES/DEV/RGAUSS/4.2-CORE-CHANGES-43298 to HEAD:
43309: Merged BRANCHES/DEV/RGAUSS/V4.1-BUG-FIX-TAG-MAPPING to BRANCHES/DEV/RGAUSS/4.2-CORE-CHANGES-43298: 39447: Merged BRANCHES/DEV/RGAUSS/V4.1-BUG-FIX-38527 to BRANCHES/DEV/RGAUSS/V4.1-BUG-FIX-TAG-MAPPING: 38719: ALF-14965: Ability to Map Extracted Metadata to Standard Tags - Added more specific MalformedNodeRefException - Changed NodeRef to throw MalformedNodeRefException on a bad string constructor rather than generic AlfrescoRunTimeException - ContentMetadataExtracter: Added enableStringTagging boolean field - ContentMetadataExtracter: Added taggingService - ContentMetadataExtracter: Added addTags method responsible for iterating the raw value from the metadata extracter and creating either string tags or NodeRef links - ContentMetadataExtracter: Added check for instanceof AbstractMappingMetadataExtracter and if so set its enableStringTagging field - ContentMetadataExtracter: Added check for enableStringTagging in executeImpl and if enabled call addTags - AbstractMappingMetadataExtracter: Added enableStringTagging boolean field - AbstractMappingMetadataExtracter: Added catch of MalformedNodeRefException and if string tagging enabled leave the raw properties for processing by ContentMetadataExtracter 39448: ALF-14965: Ability to Map Extracted Metadata to Standard Tags - Added fix for single valued raw properties - Added tag mapping unit test and test resource 39449: ALF-14965: Ability to Map Extracted Metadata to Standard Tags - Added better class javadoc 39479: ALF-14965: Ability to Map Extracted Metadata to Standard Tags - Changed behavior of addition of tags by NodeRef - Changed where some items were setup in the unit test - Added manual test keywords to those extracted from file in unit test - Added testing of addition of tag by NodeRef 43324: ALF-14965: Ability to Map Extracted Metadata to Standard Tags - Added Javadoc to AbstractMappingMetadataExtracter.setEnableStringTagging - Changed check of enableStringTagging in AbstractMappingMetadataExtracter.convertSystemPropertyValues to allow graceful failure if mappings to cm:taggable are present but enableStringTagging is false git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@43335 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -19,6 +19,8 @@
|
|||||||
package org.alfresco.repo.action.executer;
|
package org.alfresco.repo.action.executer;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -26,6 +28,7 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.alfresco.model.ContentModel;
|
import org.alfresco.model.ContentModel;
|
||||||
|
import org.alfresco.repo.content.metadata.AbstractMappingMetadataExtracter;
|
||||||
import org.alfresco.repo.content.metadata.MetadataExtracter;
|
import org.alfresco.repo.content.metadata.MetadataExtracter;
|
||||||
import org.alfresco.repo.content.metadata.MetadataExtracterRegistry;
|
import org.alfresco.repo.content.metadata.MetadataExtracterRegistry;
|
||||||
import org.alfresco.service.cmr.action.Action;
|
import org.alfresco.service.cmr.action.Action;
|
||||||
@@ -37,6 +40,8 @@ import org.alfresco.service.cmr.repository.ContentReader;
|
|||||||
import org.alfresco.service.cmr.repository.ContentService;
|
import org.alfresco.service.cmr.repository.ContentService;
|
||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
import org.alfresco.service.cmr.repository.NodeService;
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
|
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
|
||||||
|
import org.alfresco.service.cmr.tagging.TaggingService;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
@@ -60,8 +65,10 @@ public class ContentMetadataExtracter extends ActionExecuterAbstractBase
|
|||||||
private NodeService nodeService;
|
private NodeService nodeService;
|
||||||
private ContentService contentService;
|
private ContentService contentService;
|
||||||
private DictionaryService dictionaryService;
|
private DictionaryService dictionaryService;
|
||||||
|
private TaggingService taggingService;
|
||||||
private MetadataExtracterRegistry metadataExtracterRegistry;
|
private MetadataExtracterRegistry metadataExtracterRegistry;
|
||||||
private boolean carryAspectProperties = true;
|
private boolean carryAspectProperties = true;
|
||||||
|
private boolean enableStringTagging = false;
|
||||||
|
|
||||||
public ContentMetadataExtracter()
|
public ContentMetadataExtracter()
|
||||||
{
|
{
|
||||||
@@ -91,6 +98,14 @@ public class ContentMetadataExtracter extends ActionExecuterAbstractBase
|
|||||||
this.dictionaryService = dictService;
|
this.dictionaryService = dictService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param taggingService The TaggingService to set.
|
||||||
|
*/
|
||||||
|
public void setTaggingService(TaggingService taggingService)
|
||||||
|
{
|
||||||
|
this.taggingService = taggingService;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param metadataExtracterRegistry The metadataExtracterRegistry to set.
|
* @param metadataExtracterRegistry The metadataExtracterRegistry to set.
|
||||||
*/
|
*/
|
||||||
@@ -111,6 +126,91 @@ public class ContentMetadataExtracter extends ActionExecuterAbstractBase
|
|||||||
this.carryAspectProperties = carryAspectProperties;
|
this.carryAspectProperties = carryAspectProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not to enable mapping of simple strings to cm:taggable tags
|
||||||
|
*
|
||||||
|
* @param enableStringTagging <tt>true</tt> find or create tags for each string
|
||||||
|
* mapped to cm:taggable. <tt>false</tt> (default)
|
||||||
|
* ignore mapping strings to tags.
|
||||||
|
*/
|
||||||
|
public void setEnableStringTagging(boolean enableStringTagging)
|
||||||
|
{
|
||||||
|
this.enableStringTagging = enableStringTagging;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates the values of the taggable property which the metadata
|
||||||
|
* extractor should have already attempted to convert values to {@link NodeRef}s.
|
||||||
|
* <p>
|
||||||
|
* If conversion by the metadata extracter failed due to a MalformedNodeRefException
|
||||||
|
* the taggable property should still contain raw string values.
|
||||||
|
* <p>
|
||||||
|
* Mixing of NodeRefs and string values is permitted so each raw value is
|
||||||
|
* checked for a valid NodeRef representation and if so, converts to a NodeRef,
|
||||||
|
* if not, adds as a tag via the {@link TaggingService}.
|
||||||
|
*
|
||||||
|
* @param actionedUponNodeRef The NodeRef being actioned upon
|
||||||
|
* @param propertyDef the PropertyDefinition of the taggable property
|
||||||
|
* @param rawValue the raw value from the metadata extracter
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected void addTags(NodeRef actionedUponNodeRef, PropertyDefinition propertyDef, Serializable rawValue)
|
||||||
|
{
|
||||||
|
List<String> tags = new ArrayList<String>();
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("converting " + rawValue.toString() + " of type " +
|
||||||
|
rawValue.getClass().getCanonicalName() + " to tags");
|
||||||
|
}
|
||||||
|
if (rawValue instanceof Collection<?>)
|
||||||
|
{
|
||||||
|
for (Object singleValue : (Collection<?>) rawValue)
|
||||||
|
{
|
||||||
|
if (singleValue instanceof String)
|
||||||
|
{
|
||||||
|
if (NodeRef.isNodeRef((String) singleValue))
|
||||||
|
{
|
||||||
|
// Convert to a NodeRef
|
||||||
|
Serializable convertedPropertyValue = (Serializable) DefaultTypeConverter.INSTANCE.convert(
|
||||||
|
propertyDef.getDataType(),
|
||||||
|
(String) singleValue);
|
||||||
|
String tagName = (String) nodeService.getProperty((NodeRef) convertedPropertyValue, ContentModel.PROP_NAME);
|
||||||
|
if (logger.isTraceEnabled())
|
||||||
|
{
|
||||||
|
logger.trace("found tag '" + tagName + "' from tag nodeRef '" + (String) singleValue + "', " +
|
||||||
|
"adding to " + actionedUponNodeRef.toString());
|
||||||
|
}
|
||||||
|
tags.add(tagName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Must be a simple string
|
||||||
|
if (logger.isTraceEnabled())
|
||||||
|
{
|
||||||
|
logger.trace("adding string tag '" + (String) singleValue + "' to " + actionedUponNodeRef.toString());
|
||||||
|
}
|
||||||
|
tags.add((String) singleValue);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (singleValue instanceof NodeRef)
|
||||||
|
{
|
||||||
|
String tagName = (String) nodeService.getProperty((NodeRef) singleValue, ContentModel.PROP_NAME);
|
||||||
|
tags.add(tagName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (rawValue instanceof String)
|
||||||
|
{
|
||||||
|
if (logger.isTraceEnabled())
|
||||||
|
{
|
||||||
|
logger.trace("adding tag '" + (String) rawValue + "' to " + actionedUponNodeRef.toString());
|
||||||
|
}
|
||||||
|
tags.add((String) rawValue);
|
||||||
|
}
|
||||||
|
taggingService.addTags(actionedUponNodeRef, tags);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see org.alfresco.repo.action.executer.ActionExecuter#execute(org.alfresco.service.cmr.repository.NodeRef,
|
* @see org.alfresco.repo.action.executer.ActionExecuter#execute(org.alfresco.service.cmr.repository.NodeRef,
|
||||||
* NodeRef)
|
* NodeRef)
|
||||||
@@ -144,6 +244,10 @@ public class ContentMetadataExtracter extends ActionExecuterAbstractBase
|
|||||||
// There is no extracter to use
|
// There is no extracter to use
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (enableStringTagging && (extracter instanceof AbstractMappingMetadataExtracter))
|
||||||
|
{
|
||||||
|
((AbstractMappingMetadataExtracter) extracter).setEnableStringTagging(enableStringTagging);
|
||||||
|
}
|
||||||
|
|
||||||
// Get all the node's properties
|
// Get all the node's properties
|
||||||
Map<QName, Serializable> nodeProperties = nodeService.getProperties(actionedUponNodeRef);
|
Map<QName, Serializable> nodeProperties = nodeService.getProperties(actionedUponNodeRef);
|
||||||
@@ -212,11 +316,22 @@ public class ContentMetadataExtracter extends ActionExecuterAbstractBase
|
|||||||
ClassDefinition propertyContainerDef = propertyDef.getContainerClass();
|
ClassDefinition propertyContainerDef = propertyDef.getContainerClass();
|
||||||
if (propertyContainerDef.isAspect())
|
if (propertyContainerDef.isAspect())
|
||||||
{
|
{
|
||||||
QName aspectQName = propertyContainerDef.getName();
|
if (enableStringTagging && propertyContainerDef.getName().equals(ContentModel.ASPECT_TAGGABLE))
|
||||||
requiredAspectQNames.add(aspectQName);
|
{
|
||||||
// Get all properties associated with the aspect
|
Serializable oldValue = nodeProperties.get(propertyQName);
|
||||||
Set<QName> aspectProperties = propertyContainerDef.getProperties().keySet();
|
addTags(actionedUponNodeRef, propertyDef, oldValue);
|
||||||
aspectPropertyQNames.addAll(aspectProperties);
|
// Replace the raw value with the created tag NodeRefs
|
||||||
|
nodeProperties.put(ContentModel.PROP_TAGS,
|
||||||
|
nodeService.getProperty(actionedUponNodeRef, ContentModel.PROP_TAGS));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QName aspectQName = propertyContainerDef.getName();
|
||||||
|
requiredAspectQNames.add(aspectQName);
|
||||||
|
// Get all properties associated with the aspect
|
||||||
|
Set<QName> aspectProperties = propertyContainerDef.getProperties().keySet();
|
||||||
|
aspectPropertyQNames.addAll(aspectProperties);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -0,0 +1,385 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2012 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.action.executer;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.alfresco.model.ContentModel;
|
||||||
|
import org.alfresco.repo.action.ActionImpl;
|
||||||
|
import org.alfresco.repo.action.ActionModel;
|
||||||
|
import org.alfresco.repo.action.AsynchronousActionExecutionQueuePolicies;
|
||||||
|
import org.alfresco.repo.content.MimetypeMap;
|
||||||
|
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;
|
||||||
|
import org.alfresco.repo.policy.PolicyComponent;
|
||||||
|
import org.alfresco.repo.security.authentication.AuthenticationComponent;
|
||||||
|
import org.alfresco.repo.tagging.TaggingServiceImplTest;
|
||||||
|
import org.alfresco.repo.tagging.TaggingServiceImplTest.AsyncOccurs;
|
||||||
|
import org.alfresco.repo.tagging.UpdateTagScopesActionExecuter;
|
||||||
|
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
||||||
|
import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
|
||||||
|
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||||
|
import org.alfresco.service.cmr.audit.AuditService;
|
||||||
|
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.StoreRef;
|
||||||
|
import org.alfresco.service.cmr.tagging.TaggingService;
|
||||||
|
import org.alfresco.service.namespace.NamespaceService;
|
||||||
|
import org.alfresco.service.namespace.QName;
|
||||||
|
import org.alfresco.service.transaction.TransactionService;
|
||||||
|
import org.alfresco.util.ApplicationContextHelper;
|
||||||
|
import org.alfresco.util.GUID;
|
||||||
|
import org.apache.tika.metadata.Metadata;
|
||||||
|
import org.apache.tika.parser.Parser;
|
||||||
|
import org.apache.tika.parser.jpeg.JpegParser;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test of the ActionExecuter for extracting metadata, specifically for
|
||||||
|
* the mapping to cm:taggable tags which requires different transaction
|
||||||
|
* mechanisms than the existing {@link ContentMetadataExtracterTest}.
|
||||||
|
*
|
||||||
|
* @author Roy Wetherall
|
||||||
|
* @author Nick Burch
|
||||||
|
* @author Ray Gauss II
|
||||||
|
*/
|
||||||
|
public class ContentMetadataExtracterTagMappingTest extends TestCase
|
||||||
|
{
|
||||||
|
private static ConfigurableApplicationContext ctx =
|
||||||
|
(ConfigurableApplicationContext)ApplicationContextHelper.getApplicationContext();
|
||||||
|
|
||||||
|
protected static final String TAGGING_AUDIT_APPLICATION_NAME = "Alfresco Tagging Service";
|
||||||
|
protected static final String QUICK_FILENAME = "quickIPTC.jpg";
|
||||||
|
protected static final String QUICK_KEYWORD = "fox";
|
||||||
|
protected static final String TAG_1 = "tag one";
|
||||||
|
protected static final String TAG_2 = "tag two";
|
||||||
|
protected static final String TAG_3 = "Tag Three";
|
||||||
|
|
||||||
|
/** Services */
|
||||||
|
private TaggingService taggingService;
|
||||||
|
private NodeService nodeService;
|
||||||
|
private ContentService contentService;
|
||||||
|
private AuditService auditService;
|
||||||
|
private TransactionService transactionService;
|
||||||
|
private AuthenticationComponent authenticationComponent;
|
||||||
|
private AsyncOccurs asyncOccurs;
|
||||||
|
|
||||||
|
private static StoreRef storeRef;
|
||||||
|
private static NodeRef rootNode;
|
||||||
|
private NodeRef folder;
|
||||||
|
private NodeRef document;
|
||||||
|
|
||||||
|
private ContentMetadataExtracter executer;
|
||||||
|
private TagMappingMetadataExtracter extractor;
|
||||||
|
|
||||||
|
private static boolean init = false;
|
||||||
|
|
||||||
|
private final static String ID = GUID.generate();
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setUp() throws Exception
|
||||||
|
{
|
||||||
|
// Detect any dangling transactions as there is a lot of direct UserTransaction manipulation
|
||||||
|
if (AlfrescoTransactionSupport.getTransactionReadState() != TxnReadState.TXN_NONE)
|
||||||
|
{
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"There should not be any transactions when starting test: " +
|
||||||
|
AlfrescoTransactionSupport.getTransactionId() + " started at " +
|
||||||
|
new Date(AlfrescoTransactionSupport.getTransactionStartTime()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get services
|
||||||
|
this.taggingService = (TaggingService)ctx.getBean("TaggingService");
|
||||||
|
this.nodeService = (NodeService) ctx.getBean("NodeService");
|
||||||
|
this.contentService = (ContentService) ctx.getBean("ContentService");
|
||||||
|
|
||||||
|
this.transactionService = (TransactionService)ctx.getBean("transactionComponent");
|
||||||
|
this.auditService = (AuditService)ctx.getBean("auditService");
|
||||||
|
this.authenticationComponent = (AuthenticationComponent)ctx.getBean("authenticationComponent");
|
||||||
|
|
||||||
|
this.executer = (ContentMetadataExtracter) ctx.getBean("extract-metadata");
|
||||||
|
executer.setEnableStringTagging(true);
|
||||||
|
executer.setTaggingService(taggingService);
|
||||||
|
|
||||||
|
if (init == false)
|
||||||
|
{
|
||||||
|
this.transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Void>(){
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void execute() throws Throwable
|
||||||
|
{
|
||||||
|
// Authenticate as the system user
|
||||||
|
authenticationComponent.setSystemUserAsCurrentUser();
|
||||||
|
|
||||||
|
// Create the store and get the root node
|
||||||
|
ContentMetadataExtracterTagMappingTest.storeRef = nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis());
|
||||||
|
ContentMetadataExtracterTagMappingTest.rootNode = nodeService.getRootNode(ContentMetadataExtracterTagMappingTest.storeRef);
|
||||||
|
|
||||||
|
// Create the required tagging category
|
||||||
|
NodeRef catContainer = nodeService.createNode(ContentMetadataExtracterTagMappingTest.rootNode, ContentModel.ASSOC_CHILDREN, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "categoryContainer"), ContentModel.TYPE_CONTAINER).getChildRef();
|
||||||
|
NodeRef catRoot = nodeService.createNode(
|
||||||
|
catContainer,
|
||||||
|
ContentModel.ASSOC_CHILDREN,
|
||||||
|
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "categoryRoot"),
|
||||||
|
ContentModel.TYPE_CATEGORYROOT).getChildRef();
|
||||||
|
nodeService.createNode(
|
||||||
|
catRoot,
|
||||||
|
ContentModel.ASSOC_CATEGORIES,
|
||||||
|
ContentModel.ASPECT_TAGGABLE,
|
||||||
|
ContentModel.TYPE_CATEGORY).getChildRef();
|
||||||
|
|
||||||
|
MetadataExtracterRegistry registry = (MetadataExtracterRegistry) ctx.getBean("metadataExtracterRegistry");
|
||||||
|
extractor = new TagMappingMetadataExtracter();
|
||||||
|
extractor.setRegistry(registry);
|
||||||
|
extractor.register();
|
||||||
|
|
||||||
|
init = true;
|
||||||
|
return null;
|
||||||
|
}});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// We want to know when tagging actions have finished running
|
||||||
|
asyncOccurs = (new TaggingServiceImplTest()).new AsyncOccurs();
|
||||||
|
((PolicyComponent)ctx.getBean("policyComponent")).bindClassBehaviour(
|
||||||
|
AsynchronousActionExecutionQueuePolicies.OnAsyncActionExecute.QNAME,
|
||||||
|
ActionModel.TYPE_ACTION,
|
||||||
|
new JavaBehaviour(asyncOccurs, "onAsyncActionExecute", NotificationFrequency.EVERY_EVENT)
|
||||||
|
);
|
||||||
|
|
||||||
|
// We do want action tracking whenever the tag scope updater runs
|
||||||
|
UpdateTagScopesActionExecuter updateTagsAction =
|
||||||
|
(UpdateTagScopesActionExecuter)ctx.getBean("update-tagscope");
|
||||||
|
updateTagsAction.setTrackStatus(true);
|
||||||
|
|
||||||
|
// Create the folders and documents to be tagged
|
||||||
|
createTestDocumentsAndFolders();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void tearDown() throws Exception
|
||||||
|
{
|
||||||
|
removeTestDocumentsAndFolders();
|
||||||
|
if (AlfrescoTransactionSupport.getTransactionReadState() != TxnReadState.TXN_NONE)
|
||||||
|
{
|
||||||
|
fail("Test is not transaction-safe. Fix up transaction handling and re-test.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createTestDocumentsAndFolders() throws Exception
|
||||||
|
{
|
||||||
|
this.transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Void>(){
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void execute() throws Throwable
|
||||||
|
{
|
||||||
|
|
||||||
|
// Authenticate as the system user
|
||||||
|
authenticationComponent.setSystemUserAsCurrentUser();
|
||||||
|
|
||||||
|
String guid = GUID.generate();
|
||||||
|
|
||||||
|
// Create a folder
|
||||||
|
Map<QName, Serializable> folderProps = new HashMap<QName, Serializable>(1);
|
||||||
|
folderProps.put(ContentModel.PROP_NAME, "testFolder" + guid);
|
||||||
|
folder = nodeService.createNode(
|
||||||
|
ContentMetadataExtracterTagMappingTest.rootNode,
|
||||||
|
ContentModel.ASSOC_CHILDREN,
|
||||||
|
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "testFolder" + guid),
|
||||||
|
ContentModel.TYPE_FOLDER,
|
||||||
|
folderProps).getChildRef();
|
||||||
|
|
||||||
|
// Create a node
|
||||||
|
Map<QName, Serializable> docProps = new HashMap<QName, Serializable>(1);
|
||||||
|
docProps.put(ContentModel.PROP_NAME, "testDocument" + guid + ".jpg");
|
||||||
|
document = nodeService.createNode(
|
||||||
|
folder,
|
||||||
|
ContentModel.ASSOC_CONTAINS,
|
||||||
|
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "testDocument" + guid + ".jpg"),
|
||||||
|
ContentModel.TYPE_CONTENT,
|
||||||
|
docProps).getChildRef();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ContentWriter cw = contentService.getWriter(document, ContentModel.PROP_CONTENT, true);
|
||||||
|
cw.setMimetype(MimetypeMap.MIMETYPE_IMAGE_JPEG);
|
||||||
|
cw.putContent(AbstractContentTransformerTest.loadNamedQuickTestFile(QUICK_FILENAME));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
fail(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeTestDocumentsAndFolders() throws Exception
|
||||||
|
{
|
||||||
|
this.transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Void>(){
|
||||||
|
@Override
|
||||||
|
public Void execute() throws Throwable
|
||||||
|
{
|
||||||
|
// Authenticate as the system user
|
||||||
|
authenticationComponent.setSystemUserAsCurrentUser();
|
||||||
|
|
||||||
|
// If anything is a tag scope, stop it being
|
||||||
|
NodeRef[] nodes = new NodeRef[] { document, folder };
|
||||||
|
for(NodeRef nodeRef : nodes)
|
||||||
|
{
|
||||||
|
if(taggingService.isTagScope(nodeRef))
|
||||||
|
{
|
||||||
|
taggingService.removeTagScope(nodeRef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the sample nodes
|
||||||
|
for(NodeRef nodeRef : nodes)
|
||||||
|
{
|
||||||
|
nodeService.deleteNode(nodeRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tidy up the audit component, now all the nodes have gone
|
||||||
|
auditService.clearAudit(
|
||||||
|
TAGGING_AUDIT_APPLICATION_NAME,
|
||||||
|
0l, System.currentTimeMillis()+1
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TagMappingMetadataExtracter extends TikaPoweredMetadataExtracter
|
||||||
|
{
|
||||||
|
|
||||||
|
private String existingTagNodeRef;
|
||||||
|
|
||||||
|
public TagMappingMetadataExtracter()
|
||||||
|
{
|
||||||
|
super(Sets.newHashSet(MimetypeMap.MIMETYPE_IMAGE_JPEG));
|
||||||
|
Properties mappingProperties = new Properties();
|
||||||
|
// TODO move to new keyword once tika is upgraded
|
||||||
|
mappingProperties.put(Metadata.KEYWORDS, ContentModel.PROP_TAGS.toString());
|
||||||
|
mappingProperties.put(Metadata.DESCRIPTION, ContentModel.PROP_DESCRIPTION.toString());
|
||||||
|
setMappingProperties(mappingProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExistingTagNodeRef(String existingTagNodeRef)
|
||||||
|
{
|
||||||
|
this.existingTagNodeRef = existingTagNodeRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Map<String, Set<QName>> getDefaultMapping()
|
||||||
|
{
|
||||||
|
// No need to give anything back as we have explicitly set the mapping already
|
||||||
|
return new HashMap<String, Set<QName>>(0);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean isSupported(String sourceMimetype)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Add some test keywords to those actually extracted from the file including a nodeRef
|
||||||
|
List<String> keywords = new ArrayList<String>(Arrays.asList(new String[] { existingTagNodeRef, TAG_2, TAG_3 }));
|
||||||
|
Serializable extractedKeywords = rawMap.get(Metadata.KEYWORDS);
|
||||||
|
if (extractedKeywords != null && extractedKeywords instanceof String)
|
||||||
|
{
|
||||||
|
keywords.add((String) extractedKeywords);
|
||||||
|
}
|
||||||
|
else if (extractedKeywords != null && extractedKeywords instanceof Collection<?>)
|
||||||
|
{
|
||||||
|
keywords.addAll((Collection<? extends String>) extractedKeywords);
|
||||||
|
}
|
||||||
|
putRawValue(Metadata.KEYWORDS, (Serializable) keywords, rawMap);
|
||||||
|
return rawMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test execution of mapping strings to tags
|
||||||
|
*/
|
||||||
|
public void testTagMapping()
|
||||||
|
{
|
||||||
|
this.transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Void>(){
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void execute() throws Throwable
|
||||||
|
{
|
||||||
|
NodeRef existingTagNodeRef = taggingService.createTag(storeRef, TAG_1);
|
||||||
|
extractor.setExistingTagNodeRef(existingTagNodeRef.toString());
|
||||||
|
|
||||||
|
ActionImpl action = new ActionImpl(document, ID, ContentMetadataExtracter.EXECUTOR_NAME, null);
|
||||||
|
executer.execute(action, document);
|
||||||
|
|
||||||
|
// Test extracted properties
|
||||||
|
assertEquals(ContentMetadataExtracterTest.QUICK_DESCRIPTION,
|
||||||
|
nodeService.getProperty(document, ContentModel.PROP_DESCRIPTION));
|
||||||
|
assertTrue("storeRef tags should contain '" + QUICK_KEYWORD + "'",
|
||||||
|
taggingService.getTags(storeRef).contains(QUICK_KEYWORD));
|
||||||
|
assertTrue("document's tags should contain '" + QUICK_KEYWORD + "'",
|
||||||
|
taggingService.getTags(document).contains(QUICK_KEYWORD));
|
||||||
|
|
||||||
|
// Test manually added keyword
|
||||||
|
assertTrue("tags should contain '" + TAG_2 + "'",
|
||||||
|
taggingService.getTags(document).contains(TAG_2));
|
||||||
|
|
||||||
|
// Test manually added nodeRef keyword
|
||||||
|
assertTrue("tags should contain '" + TAG_1 + "'",
|
||||||
|
taggingService.getTags(document).contains(TAG_1));
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -38,11 +38,13 @@ import java.util.Set;
|
|||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
import org.alfresco.error.AlfrescoRuntimeException;
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
|
import org.alfresco.model.ContentModel;
|
||||||
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
||||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||||
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
||||||
import org.alfresco.service.cmr.repository.ContentReader;
|
import org.alfresco.service.cmr.repository.ContentReader;
|
||||||
import org.alfresco.service.cmr.repository.ContentWriter;
|
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||||
|
import org.alfresco.service.cmr.repository.MalformedNodeRefException;
|
||||||
import org.alfresco.service.cmr.repository.MimetypeService;
|
import org.alfresco.service.cmr.repository.MimetypeService;
|
||||||
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
|
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
|
||||||
import org.alfresco.service.cmr.repository.datatype.TypeConversionException;
|
import org.alfresco.service.cmr.repository.datatype.TypeConversionException;
|
||||||
@@ -114,6 +116,7 @@ abstract public class AbstractMappingMetadataExtracter implements MetadataExtrac
|
|||||||
private Map<QName, Set<String>> embedMapping;
|
private Map<QName, Set<String>> embedMapping;
|
||||||
private boolean inheritDefaultMapping;
|
private boolean inheritDefaultMapping;
|
||||||
private boolean inheritDefaultEmbedMapping;
|
private boolean inheritDefaultEmbedMapping;
|
||||||
|
private boolean enableStringTagging;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default constructor. If this is called, then {@link #isSupported(String)} should
|
* Default constructor. If this is called, then {@link #isSupported(String)} should
|
||||||
@@ -351,6 +354,18 @@ abstract public class AbstractMappingMetadataExtracter implements MetadataExtrac
|
|||||||
this.inheritDefaultMapping = inheritDefaultMapping;
|
this.inheritDefaultMapping = inheritDefaultMapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not to enable the pass through of simple strings to cm:taggable tags
|
||||||
|
*
|
||||||
|
* @param enableStringTagging <tt>true</tt> find or create tags for each string
|
||||||
|
* mapped to cm:taggable. <tt>false</tt> (default)
|
||||||
|
* ignore mapping strings to tags.
|
||||||
|
*/
|
||||||
|
public void setEnableStringTagging(boolean enableStringTagging)
|
||||||
|
{
|
||||||
|
this.enableStringTagging = enableStringTagging;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set if the embed property mappings augment or override the mapping generically provided by the
|
* Set if the embed property mappings augment or override the mapping generically provided by the
|
||||||
* extracter implementation. The default is <tt>false</tt>, i.e. any mapping set completely
|
* extracter implementation. The default is <tt>false</tt>, i.e. any mapping set completely
|
||||||
@@ -1298,6 +1313,38 @@ abstract public class AbstractMappingMetadataExtracter implements MetadataExtrac
|
|||||||
propertyValue);
|
propertyValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (MalformedNodeRefException e)
|
||||||
|
{
|
||||||
|
if (propertyQName.equals(ContentModel.PROP_TAGS))
|
||||||
|
{
|
||||||
|
if (enableStringTagging)
|
||||||
|
{
|
||||||
|
// We must want to map tag string values instead of nodeRefs
|
||||||
|
// ContentMetadataExtracter will take care of tagging by string
|
||||||
|
ArrayList<Object> list = new ArrayList<Object>(1);
|
||||||
|
for (Object value : (Object[]) propertyValue)
|
||||||
|
{
|
||||||
|
list.add(value);
|
||||||
|
}
|
||||||
|
convertedProperties.put(propertyQName, list);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (logger.isInfoEnabled())
|
||||||
|
{
|
||||||
|
logger.info("enableStringTagging is false and could not convert " +
|
||||||
|
propertyQName.toString() + ": " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (failOnTypeConversion)
|
||||||
|
{
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Done
|
// Done
|
||||||
return convertedProperties;
|
return convertedProperties;
|
||||||
|
BIN
source/test-resources/quick/quickIPTC.jpg
Normal file
BIN
source/test-resources/quick/quickIPTC.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
Reference in New Issue
Block a user