diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index 6f2257b476..9ff8604ebe 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -418,8 +418,11 @@ - - + + + + + alfresco/model/dictionaryModel.xml diff --git a/source/java/org/alfresco/repo/exporter/ChainedExporter.java b/source/java/org/alfresco/repo/exporter/ChainedExporter.java index 778eee786c..93fb58f676 100644 --- a/source/java/org/alfresco/repo/exporter/ChainedExporter.java +++ b/source/java/org/alfresco/repo/exporter/ChainedExporter.java @@ -310,6 +310,28 @@ import org.alfresco.service.namespace.QName; exporter.endAssocs(nodeRef); } } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#startReference(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void startReference(NodeRef nodeRef, QName childName) + { + for (Exporter exporter : exporters) + { + exporter.startReference(nodeRef, childName); + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#endReference(org.alfresco.service.cmr.repository.NodeRef) + */ + public void endReference(NodeRef nodeRef) + { + for (Exporter exporter : exporters) + { + exporter.endReference(nodeRef); + } + } /* (non-Javadoc) * @see org.alfresco.service.cmr.view.Exporter#warning(java.lang.String) diff --git a/source/java/org/alfresco/repo/exporter/ExporterComponent.java b/source/java/org/alfresco/repo/exporter/ExporterComponent.java index c99a0a88f8..1f41bf0932 100644 --- a/source/java/org/alfresco/repo/exporter/ExporterComponent.java +++ b/source/java/org/alfresco/repo/exporter/ExporterComponent.java @@ -23,25 +23,27 @@ import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.util.Collection; import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; -import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.repository.AssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.Path; import org.alfresco.service.cmr.repository.datatype.TypeConversionException; 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.AuthenticationService; import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.service.cmr.security.AccessStatus; import org.alfresco.service.cmr.view.ExportPackageHandler; import org.alfresco.service.cmr.view.Exporter; import org.alfresco.service.cmr.view.ExporterContext; @@ -53,12 +55,12 @@ import org.alfresco.service.cmr.view.Location; import org.alfresco.service.descriptor.DescriptorService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.util.ParameterCheck; import org.dom4j.io.OutputFormat; import org.dom4j.io.XMLWriter; - /** * Default implementation of the Exporter Service. * @@ -229,23 +231,32 @@ public class ExporterComponent */ private class DefaultCrawler implements ExporterCrawler { - // Flush threshold - private int flushThreshold = 500; - private int flushCount = 0; + private ExporterContext context; + private Map nodesWithSecondaryLinks = new HashMap(); + private Map nodesWithAssociations = new HashMap(); + /* (non-Javadoc) * @see org.alfresco.service.cmr.view.ExporterCrawler#export(org.alfresco.service.cmr.view.Exporter) */ public void export(ExporterCrawlerParameters parameters, Exporter exporter) { - ExporterContext context = new ExporterContextImpl(parameters); + // Initialise Crawler + nodesWithSecondaryLinks.clear(); + nodesWithAssociations.clear(); + context = new ExporterContextImpl(parameters); exporter.start(context); + // + // Export Nodes + // + // determine if root repository node NodeRef nodeRef = context.getExportOf(); boolean rootNode = nodeService.getRootNode(nodeRef.getStoreRef()).equals(nodeRef); if (parameters.isCrawlSelf() && !rootNode) { + // export root node of specified export location walkStartNamespaces(parameters, exporter); walkNode(nodeRef, parameters, exporter); walkEndNamespaces(parameters, exporter); @@ -261,6 +272,28 @@ public class ExporterComponent walkEndNamespaces(parameters, exporter); } } + + // + // Export Secondary Links between Nodes + // + + for (NodeRef nodeWithAssociations : nodesWithSecondaryLinks.keySet()) + { + walkStartNamespaces(parameters, exporter); + walkNodeSecondaryLinks(nodeWithAssociations, parameters, exporter); + walkEndNamespaces(parameters, exporter); + } + + // + // Export Associations between Nodes + // + + for (NodeRef nodeWithAssociations : nodesWithAssociations.keySet()) + { + walkStartNamespaces(parameters, exporter); + walkNodeAssociations(nodeWithAssociations, parameters, exporter); + walkEndNamespaces(parameters, exporter); + } exporter.end(); } @@ -310,14 +343,6 @@ public class ExporterComponent return; } - // Do we need to flush? - flushCount++; - if (flushCount > flushThreshold) - { - AlfrescoTransactionSupport.flush(); - flushCount = 0; - } - exporter.startNode(nodeRef); // Export node aspects @@ -451,6 +476,11 @@ public class ExporterComponent { continue; } + if (childAssoc.isPrimary() == false) + { + nodesWithSecondaryLinks.put(nodeRef, nodeRef); + continue; + } if (i == 0 || childAssocs.get(i - 1).getTypeQName().equals(childAssocType) == false) { exporter.startAssoc(nodeRef, childAssocType); @@ -467,12 +497,114 @@ public class ExporterComponent exporter.endAssocs(nodeRef); } - // TODO: Export node associations - + // Export node associations + if (parameters.isCrawlAssociations()) + { + List associations = nodeService.getTargetAssocs(nodeRef, RegexQNamePattern.MATCH_ALL); + if (associations.size() > 0) + { + nodesWithAssociations.put(nodeRef, nodeRef); + } + } + // Signal end of node exporter.endNode(nodeRef); } + /** + * Export Secondary Links + * + * @param nodeRef + * @param parameters + * @param exporter + */ + private void walkNodeSecondaryLinks(NodeRef nodeRef, ExporterCrawlerParameters parameters, Exporter exporter) + { + exporter.startReference(nodeRef, null); + exporter.startAssocs(nodeRef); + List childAssocs = nodeService.getChildAssocs(nodeRef); + for (int i = 0; i < childAssocs.size(); i++) + { + ChildAssociationRef childAssoc = childAssocs.get(i); + + // determine if child association should be exported + QName childAssocType = childAssoc.getTypeQName(); + if (isExcludedURI(parameters.getExcludeNamespaceURIs(), childAssocType.getNamespaceURI())) + { + continue; + } + if (childAssoc.isPrimary()) + { + continue; + } + if (!isWithinExport(childAssoc.getChildRef(), parameters)) + { + continue; + } + + // export the association + if (i == 0 || childAssocs.get(i - 1).getTypeQName().equals(childAssocType) == false) + { + exporter.startAssoc(nodeRef, childAssocType); + } + QName childName = childAssoc.getQName(); + if (!isExcludedURI(parameters.getExcludeNamespaceURIs(), childName.getNamespaceURI())) + { + exporter.startReference(childAssoc.getChildRef(), childName); + exporter.endReference(childAssoc.getChildRef()); + } + if (i == childAssocs.size() - 1 || childAssocs.get(i + 1).getTypeQName().equals(childAssocType) == false) + { + exporter.endAssoc(nodeRef, childAssocType); + } + } + exporter.endAssocs(nodeRef); + exporter.endReference(nodeRef); + } + + /** + * Export Node Associations + * + * @param nodeRef + * @param parameters + * @param exporter + */ + private void walkNodeAssociations(NodeRef nodeRef, ExporterCrawlerParameters parameters, Exporter exporter) + { + exporter.startReference(nodeRef, null); + exporter.startAssocs(nodeRef); + List assocs = nodeService.getTargetAssocs(nodeRef, RegexQNamePattern.MATCH_ALL); + for (int i = 0; i < assocs.size(); i++) + { + AssociationRef assoc = assocs.get(i); + + // determine if association should be exported + QName assocType = assoc.getTypeQName(); + if (isExcludedURI(parameters.getExcludeNamespaceURIs(), assocType.getNamespaceURI())) + { + continue; + } + if (!isWithinExport(assoc.getTargetRef(), parameters)) + { + continue; + } + + // export the association + if (i == 0 || assocs.get(i - 1).getTypeQName().equals(assocType) == false) + { + exporter.startAssoc(nodeRef, assocType); + } + exporter.startReference(assoc.getTargetRef(), null); + exporter.endReference(assoc.getTargetRef()); + if (i == assocs.size() - 1 || assocs.get(i + 1).getTypeQName().equals(assocType) == false) + { + exporter.endAssoc(nodeRef, assocType); + } + } + exporter.endAssocs(nodeRef); + exporter.endReference(nodeRef); + } + /** * Is the specified URI an excluded URI? * @@ -491,6 +623,40 @@ public class ExporterComponent return false; } + /** + * Determine if specified Node Reference is within the set of nodes to be exported + * + * @param nodeRef node reference to check + * @return true => node reference is within export set + */ + private boolean isWithinExport(NodeRef nodeRef, ExporterCrawlerParameters parameters) + { + boolean isWithin = false; + + // Current strategy is to determine if node is a child of the root exported node + NodeRef exportRoot = context.getExportOf(); + if (nodeRef.equals(exportRoot) && parameters.isCrawlSelf() == true) + { + // node to export is the root export node (and root is to be exported) + isWithin = true; + } + else + { + // locate export root in primary parent path of node + Path nodePath = nodeService.getPath(nodeRef); + for (int i = nodePath.size() -1; i >= 0; i--) + { + Path.ChildAssocElement pathElement = (Path.ChildAssocElement)nodePath.get(i); + if (pathElement.getRef().getChildRef().equals(exportRoot)) + { + isWithin = true; + break; + } + } + } + + return isWithin; + } } @@ -500,6 +666,7 @@ public class ExporterComponent private class ExporterContextImpl implements ExporterContext { private NodeRef exportOf; + private NodeRef parent; private String exportedBy; private Date exportedDate; private String exporterVersion; @@ -521,6 +688,9 @@ public class ExporterComponent // get export of exportOf = getNodeRef(parameters.getExportFrom()); + // get export parent + parent = getParent(exportOf, parameters.isCrawlSelf()); + // get exporter version exporterVersion = descriptorService.getServerDescriptor().getVersion(); } @@ -557,6 +727,15 @@ public class ExporterComponent return exportOf; } + /* + * (non-Javadoc) + * @see org.alfresco.service.cmr.view.ExporterContext#getExportParent() + */ + public NodeRef getExportParent() + { + return parent; + } + /** * Get the Node Ref from the specified Location * @@ -567,7 +746,7 @@ public class ExporterComponent { ParameterCheck.mandatory("Location", location); - // Establish node to import within + // Establish node to export from NodeRef nodeRef = (location == null) ? null : location.getNodeRef(); if (nodeRef == null) { @@ -596,7 +775,39 @@ public class ExporterComponent return nodeRef; } - + + /** + * Gets the parent node of the items to be exported + * + * @param exportOf + * @param exportSelf + * @return + */ + private NodeRef getParent(NodeRef exportOf, boolean exportSelf) + { + NodeRef parent = null; + + if (exportSelf) + { + NodeRef rootNode = nodeService.getRootNode(exportOf.getStoreRef()); + if (rootNode.equals(exportOf)) + { + parent = exportOf; + } + else + { + ChildAssociationRef parentRef = nodeService.getPrimaryParent(exportOf); + parent = parentRef.getParentRef(); + } + } + else + { + parent = exportOf; + } + + return parent; + } + } } diff --git a/source/java/org/alfresco/repo/exporter/ExporterComponentTest.java b/source/java/org/alfresco/repo/exporter/ExporterComponentTest.java index f9b5c2ce79..32172f397f 100644 --- a/source/java/org/alfresco/repo/exporter/ExporterComponentTest.java +++ b/source/java/org/alfresco/repo/exporter/ExporterComponentTest.java @@ -231,6 +231,16 @@ public class ExporterComponentTest extends BaseSpringTest // System.out.println("TestProgress: endACL: " + nodeRef); } + public void startReference(NodeRef nodeRef, QName childName) + { +// System.out.println("TestProgress: startReference: " + nodeRef); + } + + public void endReference(NodeRef nodeRef) + { +// System.out.println("TestProgress: endReference: " + nodeRef); + } + } } diff --git a/source/java/org/alfresco/repo/exporter/URLExporter.java b/source/java/org/alfresco/repo/exporter/URLExporter.java index 649ca2f530..360e407829 100644 --- a/source/java/org/alfresco/repo/exporter/URLExporter.java +++ b/source/java/org/alfresco/repo/exporter/URLExporter.java @@ -243,7 +243,24 @@ import org.alfresco.util.ParameterCheck; { exporter.endAssocs(nodeRef); } - + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#startReference(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void startReference(NodeRef nodeRef, QName childName) + { + exporter.startReference(nodeRef, childName); + } + + /* + * (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#endReference(org.alfresco.service.cmr.repository.NodeRef) + */ + public void endReference(NodeRef nodeRef) + { + exporter.endReference(nodeRef); + } + /* (non-Javadoc) * @see org.alfresco.service.cmr.view.Exporter#warning(java.lang.String) */ diff --git a/source/java/org/alfresco/repo/exporter/ViewXMLExporter.java b/source/java/org/alfresco/repo/exporter/ViewXMLExporter.java index bf079e238e..4903135218 100644 --- a/source/java/org/alfresco/repo/exporter/ViewXMLExporter.java +++ b/source/java/org/alfresco/repo/exporter/ViewXMLExporter.java @@ -69,6 +69,8 @@ import org.xml.sax.helpers.AttributesImpl; private static final String AUTHORITY_LOCALNAME = "authority"; private static final String PERMISSION_LOCALNAME = "permission"; private static final String INHERITPERMISSIONS_LOCALNAME = "inherit"; + private static final String REFERENCE_LOCALNAME = "reference"; + private static final String PATHREF_LOCALNAME = "pathref"; private static QName VIEW_QNAME; private static QName VALUES_QNAME; private static QName VALUE_QNAME; @@ -89,6 +91,8 @@ import org.xml.sax.helpers.AttributesImpl; private static QName AUTHORITY_QNAME; private static QName PERMISSION_QNAME; private static QName INHERITPERMISSIONS_QNAME; + private static QName REFERENCE_QNAME; + private static QName PATHREF_QNAME; private static final AttributesImpl EMPTY_ATTRIBUTES = new AttributesImpl(); // Service dependencies @@ -99,7 +103,7 @@ import org.xml.sax.helpers.AttributesImpl; // View context private ContentHandler contentHandler; - private Path exportNodePath; + private ExporterContext context; /** @@ -138,6 +142,8 @@ import org.xml.sax.helpers.AttributesImpl; AUTHORITY_QNAME = QName.createQName(NamespaceService.REPOSITORY_VIEW_PREFIX, AUTHORITY_LOCALNAME, namespaceService); PERMISSION_QNAME = QName.createQName(NamespaceService.REPOSITORY_VIEW_PREFIX, PERMISSION_LOCALNAME, namespaceService); INHERITPERMISSIONS_QNAME = QName.createQName(NamespaceService.REPOSITORY_VIEW_PREFIX, INHERITPERMISSIONS_LOCALNAME, namespaceService); + REFERENCE_QNAME = QName.createQName(NamespaceService.REPOSITORY_VIEW_PREFIX, REFERENCE_LOCALNAME, namespaceService); + PATHREF_QNAME = QName.createQName(NamespaceService.REPOSITORY_VIEW_PREFIX, PATHREF_LOCALNAME, namespaceService); } @@ -148,7 +154,7 @@ import org.xml.sax.helpers.AttributesImpl; { try { - exportNodePath = nodeService.getPath(context.getExportOf()); + this.context = context; contentHandler.startDocument(); contentHandler.startPrefixMapping(NamespaceService.REPOSITORY_VIEW_PREFIX, NamespaceService.REPOSITORY_VIEW_1_0_URI); contentHandler.startElement(NamespaceService.REPOSITORY_VIEW_PREFIX, VIEW_LOCALNAME, VIEW_QNAME.toPrefixString(), EMPTY_ATTRIBUTES); @@ -176,7 +182,7 @@ import org.xml.sax.helpers.AttributesImpl; // export of contentHandler.startElement(NamespaceService.REPOSITORY_VIEW_PREFIX, EXPORTOF_LOCALNAME, EXPORTOF_QNAME.toPrefixString(), EMPTY_ATTRIBUTES); - String path = exportNodePath.toPrefixString(namespaceService); + String path = nodeService.getPath(context.getExportOf()).toPrefixString(namespaceService); contentHandler.characters(path.toCharArray(), 0, path.length()); contentHandler.endElement(NamespaceService.REPOSITORY_VIEW_PREFIX, EXPORTOF_LOCALNAME, EXPORTOF_QNAME.toPrefixString()); @@ -474,7 +480,7 @@ import org.xml.sax.helpers.AttributesImpl; // convert node references to paths if (value instanceof NodeRef) { - Path nodeRefPath = createRelativePath(nodeRef, (NodeRef)value); + Path nodeRefPath = createRelativePath(context.getExportOf(), nodeRef, (NodeRef)value); value = (nodeRefPath == null) ? null : nodeRefPath.toPrefixString(namespaceService); } @@ -553,7 +559,7 @@ import org.xml.sax.helpers.AttributesImpl; // convert node references to paths if (value instanceof NodeRef) { - value = createRelativePath(nodeRef, (NodeRef)value).toPrefixString(namespaceService); + value = createRelativePath(context.getExportOf(), nodeRef, (NodeRef)value).toPrefixString(namespaceService); } // output value @@ -643,6 +649,44 @@ import org.xml.sax.helpers.AttributesImpl; throw new ExporterException("Failed to process end associations", e); } } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#startReference(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void startReference(NodeRef nodeRef, QName childName) + { + try + { + Path path = createRelativePath(context.getExportParent(), context.getExportParent(), nodeRef); + AttributesImpl attrs = new AttributesImpl(); + attrs.addAttribute(NamespaceService.REPOSITORY_VIEW_1_0_URI, PATHREF_LOCALNAME, PATHREF_QNAME.toPrefixString(), null, path.toPrefixString(namespaceService)); + if (childName != null) + { + attrs.addAttribute(NamespaceService.REPOSITORY_VIEW_1_0_URI, CHILDNAME_LOCALNAME, CHILDNAME_QNAME.toPrefixString(), null, childName.toPrefixString(namespaceService)); + } + contentHandler.startElement(REFERENCE_QNAME.getNamespaceURI(), REFERENCE_LOCALNAME, toPrefixString(REFERENCE_QNAME), attrs); + } + catch (SAXException e) + { + throw new ExporterException("Failed to process start reference", e); + } + } + + /* + * (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#endReference(org.alfresco.service.cmr.repository.NodeRef) + */ + public void endReference(NodeRef nodeRef) + { + try + { + contentHandler.endElement(REFERENCE_QNAME.getNamespaceURI(), REFERENCE_LOCALNAME, toPrefixString(REFERENCE_QNAME)); + } + catch (SAXException e) + { + throw new ExporterException("Failed to process end reference", e); + } + } /* (non-Javadoc) * @see org.alfresco.service.cmr.view.Exporter#warning(java.lang.String) @@ -685,7 +729,7 @@ import org.xml.sax.helpers.AttributesImpl; * @param toRef to reference * @return path */ - private Path createRelativePath(NodeRef fromRef, NodeRef toRef) + private Path createRelativePath(NodeRef rootRef, NodeRef fromRef, NodeRef toRef) { // Check that item exists first if (!nodeService.exists(toRef)) @@ -694,6 +738,7 @@ import org.xml.sax.helpers.AttributesImpl; return null; } + Path rootPath = nodeService.getPath(rootRef); Path fromPath = nodeService.getPath(fromRef); Path toPath = nodeService.getPath(toRef); Path relativePath = null; @@ -720,19 +765,19 @@ import org.xml.sax.helpers.AttributesImpl; { // Determine if from node is relative to export tree int i = 0; - while (i < exportNodePath.size() && i < fromPath.size() && exportNodePath.get(i).equals(fromPath.get(i))) + while (i < rootPath.size() && i < fromPath.size() && rootPath.get(i).equals(fromPath.get(i))) { i++; } - if (i == exportNodePath.size()) + if (i == rootPath.size()) { // Determine if to node is relative to export tree i = 0; - while (i < exportNodePath.size() && i < toPath.size() && exportNodePath.get(i).equals(toPath.get(i))) + while (i < rootPath.size() && i < toPath.size() && rootPath.get(i).equals(toPath.get(i))) { i++; } - if (i == exportNodePath.size()) + if (i == rootPath.size()) { // build relative path between from and to relativePath = new Path(); @@ -756,11 +801,11 @@ import org.xml.sax.helpers.AttributesImpl; } catch(Throwable e) { - String msg = "Failed to determine relative path: export path=" + exportNodePath + "; from path=" + fromPath + "; to path=" + toPath; + String msg = "Failed to determine relative path: root path=" + rootPath + "; from path=" + fromPath + "; to path=" + toPath; throw new ExporterException(msg, e); } return relativePath; } - + } diff --git a/source/java/org/alfresco/repo/importer/Importer.java b/source/java/org/alfresco/repo/importer/Importer.java index 896c03b7e1..cb737a1eb6 100644 --- a/source/java/org/alfresco/repo/importer/Importer.java +++ b/source/java/org/alfresco/repo/importer/Importer.java @@ -67,6 +67,14 @@ public interface Importer */ public NodeRef importNode(ImportNode node); + /** + * Resolve path within context of root reference + * + * @param path the path to resolve + * @return node reference + */ + public NodeRef resolvePath(String path); + /** * Signal completion of node import * diff --git a/source/java/org/alfresco/repo/importer/ImporterComponent.java b/source/java/org/alfresco/repo/importer/ImporterComponent.java index 4ed7495732..5114b29010 100644 --- a/source/java/org/alfresco/repo/importer/ImporterComponent.java +++ b/source/java/org/alfresco/repo/importer/ImporterComponent.java @@ -31,6 +31,7 @@ import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.repo.policy.BehaviourFilter; +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; @@ -543,42 +544,49 @@ public class ImporterComponent ImportParent parentContext = context.getParentContext(); NodeRef parentRef = parentContext.getParentRef(); - // determine the child node reference + // determine the node reference to link to String uuid = context.getUUID(); - if (uuid == null) + if (uuid == null || uuid.length() == 0) { - throw new ImporterException("Node reference does not resolve to a node"); + throw new ImporterException("Node reference does not specify a reference to follow."); } - NodeRef childRef = new NodeRef(parentRef.getStoreRef(), uuid); + 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); - - // determine child name - QName childQName = getChildName(context); - if (childQName == null) + AssociationDefinition assocDef = dictionaryService.getAssociation(assocType); + if (assocDef.isChild()) { - String name = (String)nodeService.getProperty(childRef, ContentModel.PROP_NAME); - if (name == null || name.length() == 0) + // determine child name + QName childQName = getChildName(context); + if (childQName == null) { - throw new ImporterException("Cannot determine node reference child name"); + 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); } - String localName = QName.createValidLocalName(name); - childQName = QName.createQName(assocType.getNamespaceURI(), localName); - } - // create the link - nodeService.addChild(parentRef, childRef, assocType, childQName); - reportNodeLinked(childRef, parentRef, assocType, childQName); + // 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 childRef; + return referencedRef; } /** @@ -621,6 +629,19 @@ public class ImporterComponent 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#end() @@ -800,7 +821,7 @@ public class ImporterComponent return closestAssocType; } - + /** * For the given import node, return the behaviours to disable during import * @@ -1347,7 +1368,7 @@ public class ImporterComponent // do the update Map existingProperties = nodeService.getProperties(existingNodeRef); Map updateProperties = bindProperties(node); - if (updateProperties != null) + if (updateProperties != null && updateProperties.size() > 0) { existingProperties.putAll(updateProperties); nodeService.setProperties(existingNodeRef, existingProperties); @@ -1382,6 +1403,7 @@ public class ImporterComponent return createNewStrategy.importNode(node); } } + } /** diff --git a/source/java/org/alfresco/repo/importer/view/ParentContext.java b/source/java/org/alfresco/repo/importer/view/ParentContext.java index 25b14a1ed2..83cbff5132 100644 --- a/source/java/org/alfresco/repo/importer/view/ParentContext.java +++ b/source/java/org/alfresco/repo/importer/view/ParentContext.java @@ -23,7 +23,7 @@ import java.util.Set; import org.alfresco.repo.importer.ImportParent; import org.alfresco.repo.importer.Importer; import org.alfresco.service.cmr.dictionary.AspectDefinition; -import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.TypeDefinition; import org.alfresco.service.cmr.repository.NodeRef; @@ -80,14 +80,18 @@ public class ParentContext extends ElementContext * @param parent * @param childDef */ - public ParentContext(QName elementName, NodeContext parent, ChildAssociationDefinition childDef) + public ParentContext(QName elementName, NodeContext parent, AssociationDefinition assocDef) { this(elementName, parent); TypeDefinition typeDef = parent.getTypeDefinition(); if (typeDef != null) { + // // Ensure association type is valid for node parent + // + + // Build complete Type Definition Set allAspects = new HashSet(); for (AspectDefinition typeAspect : parent.getTypeDefinition().getDefaultAspects()) { @@ -95,15 +99,17 @@ public class ParentContext extends ElementContext } allAspects.addAll(parent.getNodeAspects()); TypeDefinition anonymousType = getDictionaryService().getAnonymousType(parent.getTypeDefinition().getName(), allAspects); - Map nodeAssociations = anonymousType.getChildAssociations(); - if (nodeAssociations.containsKey(childDef.getName()) == false) + + // Determine if Association is valid for Type Definition + Map nodeAssociations = anonymousType.getAssociations(); + if (nodeAssociations.containsKey(assocDef.getName()) == false) { - throw new ImporterException("Association " + childDef.getName() + " is not valid for node " + parent.getTypeDefinition().getName()); + throw new ImporterException("Association " + assocDef.getName() + " is not valid for node " + parent.getTypeDefinition().getName()); } } parentRef = parent.getNodeRef(); - assocType = childDef.getName(); + assocType = assocDef.getName(); } /* (non-Javadoc) diff --git a/source/java/org/alfresco/repo/importer/view/ViewParser.java b/source/java/org/alfresco/repo/importer/view/ViewParser.java index f5cdb8bd0d..9d6691c793 100644 --- a/source/java/org/alfresco/repo/importer/view/ViewParser.java +++ b/source/java/org/alfresco/repo/importer/view/ViewParser.java @@ -26,7 +26,6 @@ import org.alfresco.repo.importer.Importer; import org.alfresco.repo.importer.Parser; import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.AssociationDefinition; -import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.dictionary.TypeDefinition; @@ -60,6 +59,7 @@ public class ViewParser implements Parser private static final String VIEW_ACCESS_STATUS_ATTR = "access"; private static final String VIEW_ID_ATTR = "id"; private static final String VIEW_IDREF_ATTR = "idref"; + private static final String VIEW_PATHREF_ATTR = "pathref"; private static final QName VIEW_METADATA = QName.createQName(NamespaceService.REPOSITORY_VIEW_1_0_URI, "metadata"); private static final QName VIEW_VALUE_QNAME = QName.createQName(NamespaceService.REPOSITORY_VIEW_1_0_URI, "value"); private static final QName VIEW_VALUES_QNAME = QName.createQName(NamespaceService.REPOSITORY_VIEW_1_0_URI, "values"); @@ -262,15 +262,11 @@ public class ViewParser implements Parser processProperty(xpp, ((PropertyDefinition)def).getName(), parserContext); return; } - else if (def instanceof ChildAssociationDefinition) + else if (def instanceof AssociationDefinition) { - processStartChildAssoc(xpp, (ChildAssociationDefinition)def, parserContext); + processStartAssoc(xpp, (AssociationDefinition)def, parserContext); return; } - else - { - // TODO: general association - } } else if (element instanceof NodeItemContext) { @@ -298,12 +294,7 @@ public class ViewParser implements Parser { throw new ImporterException("Association name " + defName + " is not valid; cannot find in Repository dictionary"); } - // TODO: Handle general associations... - if (!(def instanceof ChildAssociationDefinition)) - { - throw new ImporterException("Unsupported operation: The association " + defName + " cannot be imported - only child associations are supported at this time"); - } - processStartChildAssoc(xpp, (ChildAssociationDefinition)def, parserContext); + processStartAssoc(xpp, (AssociationDefinition)def, parserContext); } else if (itemName.equals(VIEW_ACL)) { @@ -414,10 +405,18 @@ public class ViewParser implements Parser throws XmlPullParserException, IOException { ParentContext parent = (ParentContext)parserContext.elementStack.peek(); + NodeContext node = new NodeContext(refName, parent, null); + node.setReference(true); // Extract Import scoped reference Id if explicitly defined - String uuid = null; String idRef = xpp.getAttributeValue(NamespaceService.REPOSITORY_VIEW_1_0_URI, VIEW_IDREF_ATTR); + String pathRef = xpp.getAttributeValue(NamespaceService.REPOSITORY_VIEW_1_0_URI, VIEW_PATHREF_ATTR); + + if ((idRef != null && idRef.length() > 0) && (pathRef != null && pathRef.length() > 0)) + { + // Do not support both IDREF and PATHREF + throw new ImporterException("Only one of " + VIEW_IDREF_ATTR + " or " + VIEW_PATHREF_ATTR + " can be specified."); + } if (idRef != null && idRef.length() > 0) { // retrieve uuid from previously imported node @@ -426,15 +425,16 @@ public class ViewParser implements Parser { throw new ImporterException("Cannot find node referenced by id " + idRef); } - uuid = nodeRef.getId(); + node.setUUID(nodeRef.getId()); } - - // Create reference - NodeContext node = new NodeContext(refName, parent, null); - node.setReference(true); - if (uuid != null) + else if (pathRef != null && pathRef.length() > 0) { - node.setUUID(uuid); + NodeRef referencedRef = parserContext.importer.resolvePath(pathRef); + if (referencedRef == null) + { + throw new ImporterException("Cannot find node referenced by path " + pathRef); + } + node.setUUID(referencedRef.getId()); } // Extract child name if explicitly defined @@ -705,22 +705,22 @@ public class ViewParser implements Parser } /** - * Process start of child association definition + * Process start of association definition * * @param xpp - * @param childAssocDef + * @param AssocDef * @param contextStack * @throws XmlPullParserException * @throws IOException */ - private void processStartChildAssoc(XmlPullParser xpp, ChildAssociationDefinition childAssocDef, ParserContext parserContext) + private void processStartAssoc(XmlPullParser xpp, AssociationDefinition assocDef, ParserContext parserContext) throws XmlPullParserException, IOException { NodeContext node = peekNodeContext(parserContext.elementStack); importNode(parserContext, node); // Construct Child Association Context - ParentContext parent = new ParentContext(childAssocDef.getName(), node, childAssocDef); + ParentContext parent = new ParentContext(assocDef.getName(), node, assocDef); parserContext.elementStack.push(parent); if (logger.isDebugEnabled()) @@ -750,7 +750,7 @@ public class ViewParser implements Parser } else if (element instanceof ParentContext) { - processEndChildAssoc(parserContext, (ParentContext)element); + processEndAssoc(parserContext, (ParentContext)element); } else if (element instanceof MetaDataContext) { @@ -776,7 +776,7 @@ public class ViewParser implements Parser * * @param context */ - private void processEndChildAssoc(ParserContext parserContext, ParentContext parent) + private void processEndAssoc(ParserContext parserContext, ParentContext parent) { } diff --git a/source/java/org/alfresco/service/cmr/view/Exporter.java b/source/java/org/alfresco/service/cmr/view/Exporter.java index f8180aa1c2..24fd90985a 100644 --- a/source/java/org/alfresco/service/cmr/view/Exporter.java +++ b/source/java/org/alfresco/service/cmr/view/Exporter.java @@ -67,6 +67,20 @@ public interface Exporter */ public void endNode(NodeRef nodeRef); + /** + * Start export of node reference + * + * @param nodeRef the node reference + */ + public void startReference(NodeRef nodeRef, QName childName); + + /** + * End export of node reference + * + * @param nodeRef the node reference + */ + public void endReference(NodeRef nodeRef); + /** * Start export of aspects * @@ -97,15 +111,28 @@ public interface Exporter */ public void endAspects(NodeRef nodeRef); - + /** + * Start export of ACL + * + * @param nodeRef for node reference + */ public void startACL(NodeRef nodeRef); + /** + * Export permission + * + * @param nodeRef for node reference + * @param permission the permission + */ public void permission(NodeRef nodeRef, AccessPermission permission); + /** + * End export of ACL + * + * @param nodeRef for node reference + */ public void endACL(NodeRef nodeRef); - - /** * Start export of properties * diff --git a/source/java/org/alfresco/service/cmr/view/ExporterContext.java b/source/java/org/alfresco/service/cmr/view/ExporterContext.java index 6c6564b51f..046a5ef3dc 100644 --- a/source/java/org/alfresco/service/cmr/view/ExporterContext.java +++ b/source/java/org/alfresco/service/cmr/view/ExporterContext.java @@ -15,4 +15,6 @@ public interface ExporterContext public NodeRef getExportOf(); + public NodeRef getExportParent(); + } diff --git a/source/java/org/alfresco/service/cmr/view/ExporterCrawlerParameters.java b/source/java/org/alfresco/service/cmr/view/ExporterCrawlerParameters.java index b3cd768039..e008b726da 100644 --- a/source/java/org/alfresco/service/cmr/view/ExporterCrawlerParameters.java +++ b/source/java/org/alfresco/service/cmr/view/ExporterCrawlerParameters.java @@ -32,6 +32,7 @@ public class ExporterCrawlerParameters private Location exportFrom = null; private boolean crawlSelf = false; private boolean crawlChildNodes = true; + private boolean crawlAssociations = true; private boolean crawlContent = true; private boolean crawlNullProperties = true; private String[] excludeNamespaceURIs = new String[] { NamespaceService.REPOSITORY_VIEW_1_0_URI }; @@ -57,6 +58,26 @@ public class ExporterCrawlerParameters this.crawlChildNodes = crawlChildNodes; } + /** + * Crawl and export associations + * + * @return true => crawl associations + */ + public boolean isCrawlAssociations() + { + return crawlAssociations; + } + + /** + * Sets whether to crawl associations + * + * @param crawlAssociations + */ + public void setCrawlAssociations(boolean crawlAssociations) + { + this.crawlAssociations = crawlAssociations; + } + /** * Crawl and export content properties * diff --git a/source/java/org/alfresco/tools/Export.java b/source/java/org/alfresco/tools/Export.java index 32669796a1..61a74b4419 100644 --- a/source/java/org/alfresco/tools/Export.java +++ b/source/java/org/alfresco/tools/Export.java @@ -615,6 +615,22 @@ public final class Export extends Tool { } + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#startReference(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void startReference(NodeRef nodeRef, QName childName) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#endReference(org.alfresco.service.cmr.repository.NodeRef) + */ + public void endReference(NodeRef nodeRef) + { + // TODO Auto-generated method stub + + } + } } diff --git a/source/java/org/alfresco/util/ISO9075.java b/source/java/org/alfresco/util/ISO9075.java index e8f661a162..a5da8d8b52 100644 --- a/source/java/org/alfresco/util/ISO9075.java +++ b/source/java/org/alfresco/util/ISO9075.java @@ -64,7 +64,7 @@ public class ISO9075 { return toEncode; } - else if (XMLChar.isValidName(toEncode) && (toEncode.indexOf("_x") == -1)) + else if (XMLChar.isValidName(toEncode) && (toEncode.indexOf("_x") == -1) && (toEncode.indexOf(':') == -1)) { return toEncode; } diff --git a/source/java/org/alfresco/util/ISO9075Test.java b/source/java/org/alfresco/util/ISO9075Test.java index 1876c68aff..ed67c9727d 100644 --- a/source/java/org/alfresco/util/ISO9075Test.java +++ b/source/java/org/alfresco/util/ISO9075Test.java @@ -47,7 +47,7 @@ public class ISO9075Test extends TestCase "_x0020__x0060__x00ac__x00a6__x0021__x0022__x00a3__x0024__x0025__x005e__x0026__x002a__x0028__x0029_-__x003d__x002b__x0009__x000a__x005c__x0000__x005b__x005d__x007b__x007d__x003b__x0027__x0023__x003a__x0040__x007e__x002c_._x002f__x003c__x003e__x003f__x005c__x007c_", ISO9075.encode(" `¬¦!\"£$%^&*()-_=+\t\n\\\u0000[]{};'#:@~,./<>?\\|")); assertEquals("\u0123_x4567_\u8900_xabcd__xefff__xT65A_", ISO9075.encode("\u0123\u4567\u8900\uabcd\uefff_xT65A_")); - + assertEquals("_x003a_", ISO9075.encode(":")); } public void testDeEncoding() diff --git a/source/java/org/alfresco/util/debug/NodeStoreInspector.java b/source/java/org/alfresco/util/debug/NodeStoreInspector.java index fa8c4538c3..5bfccbd5a6 100644 --- a/source/java/org/alfresco/util/debug/NodeStoreInspector.java +++ b/source/java/org/alfresco/util/debug/NodeStoreInspector.java @@ -62,6 +62,32 @@ public class NodeStoreInspector return builder.toString(); } + + /** + * Dumps the contents of a node + * + * @param nodeService + * @param nodeRef + * @return + */ + public static String dumpNode(NodeService nodeService, NodeRef nodeRef) + { + StringBuilder builder = new StringBuilder(); + + if (nodeService.exists(nodeRef) == true) + { + builder.append(outputNode(0, nodeService, nodeRef)); + } + else + { + builder. + append("The node "). + append(nodeRef.toString()). + append(" does not exist."); + } + + return builder.toString(); + } /** * Output the node