Java tutorial
/* * Copyright (C) 2005-2009 Alfresco Software Limited. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program 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 General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * As a special exception to the terms and conditions of version 2.0 of * the GPL, you may redistribute this Program in connection with Free/Libre * and Open Source Software ("FLOSS") applications as described in Alfresco's * FLOSS exception. You should have recieved a copy of the text describing * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ package fr.openwide.talendalfresco.alfresco.importer; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.repo.importer.DefaultContentHandler; import org.alfresco.repo.importer.ImportContentHandler; import org.alfresco.repo.importer.ImportNode; import org.alfresco.repo.importer.ImportParent; import org.alfresco.repo.importer.Importer; import org.alfresco.repo.importer.ImporterComponent; import org.alfresco.repo.importer.Parser; import org.alfresco.repo.importer.view.NodeContext; import org.alfresco.repo.policy.BehaviourFilter; import org.alfresco.repo.security.authentication.AuthenticationContext; import org.alfresco.service.cmr.dictionary.AssociationDefinition; import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition; import org.alfresco.service.cmr.dictionary.ClassDefinition; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.TypeDefinition; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.InvalidNodeRefException; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.XPathException; import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; import org.alfresco.service.cmr.rule.RuleService; import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.security.AccessPermission; import org.alfresco.service.cmr.security.AccessStatus; import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.cmr.security.OwnableService; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.view.ImportPackageHandler; import org.alfresco.service.cmr.view.ImporterBinding; import org.alfresco.service.cmr.view.ImporterException; import org.alfresco.service.cmr.view.ImporterProgress; import org.alfresco.service.cmr.view.ImporterService; import org.alfresco.service.cmr.view.Location; import org.alfresco.service.cmr.view.ImporterBinding.UUID_BINDING; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.util.ParameterCheck; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.util.StringUtils; import org.xml.sax.ContentHandler; /** * [talendalfresco] "almost" copy of ImportComponent v3.2 to allow extending it. * Changes : changed every private to protected * * Default implementation of the Importer Service * * @author David Caruana */ public class ContentImporterComponentBase implements ImporterService { // Logger protected static final Log logger = LogFactory.getLog(ImporterComponent.class); // default importer // TODO: Allow registration of plug-in parsers (by namespace) protected Parser viewParser; // supporting services protected NamespaceService namespaceService; protected DictionaryService dictionaryService; protected BehaviourFilter behaviourFilter; protected NodeService nodeService; protected SearchService searchService; protected ContentService contentService; protected RuleService ruleService; protected PermissionService permissionService; protected AuthorityService authorityService; protected AuthenticationContext authenticationContext; protected OwnableService ownableService; // binding markers protected static final String START_BINDING_MARKER = "${"; protected static final String END_BINDING_MARKER = "}"; /** * @param viewParser the default parser */ public void setViewParser(Parser viewParser) { this.viewParser = viewParser; } /** * @param nodeService the node service */ public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; } /** * @param searchService the service to perform path searches */ public void setSearchService(SearchService searchService) { this.searchService = searchService; } /** * @param contentService the content service */ public void setContentService(ContentService contentService) { this.contentService = contentService; } /** * @param dictionaryService the dictionary service */ public void setDictionaryService(DictionaryService dictionaryService) { this.dictionaryService = dictionaryService; } /** * @param namespaceService the namespace service */ public void setNamespaceService(NamespaceService namespaceService) { this.namespaceService = namespaceService; } /** * @param behaviourFilter policy behaviour filter */ public void setBehaviourFilter(BehaviourFilter behaviourFilter) { this.behaviourFilter = behaviourFilter; } /** * TODO: Remove this in favour of appropriate rule disabling * * @param ruleService rule service */ public void setRuleService(RuleService ruleService) { this.ruleService = ruleService; } /** * @param permissionService permissionService */ public void setPermissionService(PermissionService permissionService) { this.permissionService = permissionService; } /** * @param authorityService authorityService */ public void setAuthorityService(AuthorityService authorityService) { this.authorityService = authorityService; } /** * @param authenticationContext authenticationContext */ public void setAuthenticationContext(AuthenticationContext authenticationContext) { this.authenticationContext = authenticationContext; } /** * @param ownableService ownableService */ public void setOwnableService(OwnableService ownableService) { this.ownableService = ownableService; } /* (non-Javadoc) * @see org.alfresco.service.cmr.view.ImporterService#importView(java.io.InputStreamReader, org.alfresco.service.cmr.view.Location, java.util.Properties, org.alfresco.service.cmr.view.ImporterProgress) */ public void importView(Reader viewReader, Location location, ImporterBinding binding, ImporterProgress progress) { NodeRef nodeRef = getNodeRef(location, binding); parserImport(nodeRef, location.getChildAssocType(), viewReader, new DefaultStreamHandler(), binding, progress); } /* (non-Javadoc) * @see org.alfresco.service.cmr.view.ImporterService#importView(org.alfresco.service.cmr.view.ImportPackageHandler, org.alfresco.service.cmr.view.Location, org.alfresco.service.cmr.view.ImporterBinding, org.alfresco.service.cmr.view.ImporterProgress) */ public void importView(ImportPackageHandler importHandler, Location location, ImporterBinding binding, ImporterProgress progress) throws ImporterException { importHandler.startImport(); Reader dataFileReader = importHandler.getDataStream(); NodeRef nodeRef = getNodeRef(location, binding); parserImport(nodeRef, location.getChildAssocType(), dataFileReader, importHandler, binding, progress); importHandler.endImport(); } /** * Get Node Reference from Location * * @param location the location to extract node reference from * @param binding import configuration * @return node reference */ protected NodeRef getNodeRef(Location location, ImporterBinding binding) { ParameterCheck.mandatory("Location", location); // Establish node to import within NodeRef nodeRef = location.getNodeRef(); if (nodeRef == null) { // If a specific node has not been provided, default to the root nodeRef = nodeService.getRootNode(location.getStoreRef()); } // Resolve to path within node, if one specified String path = location.getPath(); if (path != null && path.length() > 0) { // Create a valid path and search path = bindPlaceHolder(path, binding); path = createValidPath(path); List<NodeRef> nodeRefs = searchService.selectNodes(nodeRef, path, null, namespaceService, false); if (nodeRefs.size() == 0) { throw new ImporterException("Path " + path + " within node " + nodeRef + " does not exist - the path must resolve to a valid location"); } if (nodeRefs.size() > 1) { throw new ImporterException("Path " + path + " within node " + nodeRef + " found too many locations - the path must resolve to one location"); } nodeRef = nodeRefs.get(0); } // TODO: Check Node actually exists return nodeRef; } /** * Bind the specified value to the passed configuration values if it is a place holder * * @param value the value to bind * @param binding the configuration properties to bind to * @return the bound value */ protected String bindPlaceHolder(String value, ImporterBinding binding) { if (binding != null) { int iStartBinding = value.indexOf(START_BINDING_MARKER); while (iStartBinding != -1) { int iEndBinding = value.indexOf(END_BINDING_MARKER, iStartBinding + START_BINDING_MARKER.length()); if (iEndBinding == -1) { throw new ImporterException( "Cannot find end marker " + END_BINDING_MARKER + " within value " + value); } String key = value.substring(iStartBinding + START_BINDING_MARKER.length(), iEndBinding); String keyValue = binding.getValue(key); if (keyValue == null) { logger.warn("No binding value for placeholder (will default to empty string): " + value); } value = StringUtils.replace(value, START_BINDING_MARKER + key + END_BINDING_MARKER, keyValue == null ? "" : keyValue); iStartBinding = value.indexOf(START_BINDING_MARKER); } } return value; } /** * Create a valid path * * @param path * @return */ protected String createValidPath(String path) { StringBuffer validPath = new StringBuffer(path.length()); String[] segments = StringUtils.delimitedListToStringArray(path, "/"); for (int i = 0; i < segments.length; i++) { if (segments[i] != null && segments[i].length() > 0) { String[] qnameComponents = QName.splitPrefixedQName(segments[i]); QName segmentQName = QName.createQName(qnameComponents[0], QName.createValidLocalName(qnameComponents[1]), namespaceService); validPath.append(segmentQName.toPrefixString()); } if (i < (segments.length - 1)) { validPath.append("/"); } } return validPath.toString(); } /** * Perform Import via Parser * * @param nodeRef node reference to import under * @param childAssocType the child association type to import under * @param inputStream the input stream to import from * @param streamHandler the content property import stream handler * @param binding import configuration * @param progress import progress */ public void parserImport(NodeRef nodeRef, QName childAssocType, Reader viewReader, ImportPackageHandler streamHandler, ImporterBinding binding, ImporterProgress progress) { ParameterCheck.mandatory("Node Reference", nodeRef); ParameterCheck.mandatory("View Reader", viewReader); ParameterCheck.mandatory("Stream Handler", streamHandler); Importer nodeImporter = new NodeImporter(nodeRef, childAssocType, binding, streamHandler, progress); try { nodeImporter.start(); viewParser.parse(viewReader, nodeImporter); nodeImporter.end(); } catch (RuntimeException e) { nodeImporter.error(e); throw e; } } /** * Perform import via Content Handler * * @param nodeRef node reference to import under * @param childAssocType the child association type to import under * @param handler the import content handler * @param binding import configuration * @param progress import progress * @return content handler to interact with */ public ContentHandler handlerImport(NodeRef nodeRef, QName childAssocType, ImportContentHandler handler, ImporterBinding binding, ImporterProgress progress) { ParameterCheck.mandatory("Node Reference", nodeRef); DefaultContentHandler defaultHandler = new DefaultContentHandler(handler); ImportPackageHandler streamHandler = new ContentHandlerStreamHandler(defaultHandler); Importer nodeImporter = new NodeImporter(nodeRef, childAssocType, binding, streamHandler, progress); defaultHandler.setImporter(nodeImporter); return defaultHandler; } /** * Encapsulate how a node is imported into the repository */ public interface NodeImporterStrategy { /** * Import a node * * @param node to import */ public NodeRef importNode(ImportNode node); } /** * Default Importer strategy * * @author David Caruana */ protected class NodeImporter implements Importer { protected NodeRef rootRef; protected QName rootAssocType; protected ImporterBinding binding; protected ImporterProgress progress; protected ImportPackageHandler streamHandler; protected NodeImporterStrategy importStrategy; protected UpdateExistingNodeImporterStrategy updateStrategy; protected QName[] excludedClasses; // Import tracking protected List<ImportedNodeRef> nodeRefs = new ArrayList<ImportedNodeRef>(); /** * Construct * * @param rootRef * @param rootAssocType * @param binding * @param progress */ protected NodeImporter(NodeRef rootRef, QName rootAssocType, ImporterBinding binding, ImportPackageHandler streamHandler, ImporterProgress progress) { this.rootRef = rootRef; this.rootAssocType = rootAssocType; this.binding = binding; this.progress = progress; this.streamHandler = streamHandler; this.importStrategy = createNodeImporterStrategy(binding == null ? null : binding.getUUIDBinding()); this.updateStrategy = new UpdateExistingNodeImporterStrategy(); // initialise list of content models to exclude from import if (binding == null || binding.getExcludedClasses() == null) { this.excludedClasses = new QName[] { ContentModel.ASPECT_REFERENCEABLE, ContentModel.ASPECT_VERSIONABLE }; } else { this.excludedClasses = binding.getExcludedClasses(); } } /** * Create Node Importer Strategy * * @param uuidBinding UUID Binding * @return Node Importer Strategy */ protected NodeImporterStrategy createNodeImporterStrategy(ImporterBinding.UUID_BINDING uuidBinding) { if (uuidBinding == null) { return new CreateNewNodeImporterStrategy(true); } else if (uuidBinding.equals(UUID_BINDING.CREATE_NEW)) { return new CreateNewNodeImporterStrategy(true); } else if (uuidBinding.equals(UUID_BINDING.CREATE_NEW_WITH_UUID)) { return new CreateNewNodeImporterStrategy(false); } else if (uuidBinding.equals(UUID_BINDING.REMOVE_EXISTING)) { return new RemoveExistingNodeImporterStrategy(); } else if (uuidBinding.equals(UUID_BINDING.REPLACE_EXISTING)) { return new ReplaceExistingNodeImporterStrategy(); } else if (uuidBinding.equals(UUID_BINDING.UPDATE_EXISTING)) { return new UpdateExistingNodeImporterStrategy(); } else if (uuidBinding.equals(UUID_BINDING.THROW_ON_COLLISION)) { return new ThrowOnCollisionNodeImporterStrategy(); } else { return new CreateNewNodeImporterStrategy(true); } } /* (non-Javadoc) * @see org.alfresco.repo.importer.Importer#getRootRef() */ public NodeRef getRootRef() { return rootRef; } /* (non-Javadoc) * @see org.alfresco.repo.importer.Importer#getRootAssocType() */ public QName getRootAssocType() { return rootAssocType; } /* (non-Javadoc) * @see org.alfresco.repo.importer.Importer#start() */ public void start() { reportStarted(); } /* (non-Javadoc) * @see org.alfresco.repo.importer.Importer#importMetaData(java.util.Map) */ public void importMetaData(Map<QName, String> properties) { // Determine if we're importing a complete repository String complexPath = properties .get(QName.createQName(NamespaceService.REPOSITORY_VIEW_1_0_URI, "exportOf")); for (String path : complexPath.split(",")) { if (path != null && path.equals("/")) { // Only allow complete repository import into root NodeRef storeRootRef = nodeService.getRootNode(rootRef.getStoreRef()); if (!storeRootRef.equals(rootRef)) { throw new ImporterException("A complete repository package cannot be imported here"); } } } } /* (non-Javadoc) * @see org.alfresco.repo.importer.Importer#importNode(org.alfresco.repo.importer.ImportNode) */ @SuppressWarnings("unchecked") public NodeRef importNode(ImportNode context) { // import node NodeRef nodeRef; if (context.isReference()) { nodeRef = linkNode(context); } else { nodeRef = importStrategy.importNode(context); } // apply aspects for (QName aspect : context.getNodeAspects()) { if (nodeService.hasAspect(nodeRef, aspect) == false) { nodeService.addAspect(nodeRef, aspect, null); // all properties previously added reportAspectAdded(nodeRef, aspect); } } // import content, if applicable for (Map.Entry<QName, Serializable> property : context.getProperties().entrySet()) { // filter out content properties (they're imported later) DataTypeDefinition valueDataType = context.getPropertyDataType(property.getKey()); if (valueDataType != null && valueDataType.getName().equals(DataTypeDefinition.CONTENT)) { // the property may be a single value or a collection - handle both Object objVal = property.getValue(); if (objVal instanceof String) { importContent(nodeRef, property.getKey(), (String) objVal); } else if (objVal instanceof Collection) { for (String value : (Collection<String>) objVal) { importContent(nodeRef, property.getKey(), value); } } } } return nodeRef; } /** * Link an existing Node * * @param context node to link in * @return node reference of child linked in */ protected NodeRef linkNode(ImportNode context) { ImportParent parentContext = context.getParentContext(); NodeRef parentRef = parentContext.getParentRef(); // determine the node reference to link to String uuid = context.getUUID(); if (uuid == null || uuid.length() == 0) { throw new ImporterException("Node reference does not specify a reference to follow."); } NodeRef referencedRef = new NodeRef(rootRef.getStoreRef(), uuid); // Note: do not link references that are defined in the root of the import if (!parentRef.equals(getRootRef())) { // determine child assoc type QName assocType = getAssocType(context); AssociationDefinition assocDef = dictionaryService.getAssociation(assocType); if (assocDef.isChild()) { // determine child name QName childQName = getChildName(context); if (childQName == null) { String name = (String) nodeService.getProperty(referencedRef, ContentModel.PROP_NAME); if (name == null || name.length() == 0) { throw new ImporterException("Cannot determine node reference child name"); } String localName = QName.createValidLocalName(name); childQName = QName.createQName(assocType.getNamespaceURI(), localName); } // create the secondary link nodeService.addChild(parentRef, referencedRef, assocType, childQName); reportNodeLinked(referencedRef, parentRef, assocType, childQName); } else { nodeService.createAssociation(parentRef, referencedRef, assocType); reportNodeLinked(parentRef, referencedRef, assocType, null); } } // second, perform any specified udpates to the node updateStrategy.importNode(context); return referencedRef; } /** * Import Node Content. * <p> * The content URL, if present, will be a local URL. This import copies the content * from the local URL to a server-assigned location. * * @param nodeRef containing node * @param propertyName the name of the content-type property * @param contentData the identifier of the content to import */ protected void importContent(NodeRef nodeRef, QName propertyName, String importContentData) { // bind import content data description importContentData = bindPlaceHolder(importContentData, binding); if (importContentData != null && importContentData.length() > 0) { DataTypeDefinition dataTypeDef = dictionaryService.getDataType(DataTypeDefinition.CONTENT); ContentData contentData = (ContentData) DefaultTypeConverter.INSTANCE.convert(dataTypeDef, importContentData); String contentUrl = contentData.getContentUrl(); if (contentUrl != null && contentUrl.length() > 0) { // import the content from the url InputStream contentStream = streamHandler.importStream(contentUrl); ContentWriter writer = contentService.getWriter(nodeRef, propertyName, true); writer.setEncoding(contentData.getEncoding()); writer.setMimetype(contentData.getMimetype()); writer.putContent(contentStream); reportContentCreated(nodeRef, contentUrl); } } } /* (non-Javadoc) * @see org.alfresco.repo.importer.Importer#childrenImported(org.alfresco.service.cmr.repository.NodeRef) */ public void childrenImported(NodeRef nodeRef) { behaviourFilter.enableBehaviours(nodeRef); ruleService.enableRules(nodeRef); } /* (non-Javadoc) * @see org.alfresco.repo.importer.Importer#resolvePath(java.lang.String) */ public NodeRef resolvePath(String path) { NodeRef referencedRef = null; if (path != null && path.length() > 0) { referencedRef = resolveImportedNodeRef(rootRef, path); } return referencedRef; } /* * (non-Javadoc) * @see org.alfresco.repo.importer.Importer#isExcludedClass(org.alfresco.service.namespace.QName) */ public boolean isExcludedClass(QName className) { for (QName excludedClass : excludedClasses) { if (excludedClass.equals(className)) { return true; } } return false; } /* (non-Javadoc) * @see org.alfresco.repo.importer.Importer#end() */ @SuppressWarnings("unchecked") public void end() { // Bind all node references to destination space for (ImportedNodeRef importedRef : nodeRefs) { Serializable refProperty = null; if (importedRef.value != null) { if (importedRef.value instanceof Collection) { Collection<String> unresolvedRefs = (Collection<String>) importedRef.value; List<NodeRef> resolvedRefs = new ArrayList<NodeRef>(unresolvedRefs.size()); for (String unresolvedRef : unresolvedRefs) { if (unresolvedRef != null) { NodeRef nodeRef = resolveImportedNodeRef(importedRef.context.getNodeRef(), unresolvedRef); // TODO: Provide a better mechanism for invalid references? e.g. report warning if (nodeRef != null) { resolvedRefs.add(nodeRef); } } } refProperty = (Serializable) resolvedRefs; } else { refProperty = resolveImportedNodeRef(importedRef.context.getNodeRef(), (String) importedRef.value); // TODO: Provide a better mechanism for invalid references? e.g. report warning } } // Set node reference on source node Set<QName> disabledBehaviours = getDisabledBehaviours(importedRef.context); try { for (QName disabledBehaviour : disabledBehaviours) { behaviourFilter.disableBehaviour(importedRef.context.getNodeRef(), disabledBehaviour); } nodeService.setProperty(importedRef.context.getNodeRef(), importedRef.property, refProperty); if (progress != null) { progress.propertySet(importedRef.context.getNodeRef(), importedRef.property, refProperty); } } finally { behaviourFilter.enableBehaviours(importedRef.context.getNodeRef()); } } reportCompleted(); } /* * (non-Javadoc) * @see org.alfresco.repo.importer.Importer#error(java.lang.Throwable) */ public void error(Throwable e) { behaviourFilter.enableAllBehaviours(); reportError(e); } /** * Get the child name to import node under * * @param context the node * @return the child name */ protected QName getChildName(ImportNode context) { QName assocType = getAssocType(context); QName childQName = null; // Determine child name String childName = context.getChildName(); if (childName != null) { childName = bindPlaceHolder(childName, binding); String[] qnameComponents = QName.splitPrefixedQName(childName); childQName = QName.createQName(qnameComponents[0], QName.createValidLocalName(qnameComponents[1]), namespaceService); } else { Map<QName, Serializable> typeProperties = context.getProperties(); String name = (String) typeProperties.get(ContentModel.PROP_NAME); if (name != null && name.length() > 0) { name = bindPlaceHolder(name, binding); String localName = QName.createValidLocalName(name); childQName = QName.createQName(assocType.getNamespaceURI(), localName); } } return childQName; } /** * Get appropriate child association type for node to import under * * @param context node to import * @return child association type name */ protected QName getAssocType(ImportNode context) { QName assocType = context.getParentContext().getAssocType(); if (assocType != null) { // return explicitly set association type return assocType; } // // Derive association type // // build type and aspect list for node List<QName> nodeTypes = new ArrayList<QName>(); nodeTypes.add(context.getTypeDefinition().getName()); for (QName aspect : context.getNodeAspects()) { nodeTypes.add(aspect); } // build target class types for parent Map<QName, QName> targetTypes = new HashMap<QName, QName>(); QName parentType = nodeService.getType(context.getParentContext().getParentRef()); ClassDefinition classDef = dictionaryService.getClass(parentType); Map<QName, ChildAssociationDefinition> childAssocDefs = classDef.getChildAssociations(); for (ChildAssociationDefinition childAssocDef : childAssocDefs.values()) { targetTypes.put(childAssocDef.getTargetClass().getName(), childAssocDef.getName()); } Set<QName> parentAspects = nodeService.getAspects(context.getParentContext().getParentRef()); for (QName parentAspect : parentAspects) { classDef = dictionaryService.getClass(parentAspect); childAssocDefs = classDef.getChildAssociations(); for (ChildAssociationDefinition childAssocDef : childAssocDefs.values()) { targetTypes.put(childAssocDef.getTargetClass().getName(), childAssocDef.getName()); } } // find target class that is closest to node type or aspects QName closestAssocType = null; int closestHit = 1; for (QName nodeType : nodeTypes) { for (QName targetType : targetTypes.keySet()) { QName testType = nodeType; int howClose = 1; while (testType != null) { howClose--; if (targetType.equals(testType) && howClose < closestHit) { closestAssocType = targetTypes.get(targetType); closestHit = howClose; break; } ClassDefinition testTypeDef = dictionaryService.getClass(testType); testType = (testTypeDef == null) ? null : testTypeDef.getParentName(); } } } return closestAssocType; } /** * For the given import node, return the behaviours to disable during import * * @param context import node * @return the disabled behaviours */ protected Set<QName> getDisabledBehaviours(ImportNode context) { Set<QName> classNames = new HashSet<QName>(); // disable the type TypeDefinition typeDef = context.getTypeDefinition(); classNames.add(typeDef.getName()); // disable the aspects imported on the node classNames.addAll(context.getNodeAspects()); // note: do not disable default aspects that are not imported on the node. // this means they'll be added on import return classNames; } /** * Bind properties * * @param properties * @return */ @SuppressWarnings("unchecked") protected Map<QName, Serializable> bindProperties(ImportNode context) { Map<QName, Serializable> properties = context.getProperties(); Map<QName, Serializable> boundProperties = new HashMap<QName, Serializable>(properties.size()); for (QName property : properties.keySet()) { // get property datatype DataTypeDefinition valueDataType = context.getPropertyDataType(property); // filter out content properties (they're imported later) if (valueDataType != null && valueDataType.getName().equals(DataTypeDefinition.CONTENT)) { continue; } // get property value Serializable value = properties.get(property); // bind property value to configuration and convert to appropriate type if (value instanceof Collection) { List<Serializable> boundCollection = new ArrayList<Serializable>(); for (Serializable collectionValue : (Collection<Serializable>) value) { Serializable objValue = bindValue(context, property, valueDataType, collectionValue); boundCollection.add(objValue); } value = (Serializable) boundCollection; } else { value = bindValue(context, property, valueDataType, value); } // choose to provide property on node creation or at end of import for lazy binding if (valueDataType != null && (valueDataType.getName().equals(DataTypeDefinition.NODE_REF) || valueDataType.getName().equals(DataTypeDefinition.CATEGORY))) { // record node reference for end-of-import binding ImportedNodeRef importedRef = new ImportedNodeRef(context, property, value); nodeRefs.add(importedRef); } else { // property ready to be set on Node creation / update boundProperties.put(property, value); } } return boundProperties; } /** * Bind permissions - binds authorities * * @param properties * @return */ protected List<AccessPermission> bindPermissions(List<AccessPermission> permissions) { List<AccessPermission> boundPermissions = new ArrayList<AccessPermission>(permissions.size()); for (AccessPermission permission : permissions) { AccessPermission ace = new NodeContext.ACE(permission.getAccessStatus(), bindPlaceHolder(permission.getAuthority(), binding), permission.getPermission()); boundPermissions.add(ace); } return boundPermissions; } /** * Bind property value * * @param valueType value type * @param value string form of value * @return the bound value */ protected Serializable bindValue(ImportNode context, QName property, DataTypeDefinition valueType, Serializable value) { Serializable objValue = null; if (value != null && valueType != null) { if (value instanceof String) { value = bindPlaceHolder(value.toString(), binding); } if ((valueType.getName().equals(DataTypeDefinition.NODE_REF) || valueType.getName().equals(DataTypeDefinition.CATEGORY))) { objValue = value; } else { objValue = (Serializable) DefaultTypeConverter.INSTANCE.convert(valueType, value); } } return objValue; } /** * Resolve imported reference relative to specified node * * @param sourceNodeRef context to resolve within * @param importedRef reference to resolve * @return */ protected NodeRef resolveImportedNodeRef(NodeRef sourceNodeRef, String importedRef) { // Resolve path to node reference NodeRef nodeRef = null; importedRef = bindPlaceHolder(importedRef, binding); if (importedRef.equals("/")) { nodeRef = sourceNodeRef; } else if (importedRef.startsWith("/")) { List<NodeRef> nodeRefs = searchService.selectNodes(sourceNodeRef, importedRef, null, namespaceService, false); if (nodeRefs.size() > 0) { nodeRef = nodeRefs.get(0); } } else { // determine if node reference if (NodeRef.isNodeRef(importedRef)) { nodeRef = new NodeRef(importedRef); } else { // resolve relative path try { List<NodeRef> nodeRefs = searchService.selectNodes(sourceNodeRef, importedRef, null, namespaceService, false); if (nodeRefs.size() > 0) { nodeRef = nodeRefs.get(0); } } catch (XPathException e) { nodeRef = new NodeRef(importedRef); } catch (AlfrescoRuntimeException e1) { // Note: Invalid reference format - try path search instead } } } return nodeRef; } /** * Helper to report start of import */ protected void reportStarted() { if (progress != null) { progress.started(); } } /** * Helper to report end of import */ protected void reportCompleted() { if (progress != null) { progress.completed(); } } /** * Helper to report error * * @param e */ protected void reportError(Throwable e) { if (progress != null) { progress.error(e); } } /** * Helper to report node created progress * * @param progress * @param childAssocRef */ protected void reportNodeCreated(ChildAssociationRef childAssocRef) { if (progress != null) { progress.nodeCreated(childAssocRef.getChildRef(), childAssocRef.getParentRef(), childAssocRef.getTypeQName(), childAssocRef.getQName()); } } /** * Helper to report node linked progress * * @param progress * @param childAssocRef */ protected void reportNodeLinked(NodeRef childRef, NodeRef parentRef, QName assocType, QName childName) { if (progress != null) { progress.nodeLinked(childRef, parentRef, assocType, childName); } } /** * Helper to report content created progress * * @param progress * @param nodeRef * @param sourceUrl */ protected void reportContentCreated(NodeRef nodeRef, String sourceUrl) { if (progress != null) { progress.contentCreated(nodeRef, sourceUrl); } } /** * Helper to report aspect added progress * * @param progress * @param nodeRef * @param aspect */ protected void reportAspectAdded(NodeRef nodeRef, QName aspect) { if (progress != null) { progress.aspectAdded(nodeRef, aspect); } } /** * Helper to report property set progress * * @param progress * @param nodeRef * @param properties */ protected void reportPropertySet(NodeRef nodeRef, Map<QName, Serializable> properties) { if (progress != null && properties != null) { for (QName property : properties.keySet()) { progress.propertySet(nodeRef, property, properties.get(property)); } } } /** * Helper to report permission set progress * * @param nodeRef * @param permissions */ protected void reportPermissionSet(NodeRef nodeRef, List<AccessPermission> permissions) { if (progress != null && permissions != null) { for (AccessPermission permission : permissions) { progress.permissionSet(nodeRef, permission); } } } /** * Import strategy where imported nodes are always created regardless of whether a * node of the same UUID already exists in the repository */ protected class CreateNewNodeImporterStrategy implements NodeImporterStrategy { // force allocation of new UUID, even if one already specified protected boolean assignNewUUID; /** * Construct * * @param newUUID force allocation of new UUID */ public CreateNewNodeImporterStrategy(boolean assignNewUUID) { this.assignNewUUID = assignNewUUID; } /* * (non-Javadoc) * @see org.alfresco.repo.importer.ImporterComponent.NodeImporterStrategy#importNode(org.alfresco.repo.importer.ImportNode) */ public NodeRef importNode(ImportNode node) { TypeDefinition nodeType = node.getTypeDefinition(); NodeRef parentRef = node.getParentContext().getParentRef(); QName assocType = getAssocType(node); QName childQName = getChildName(node); if (childQName == null) { throw new ImporterException( "Cannot determine child name of node (type: " + nodeType.getName() + ")"); } // Create initial node (but, first disable behaviour for the node to be created) Set<QName> disabledBehaviours = getDisabledBehaviours(node); List<QName> alreadyDisabledBehaviours = new ArrayList<QName>(); for (QName disabledBehaviour : disabledBehaviours) { /*boolean alreadyDisabled =*/ behaviourFilter.disableBehaviour(disabledBehaviour); /*if (alreadyDisabled) {*/ alreadyDisabledBehaviours.add(disabledBehaviour); /*}*/ } disabledBehaviours.removeAll(alreadyDisabledBehaviours); // Build initial map of properties Map<QName, Serializable> initialProperties = bindProperties(node); // Assign UUID if already specified on imported node if (!assignNewUUID && node.getUUID() != null) { initialProperties.put(ContentModel.PROP_NODE_UUID, node.getUUID()); } // Create Node ChildAssociationRef assocRef = nodeService.createNode(parentRef, assocType, childQName, nodeType.getName(), initialProperties); NodeRef nodeRef = assocRef.getChildRef(); // Note: non-admin authorities take ownership of new nodes if (!(authenticationContext.isCurrentUserTheSystemUser() || authorityService.hasAdminAuthority())) { ownableService.takeOwnership(nodeRef); } // apply permissions List<AccessPermission> permissions = null; AccessStatus writePermission = permissionService.hasPermission(nodeRef, PermissionService.CHANGE_PERMISSIONS); if (authenticationContext.isCurrentUserTheSystemUser() || writePermission.equals(AccessStatus.ALLOWED)) { permissions = bindPermissions(node.getAccessControlEntries()); for (AccessPermission permission : permissions) { permissionService.setPermission(nodeRef, permission.getAuthority(), permission.getPermission(), permission.getAccessStatus().equals(AccessStatus.ALLOWED)); } // note: apply inheritance after setting permissions as this may affect whether you can apply permissions boolean inheritPermissions = node.getInheritPermissions(); if (!inheritPermissions) { permissionService.setInheritParentPermissions(nodeRef, false); } } // Disable behaviour for the node until the complete node (and its children have been imported) for (QName disabledBehaviour : disabledBehaviours) { behaviourFilter.enableBehaviour(disabledBehaviour); } for (QName disabledBehaviour : disabledBehaviours) { behaviourFilter.disableBehaviour(nodeRef, disabledBehaviour); } // TODO: Replace this with appropriate rule/action import handling ruleService.disableRules(nodeRef); // Report creation reportNodeCreated(assocRef); reportPropertySet(nodeRef, initialProperties); reportPermissionSet(nodeRef, permissions); // return newly created node reference return nodeRef; } } /** * Importer strategy where an existing node (one with the same UUID) as a node being * imported is first removed. The imported node is placed in the location specified * at import time. */ protected class RemoveExistingNodeImporterStrategy implements NodeImporterStrategy { protected NodeImporterStrategy createNewStrategy = new CreateNewNodeImporterStrategy(false); /* * (non-Javadoc) * @see org.alfresco.repo.importer.ImporterComponent.NodeImporterStrategy#importNode(org.alfresco.repo.importer.ImportNode) */ public NodeRef importNode(ImportNode node) { // remove existing node, if node to import has a UUID and an existing node of the same // uuid already exists String uuid = node.getUUID(); if (uuid != null && uuid.length() > 0) { NodeRef existingNodeRef = new NodeRef(rootRef.getStoreRef(), uuid); if (nodeService.exists(existingNodeRef)) { // remove primary parent link forcing deletion ChildAssociationRef childAssocRef = nodeService.getPrimaryParent(existingNodeRef); // TODO: Check for root node nodeService.removeChild(childAssocRef.getParentRef(), childAssocRef.getChildRef()); } } // import as if a new node into current import parent location return createNewStrategy.importNode(node); } } /** * Importer strategy where an existing node (one with the same UUID) as a node being * imported is first removed. The imported node is placed under the parent of the removed * node. */ protected class ReplaceExistingNodeImporterStrategy implements NodeImporterStrategy { protected NodeImporterStrategy createNewStrategy = new CreateNewNodeImporterStrategy(false); /* * (non-Javadoc) * @see org.alfresco.repo.importer.ImporterComponent.NodeImporterStrategy#importNode(org.alfresco.repo.importer.ImportNode) */ public NodeRef importNode(ImportNode node) { // replace existing node, if node to import has a UUID and an existing node of the same // uuid already exists String uuid = node.getUUID(); if (uuid != null && uuid.length() > 0) { NodeRef existingNodeRef = new NodeRef(rootRef.getStoreRef(), uuid); if (nodeService.exists(existingNodeRef)) { // remove primary parent link forcing deletion ChildAssociationRef childAssocRef = nodeService.getPrimaryParent(existingNodeRef); nodeService.removeChild(childAssocRef.getParentRef(), childAssocRef.getChildRef()); // update the parent context of the node being imported to the parent of the node just deleted node.getParentContext().setParentRef(childAssocRef.getParentRef()); node.getParentContext().setAssocType(childAssocRef.getTypeQName()); } } // import as if a new node return createNewStrategy.importNode(node); } } /** * Import strategy where an error is thrown when importing a node that has the same UUID * of an existing node in the repository. */ protected class ThrowOnCollisionNodeImporterStrategy implements NodeImporterStrategy { protected NodeImporterStrategy createNewStrategy = new CreateNewNodeImporterStrategy(false); /* * (non-Javadoc) * @see org.alfresco.repo.importer.ImporterComponent.NodeImporterStrategy#importNode(org.alfresco.repo.importer.ImportNode) */ public NodeRef importNode(ImportNode node) { // if node to import has a UUID and an existing node of the same uuid already exists // then throw an error String uuid = node.getUUID(); if (uuid != null && uuid.length() > 0) { NodeRef existingNodeRef = new NodeRef(rootRef.getStoreRef(), uuid); if (nodeService.exists(existingNodeRef)) { throw new InvalidNodeRefException("Node " + existingNodeRef + " already exists", existingNodeRef); } } // import as if a new node return createNewStrategy.importNode(node); } } /** * Import strategy where imported nodes are updated if a node with the same UUID * already exists in the repository. * * Note: this will only allow incremental update of an existing node - it does not * delete properties or associations. */ protected class UpdateExistingNodeImporterStrategy implements NodeImporterStrategy { protected NodeImporterStrategy createNewStrategy = new CreateNewNodeImporterStrategy(false); /* * (non-Javadoc) * @see org.alfresco.repo.importer.ImporterComponent.NodeImporterStrategy#importNode(org.alfresco.repo.importer.ImportNode) */ public NodeRef importNode(ImportNode node) { // replace existing node, if node to import has a UUID and an existing node of the same // uuid already exists String uuid = node.getUUID(); if (uuid != null && uuid.length() > 0) { NodeRef existingNodeRef = new NodeRef(rootRef.getStoreRef(), uuid); if (nodeService.exists(existingNodeRef)) { // do the update Map<QName, Serializable> existingProperties = nodeService.getProperties(existingNodeRef); Map<QName, Serializable> updateProperties = bindProperties(node); if (updateProperties != null && updateProperties.size() > 0) { existingProperties.putAll(updateProperties); nodeService.setProperties(existingNodeRef, existingProperties); } // Apply permissions List<AccessPermission> permissions = null; AccessStatus writePermission = permissionService.hasPermission(existingNodeRef, PermissionService.CHANGE_PERMISSIONS); if (authenticationContext.isCurrentUserTheSystemUser() || writePermission.equals(AccessStatus.ALLOWED)) { boolean inheritPermissions = node.getInheritPermissions(); if (!inheritPermissions) { permissionService.setInheritParentPermissions(existingNodeRef, false); } permissions = bindPermissions(node.getAccessControlEntries()); for (AccessPermission permission : permissions) { permissionService.setPermission(existingNodeRef, permission.getAuthority(), permission.getPermission(), permission.getAccessStatus().equals(AccessStatus.ALLOWED)); } } // report update reportPropertySet(existingNodeRef, updateProperties); reportPermissionSet(existingNodeRef, permissions); return existingNodeRef; } } // import as if a new node return createNewStrategy.importNode(node); } } } /** * Imported Node Reference * * @author David Caruana */ protected static class ImportedNodeRef { /** * Construct * * @param context * @param property * @param value */ protected ImportedNodeRef(ImportNode context, QName property, Serializable value) { this.context = context; this.property = property; this.value = value; } protected ImportNode context; protected QName property; protected Serializable value; } /** * Default Import Stream Handler * * @author David Caruana */ protected static class DefaultStreamHandler implements ImportPackageHandler { /* (non-Javadoc) * @see org.alfresco.service.cmr.view.ImportPackageHandler#startImport() */ public void startImport() { } /* (non-Javadoc) * @see org.alfresco.service.cmr.view.ImportStreamHandler#importStream(java.lang.String) */ public InputStream importStream(String content) { ResourceLoader loader = new DefaultResourceLoader(); Resource resource = loader.getResource(content); if (resource.exists() == false) { throw new ImporterException("Content URL " + content + " does not exist."); } try { return resource.getInputStream(); } catch (IOException e) { throw new ImporterException("Failed to retrieve input stream for content URL " + content); } } /* (non-Javadoc) * @see org.alfresco.service.cmr.view.ImportPackageHandler#getDataStream() */ public Reader getDataStream() { return null; } /* (non-Javadoc) * @see org.alfresco.service.cmr.view.ImportPackageHandler#endImport() */ public void endImport() { } } /** * Default Import Stream Handler * * @author David Caruana */ protected static class ContentHandlerStreamHandler implements ImportPackageHandler { protected ImportContentHandler handler; /** * Construct * * @param handler */ protected ContentHandlerStreamHandler(ImportContentHandler handler) { this.handler = handler; } /* (non-Javadoc) * @see org.alfresco.service.cmr.view.ImportPackageHandler#startImport() */ public void startImport() { } /* (non-Javadoc) * @see org.alfresco.service.cmr.view.ImportStreamHandler#importStream(java.lang.String) */ public InputStream importStream(String content) { return handler.importStream(content); } /* (non-Javadoc) * @see org.alfresco.service.cmr.view.ImportPackageHandler#getDataStream() */ public Reader getDataStream() { return null; } /* (non-Javadoc) * @see org.alfresco.service.cmr.view.ImportPackageHandler#endImport() */ public void endImport() { } } }