(this.nodeService.getAspects(nodeRef));
- types.add(this.nodeService.getType(nodeRef));
- OnContentReadPolicy policy = this.onContentReadDelegate.get(nodeRef, types);
- policy.onContentRead(nodeRef);
- }
-
- // we don't listen for anything
- // result may be null - but interface contract says we may return null
- return reader;
- }
-
- public ContentWriter getWriter(NodeRef nodeRef, QName propertyQName, boolean update)
- {
-
- // check for an existing URL - the get of the reader will perform type checking
- ContentReader existingContentReader = getReader(nodeRef, propertyQName, false);
-
- // TODO: Choose the store to write to at runtime
-
- // get the content using the (potentially) existing content - the new content
- // can be wherever the store decides.
- ContentWriter writer = store.getWriter(existingContentReader, null);
-
- // Special case for AVM repository.
- Serializable contentValue = null;
- if (nodeRef.getStoreRef().getProtocol().equals(StoreRef.PROTOCOL_AVM))
- {
- Object [] avmVersionPath = AVMNodeConverter.ToAVMVersionPath(nodeRef);
- contentValue = avmService.getContentDataForWrite((String)avmVersionPath[1]);
- }
- else
- {
- contentValue = nodeService.getProperty(nodeRef, propertyQName);
- }
- // set extra data on the reader if the property is pre-existing
- if (contentValue != null && contentValue instanceof ContentData)
- {
- ContentData contentData = (ContentData)contentValue;
- writer.setMimetype(contentData.getMimetype());
- writer.setEncoding(contentData.getEncoding());
- }
-
- // attach a listener if required
- if (update)
- {
- // need a listener to update the node when the stream closes
- WriteStreamListener listener = new WriteStreamListener(nodeService, nodeRef, propertyQName, writer);
- writer.addListener(listener);
- writer.setTransactionService(transactionService);
- }
-
- // give back to the client
- return writer;
- }
-
- /**
- * @return Returns a writer to an anonymous location
- */
- public ContentWriter getTempWriter()
- {
- // there is no existing content and we don't specify the location of the new content
- return tempStore.getWriter(null, null);
- }
-
- /**
- * @see org.alfresco.repo.content.transform.ContentTransformerRegistry
- * @see org.alfresco.repo.content.transform.ContentTransformer
- */
- public void transform(ContentReader reader, ContentWriter writer)
- throws NoTransformerException, ContentIOException
- {
- // check that source and target mimetypes are available
- String sourceMimetype = reader.getMimetype();
- if (sourceMimetype == null)
- {
- throw new AlfrescoRuntimeException("The content reader mimetype must be set: " + reader);
- }
- String targetMimetype = writer.getMimetype();
- if (targetMimetype == null)
- {
- throw new AlfrescoRuntimeException("The content writer mimetype must be set: " + writer);
- }
- // look for a transformer
- ContentTransformer transformer = transformerRegistry.getTransformer(sourceMimetype, targetMimetype);
- if (transformer == null)
- {
- throw new NoTransformerException(sourceMimetype, targetMimetype);
- }
- // we have a transformer, so do it
- transformer.transform(reader, writer);
- // done
- }
-
- /**
- * @see org.alfresco.repo.content.transform.ContentTransformerRegistry
- * @see org.alfresco.repo.content.transform.ContentTransformer
- */
- public ContentTransformer getTransformer(String sourceMimetype, String targetMimetype)
- {
- // look for a transformer
- ContentTransformer transformer = transformerRegistry.getTransformer(sourceMimetype, targetMimetype);
- // done
- return transformer;
- }
-
- /**
- * @see org.alfresco.service.cmr.repository.ContentService#getImageTransformer()
- */
- public ContentTransformer getImageTransformer()
- {
- return imageMagickContentTransformer;
- }
-
- /**
- * @see org.alfresco.repo.content.transform.ContentTransformerRegistry
- * @see org.alfresco.repo.content.transform.ContentTransformer
- */
- public boolean isTransformable(ContentReader reader, ContentWriter writer)
- {
- // check that source and target mimetypes are available
- String sourceMimetype = reader.getMimetype();
- if (sourceMimetype == null)
- {
- throw new AlfrescoRuntimeException("The content reader mimetype must be set: " + reader);
- }
- String targetMimetype = writer.getMimetype();
- if (targetMimetype == null)
- {
- throw new AlfrescoRuntimeException("The content writer mimetype must be set: " + writer);
- }
-
- // look for a transformer
- ContentTransformer transformer = transformerRegistry.getTransformer(sourceMimetype, targetMimetype);
- return (transformer != null);
- }
-
- /**
- * Ensures that, upon closure of the output stream, the node is updated with
- * the latest URL of the content to which it refers.
- *
- * The listener close operation does not need a transaction as the
- * ContentWriter
takes care of that.
- *
- * @author Derek Hulley
- */
- private static class WriteStreamListener implements ContentStreamListener
- {
- private NodeService nodeService;
- private NodeRef nodeRef;
- private QName propertyQName;
- private ContentWriter writer;
-
- public WriteStreamListener(
- NodeService nodeService,
- NodeRef nodeRef,
- QName propertyQName,
- ContentWriter writer)
- {
- this.nodeService = nodeService;
- this.nodeRef = nodeRef;
- this.propertyQName = propertyQName;
- this.writer = writer;
- }
-
- public void contentStreamClosed() throws ContentIOException
- {
- try
- {
- // set the full content property
- ContentData contentData = writer.getContentData();
- nodeService.setProperty(
- nodeRef,
- propertyQName,
- contentData);
- // done
- if (logger.isDebugEnabled())
- {
- logger.debug("Stream listener updated node: \n" +
- " node: " + nodeRef + "\n" +
- " property: " + propertyQName + "\n" +
- " value: " + contentData);
- }
- }
- catch (Throwable e)
- {
- throw new ContentIOException("Failed to set content property on stream closure: \n" +
- " node: " + nodeRef + "\n" +
- " property: " + propertyQName + "\n" +
- " writer: " + writer,
- e);
- }
- }
- }
-}
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.content;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.model.ContentModel;
+import org.alfresco.repo.avm.AVMNodeConverter;
+import org.alfresco.repo.content.ContentServicePolicies.OnContentReadPolicy;
+import org.alfresco.repo.content.ContentServicePolicies.OnContentUpdatePolicy;
+import org.alfresco.repo.content.filestore.FileContentStore;
+import org.alfresco.repo.content.transform.ContentTransformer;
+import org.alfresco.repo.content.transform.ContentTransformerRegistry;
+import org.alfresco.repo.content.transform.magick.ImageMagickContentTransformer;
+import org.alfresco.repo.policy.ClassPolicyDelegate;
+import org.alfresco.repo.policy.JavaBehaviour;
+import org.alfresco.repo.policy.PolicyComponent;
+import org.alfresco.service.cmr.avm.AVMService;
+import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
+import org.alfresco.service.cmr.dictionary.DictionaryService;
+import org.alfresco.service.cmr.dictionary.InvalidTypeException;
+import org.alfresco.service.cmr.dictionary.PropertyDefinition;
+import org.alfresco.service.cmr.repository.ContentData;
+import org.alfresco.service.cmr.repository.ContentIOException;
+import org.alfresco.service.cmr.repository.ContentReader;
+import org.alfresco.service.cmr.repository.ContentService;
+import org.alfresco.service.cmr.repository.ContentStreamListener;
+import org.alfresco.service.cmr.repository.ContentWriter;
+import org.alfresco.service.cmr.repository.NoTransformerException;
+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.namespace.NamespaceService;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.service.transaction.TransactionService;
+import org.alfresco.util.EqualsHelper;
+import org.alfresco.util.TempFileProvider;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * A content service that determines at runtime the store that the
+ * content associated with a node should be routed to.
+ *
+ * @author Derek Hulley
+ */
+public class RoutingContentService implements ContentService
+{
+ private static Log logger = LogFactory.getLog(RoutingContentService.class);
+
+ private TransactionService transactionService;
+ private DictionaryService dictionaryService;
+ private NodeService nodeService;
+ private AVMService avmService;
+
+ /** a registry of all available content transformers */
+ private ContentTransformerRegistry transformerRegistry;
+ /** TEMPORARY until we have a map to choose from at runtime */
+ private ContentStore store;
+ /** the store for all temporarily created content */
+ private ContentStore tempStore;
+ private ImageMagickContentTransformer imageMagickContentTransformer;
+
+ /**
+ * The policy component
+ */
+ private PolicyComponent policyComponent;
+
+ /**
+ * Policies delegate
+ */
+ ClassPolicyDelegate onContentUpdateDelegate;
+ ClassPolicyDelegate onContentReadDelegate;
+
+ /**
+ * Default constructor sets up a temporary store
+ */
+ public RoutingContentService()
+ {
+ this.tempStore = new FileContentStore(TempFileProvider.getTempDir().getAbsolutePath());
+ }
+
+ public void setTransactionService(TransactionService transactionService)
+ {
+ this.transactionService = transactionService;
+ }
+
+ public void setDictionaryService(DictionaryService dictionaryService)
+ {
+ this.dictionaryService = dictionaryService;
+ }
+
+ public void setNodeService(NodeService nodeService)
+ {
+ this.nodeService = nodeService;
+ }
+
+ public void setTransformerRegistry(ContentTransformerRegistry transformerRegistry)
+ {
+ this.transformerRegistry = transformerRegistry;
+ }
+
+ public void setStore(ContentStore store)
+ {
+ this.store = store;
+ }
+
+ public void setPolicyComponent(PolicyComponent policyComponent)
+ {
+ this.policyComponent = policyComponent;
+ }
+
+ public void setAvmService(AVMService service)
+ {
+ this.avmService = service;
+ }
+
+ public void setImageMagickContentTransformer(ImageMagickContentTransformer imageMagickContentTransformer)
+ {
+ this.imageMagickContentTransformer = imageMagickContentTransformer;
+ }
+
+ /**
+ * Service initialise
+ */
+ public void init()
+ {
+ // Bind on update properties behaviour
+ this.policyComponent.bindClassBehaviour(
+ QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"),
+ this,
+ new JavaBehaviour(this, "onUpdateProperties"));
+
+ // Register on content update policy
+ this.onContentUpdateDelegate = this.policyComponent.registerClassPolicy(OnContentUpdatePolicy.class);
+ this.onContentReadDelegate = this.policyComponent.registerClassPolicy(OnContentReadPolicy.class);
+ }
+
+ /**
+ * Update properties policy behaviour
+ *
+ * @param nodeRef the node reference
+ * @param before the before values of the properties
+ * @param after the after values of the properties
+ */
+ public void onUpdateProperties(
+ NodeRef nodeRef,
+ Map before,
+ Map after)
+ {
+ boolean fire = false;
+ boolean newContent = false;
+ // check if any of the content properties have changed
+ for (QName propertyQName : after.keySet())
+ {
+ // is this a content property?
+ PropertyDefinition propertyDef = dictionaryService.getProperty(propertyQName);
+ if (propertyDef == null)
+ {
+ // the property is not recognised
+ continue;
+ }
+ if (!propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))
+ {
+ // not a content type
+ continue;
+ }
+
+ try
+ {
+ ContentData beforeValue = (ContentData) before.get(propertyQName);
+ ContentData afterValue = (ContentData) after.get(propertyQName);
+ if (afterValue != null && afterValue.getContentUrl() == null)
+ {
+ // no URL - ignore
+ }
+ else if (!EqualsHelper.nullSafeEquals(beforeValue, afterValue))
+ {
+ // So debug ...
+ if (logger.isDebugEnabled() == true)
+ {
+ String beforeString = "";
+ if (beforeValue != null)
+ {
+ beforeString = beforeValue.toString();
+ }
+ String afterString = "";
+ if (afterValue != null)
+ {
+ afterString = afterValue.toString();
+ }
+ logger.debug("onContentUpate: before = " + beforeString + "; after = " + afterString);
+ }
+
+ // Figure out if the content is new or not
+ String beforeContentUrl = null;
+ if (beforeValue != null)
+ {
+ beforeContentUrl = beforeValue.getContentUrl();
+ }
+ String afterContentUrl = null;
+ if (afterValue != null)
+ {
+ afterContentUrl = afterValue.getContentUrl();
+ }
+ if (beforeContentUrl == null && afterContentUrl != null)
+ {
+ newContent = true;
+ }
+
+ // the content changed
+ // at the moment, we are only interested in this one change
+ fire = true;
+ break;
+ }
+ }
+ catch (ClassCastException e)
+ {
+ // properties don't conform to model
+ continue;
+ }
+ }
+ // fire?
+ if (fire)
+ {
+ // Fire the content update policy
+ Set types = new HashSet(this.nodeService.getAspects(nodeRef));
+ types.add(this.nodeService.getType(nodeRef));
+ OnContentUpdatePolicy policy = this.onContentUpdateDelegate.get(nodeRef, types);
+ policy.onContentUpdate(nodeRef, newContent);
+ }
+ }
+
+ public ContentReader getReader(NodeRef nodeRef, QName propertyQName)
+ {
+ return getReader(nodeRef, propertyQName, true);
+ }
+
+ private ContentReader getReader(NodeRef nodeRef, QName propertyQName, boolean fireContentReadPolicy)
+ {
+ ContentData contentData = null;
+ Serializable propValue = nodeService.getProperty(nodeRef, propertyQName);
+ if (propValue instanceof Collection)
+ {
+ Collection colPropValue = (Collection)propValue;
+ if (colPropValue.size() > 0)
+ {
+ propValue = (Serializable)colPropValue.iterator().next();
+ }
+ }
+
+ if (propValue instanceof ContentData)
+ {
+ contentData = (ContentData)propValue;
+ }
+
+ if (contentData == null)
+ {
+ // if no value or a value other content, and a property definition has been provided, ensure that it's CONTENT or ANY
+ PropertyDefinition contentPropDef = dictionaryService.getProperty(propertyQName);
+ if (contentPropDef != null &&
+ (!(contentPropDef.getDataType().getName().equals(DataTypeDefinition.CONTENT) ||
+ contentPropDef.getDataType().getName().equals(DataTypeDefinition.ANY))))
+ {
+ throw new InvalidTypeException("The node property must be of type content: \n" +
+ " node: " + nodeRef + "\n" +
+ " property name: " + propertyQName + "\n" +
+ " property type: " + ((contentPropDef == null) ? "unknown" : contentPropDef.getDataType()),
+ propertyQName);
+ }
+ }
+
+ // check that the URL is available
+ if (contentData == null || contentData.getContentUrl() == null)
+ {
+ // there is no URL - the interface specifies that this is not an error condition
+ return null;
+ }
+ String contentUrl = contentData.getContentUrl();
+
+ // TODO: Choose the store to read from at runtime
+ ContentReader reader = store.getReader(contentUrl);
+
+ // set extra data on the reader
+ reader.setMimetype(contentData.getMimetype());
+ reader.setEncoding(contentData.getEncoding());
+
+ // Fire the content read policy
+ if (reader != null && fireContentReadPolicy == true)
+ {
+ // Fire the content update policy
+ Set types = new HashSet(this.nodeService.getAspects(nodeRef));
+ types.add(this.nodeService.getType(nodeRef));
+ OnContentReadPolicy policy = this.onContentReadDelegate.get(nodeRef, types);
+ policy.onContentRead(nodeRef);
+ }
+
+ // we don't listen for anything
+ // result may be null - but interface contract says we may return null
+ return reader;
+ }
+
+ public ContentWriter getWriter(NodeRef nodeRef, QName propertyQName, boolean update)
+ {
+
+ // check for an existing URL - the get of the reader will perform type checking
+ ContentReader existingContentReader = getReader(nodeRef, propertyQName, false);
+
+ // TODO: Choose the store to write to at runtime
+
+ // get the content using the (potentially) existing content - the new content
+ // can be wherever the store decides.
+ ContentWriter writer = store.getWriter(existingContentReader, null);
+
+ // Special case for AVM repository.
+ Serializable contentValue = null;
+ if (nodeRef.getStoreRef().getProtocol().equals(StoreRef.PROTOCOL_AVM))
+ {
+ Object [] avmVersionPath = AVMNodeConverter.ToAVMVersionPath(nodeRef);
+ contentValue = avmService.getContentDataForWrite((String)avmVersionPath[1]);
+ }
+ else
+ {
+ contentValue = nodeService.getProperty(nodeRef, propertyQName);
+ }
+
+ // set extra data on the reader if the property is pre-existing
+ if (contentValue != null && contentValue instanceof ContentData)
+ {
+ ContentData contentData = (ContentData)contentValue;
+ writer.setMimetype(contentData.getMimetype());
+ writer.setEncoding(contentData.getEncoding());
+ }
+
+ // attach a listener if required
+ if (update)
+ {
+ // need a listener to update the node when the stream closes
+ WriteStreamListener listener = new WriteStreamListener(nodeService, nodeRef, propertyQName, writer);
+ writer.addListener(listener);
+ writer.setTransactionService(transactionService);
+ }
+
+ // give back to the client
+ return writer;
+ }
+
+ /**
+ * @return Returns a writer to an anonymous location
+ */
+ public ContentWriter getTempWriter()
+ {
+ // there is no existing content and we don't specify the location of the new content
+ return tempStore.getWriter(null, null);
+ }
+
+ /**
+ * @see org.alfresco.repo.content.transform.ContentTransformerRegistry
+ * @see org.alfresco.repo.content.transform.ContentTransformer
+ */
+ public void transform(ContentReader reader, ContentWriter writer)
+ throws NoTransformerException, ContentIOException
+ {
+ // check that source and target mimetypes are available
+ String sourceMimetype = reader.getMimetype();
+ if (sourceMimetype == null)
+ {
+ throw new AlfrescoRuntimeException("The content reader mimetype must be set: " + reader);
+ }
+ String targetMimetype = writer.getMimetype();
+ if (targetMimetype == null)
+ {
+ throw new AlfrescoRuntimeException("The content writer mimetype must be set: " + writer);
+ }
+ // look for a transformer
+ ContentTransformer transformer = transformerRegistry.getTransformer(sourceMimetype, targetMimetype);
+ if (transformer == null)
+ {
+ throw new NoTransformerException(sourceMimetype, targetMimetype);
+ }
+ // we have a transformer, so do it
+ transformer.transform(reader, writer);
+ // done
+ }
+
+ /**
+ * @see org.alfresco.repo.content.transform.ContentTransformerRegistry
+ * @see org.alfresco.repo.content.transform.ContentTransformer
+ */
+ public ContentTransformer getTransformer(String sourceMimetype, String targetMimetype)
+ {
+ // look for a transformer
+ ContentTransformer transformer = transformerRegistry.getTransformer(sourceMimetype, targetMimetype);
+ // done
+ return transformer;
+ }
+
+ /**
+ * @see org.alfresco.service.cmr.repository.ContentService#getImageTransformer()
+ */
+ public ContentTransformer getImageTransformer()
+ {
+ return imageMagickContentTransformer;
+ }
+
+ /**
+ * @see org.alfresco.repo.content.transform.ContentTransformerRegistry
+ * @see org.alfresco.repo.content.transform.ContentTransformer
+ */
+ public boolean isTransformable(ContentReader reader, ContentWriter writer)
+ {
+ // check that source and target mimetypes are available
+ String sourceMimetype = reader.getMimetype();
+ if (sourceMimetype == null)
+ {
+ throw new AlfrescoRuntimeException("The content reader mimetype must be set: " + reader);
+ }
+ String targetMimetype = writer.getMimetype();
+ if (targetMimetype == null)
+ {
+ throw new AlfrescoRuntimeException("The content writer mimetype must be set: " + writer);
+ }
+
+ // look for a transformer
+ ContentTransformer transformer = transformerRegistry.getTransformer(sourceMimetype, targetMimetype);
+ return (transformer != null);
+ }
+
+ /**
+ * Ensures that, upon closure of the output stream, the node is updated with
+ * the latest URL of the content to which it refers.
+ *
+ * The listener close operation does not need a transaction as the
+ * ContentWriter
takes care of that.
+ *
+ * @author Derek Hulley
+ */
+ private static class WriteStreamListener implements ContentStreamListener
+ {
+ private NodeService nodeService;
+ private NodeRef nodeRef;
+ private QName propertyQName;
+ private ContentWriter writer;
+
+ public WriteStreamListener(
+ NodeService nodeService,
+ NodeRef nodeRef,
+ QName propertyQName,
+ ContentWriter writer)
+ {
+ this.nodeService = nodeService;
+ this.nodeRef = nodeRef;
+ this.propertyQName = propertyQName;
+ this.writer = writer;
+ }
+
+ public void contentStreamClosed() throws ContentIOException
+ {
+ try
+ {
+ // set the full content property
+ ContentData contentData = writer.getContentData();
+ nodeService.setProperty(
+ nodeRef,
+ propertyQName,
+ contentData);
+ // done
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Stream listener updated node: \n" +
+ " node: " + nodeRef + "\n" +
+ " property: " + propertyQName + "\n" +
+ " value: " + contentData);
+ }
+ }
+ catch (Throwable e)
+ {
+ throw new ContentIOException("Failed to set content property on stream closure: \n" +
+ " node: " + nodeRef + "\n" +
+ " property: " + propertyQName + "\n" +
+ " writer: " + writer,
+ e);
+ }
+ }
+ }
+}
diff --git a/source/java/org/alfresco/repo/copy/CopyServiceImpl.java b/source/java/org/alfresco/repo/copy/CopyServiceImpl.java
index 8172f86235..d9a0f7e59e 100644
--- a/source/java/org/alfresco/repo/copy/CopyServiceImpl.java
+++ b/source/java/org/alfresco/repo/copy/CopyServiceImpl.java
@@ -193,17 +193,17 @@ public class CopyServiceImpl implements CopyService
*/
public NodeRef copy(
NodeRef sourceNodeRef,
- NodeRef destinationParent,
+ NodeRef destinationParentRef,
QName destinationAssocTypeQName,
QName destinationQName,
boolean copyChildren)
{
// Check that all the passed values are not null
ParameterCheck.mandatory("Source Node", sourceNodeRef);
- ParameterCheck.mandatory("Destination Parent", destinationParent);
+ ParameterCheck.mandatory("Destination Parent", destinationParentRef);
ParameterCheck.mandatory("Destination Association Name", destinationQName);
- if (sourceNodeRef.getStoreRef().equals(destinationParent.getStoreRef()) == false)
+ if (sourceNodeRef.getStoreRef().equals(destinationParentRef.getStoreRef()) == false)
{
// TODO We need to create a new node in the other store with the same id as the source
@@ -211,9 +211,19 @@ public class CopyServiceImpl implements CopyService
throw new UnsupportedOperationException("Copying nodes across stores is not currently supported.");
}
+ // Get the original parent reference
+ NodeRef sourceParentRef = nodeService.getPrimaryParent(sourceNodeRef).getParentRef();
// Recursively copy node
Map copiedChildren = new HashMap();
- NodeRef copy = recursiveCopy(sourceNodeRef, destinationParent, destinationAssocTypeQName, destinationQName, copyChildren, copiedChildren);
+ NodeRef copy = recursiveCopy(
+ sourceNodeRef,
+ sourceParentRef,
+ destinationParentRef,
+ destinationAssocTypeQName,
+ destinationQName,
+ copyChildren,
+ true, // top-level copy drops the name, if the parent is different
+ copiedChildren);
// Foreach of the newly created copies call the copy complete policy
for (Map.Entry entry : copiedChildren.entrySet())
@@ -360,20 +370,16 @@ public class CopyServiceImpl implements CopyService
/**
* Recursive copy algorithm
*
- * @param sourceNodeRef
- * @param destinationParent
- * @param destinationAssocTypeQName
- * @param destinationQName
- * @param copyChildren
- * @param copiedChildren
- * @return
+ * @param dropName drop the name property when associations don't allow duplicately named children
*/
private NodeRef recursiveCopy(
NodeRef sourceNodeRef,
- NodeRef destinationParent,
+ NodeRef sourceParentRef,
+ NodeRef destinationParentRef,
QName destinationAssocTypeQName,
QName destinationQName,
boolean copyChildren,
+ boolean dropName,
Map copiedChildren)
{
// Extract Type Definition
@@ -385,7 +391,7 @@ public class CopyServiceImpl implements CopyService
}
// Establish the scope of the copy
- PolicyScope copyDetails = getCopyDetails(sourceNodeRef, destinationParent.getStoreRef(), true);
+ PolicyScope copyDetails = getCopyDetails(sourceNodeRef, destinationParentRef.getStoreRef(), true);
// Create collection of properties for type and mandatory aspects
Map typeProps = copyDetails.getProperties();
@@ -403,8 +409,8 @@ public class CopyServiceImpl implements CopyService
}
}
- // if the parent node is the same, then remove the name property - it will have to
- // be changed by the client code
+ // Drop the name property, if required. This prevents duplicate names and leaves it up to the client
+ // to assign a new name.
AssociationDefinition assocDef = dictionaryService.getAssociation(destinationAssocTypeQName);
if (!assocDef.isChild())
{
@@ -413,7 +419,7 @@ public class CopyServiceImpl implements CopyService
else
{
ChildAssociationDefinition childAssocDef = (ChildAssociationDefinition) assocDef;
- if (!childAssocDef.getDuplicateChildNamesAllowed())
+ if (dropName && !childAssocDef.getDuplicateChildNamesAllowed())
{
// duplicate children are not allowed.
properties.remove(ContentModel.PROP_NAME);
@@ -422,14 +428,14 @@ public class CopyServiceImpl implements CopyService
// Create the new node
ChildAssociationRef destinationChildAssocRef = this.nodeService.createNode(
- destinationParent,
+ destinationParentRef,
destinationAssocTypeQName,
destinationQName,
sourceTypeRef,
properties);
NodeRef destinationNodeRef = destinationChildAssocRef.getChildRef();
copiedChildren.put(sourceNodeRef, destinationNodeRef);
-
+
// Prevent any rules being fired on the new destination node
this.ruleService.disableRules(destinationNodeRef);
try
@@ -736,9 +742,9 @@ public class CopyServiceImpl implements CopyService
*/
private void copyChildAssociations(
QName classRef,
- NodeRef destinationNodeRef,
+ NodeRef destinationNodeRef,
PolicyScope copyDetails,
- boolean copyChildren,
+ boolean copyChildren,
Map copiedChildren)
{
List childAssocs = copyDetails.getChildAssociations(classRef);
@@ -756,11 +762,13 @@ public class CopyServiceImpl implements CopyService
{
// Copy the child
recursiveCopy(
- childAssoc.getChildRef(),
+ childAssoc.getChildRef(),
+ childAssoc.getParentRef(),
destinationNodeRef,
childAssoc.getTypeQName(),
childAssoc.getQName(),
copyChildren,
+ false, // the target and source parents can't be the same
copiedChildren);
}
}
@@ -784,11 +792,13 @@ public class CopyServiceImpl implements CopyService
{
// Always recursivly copy configuration folders
recursiveCopy(
- childRef,
+ childRef,
+ childAssoc.getParentRef(),
destinationNodeRef,
childAssoc.getTypeQName(),
childAssoc.getQName(),
true,
+ false, // the target and source parents can't be the same
copiedChildren);
}
}
diff --git a/source/java/org/alfresco/repo/domain/DbAccessControlList.java b/source/java/org/alfresco/repo/domain/DbAccessControlList.java
index 327ed47347..da378dd6c0 100644
--- a/source/java/org/alfresco/repo/domain/DbAccessControlList.java
+++ b/source/java/org/alfresco/repo/domain/DbAccessControlList.java
@@ -1,83 +1,83 @@
-/*
- * Copyright (C) 2005 Alfresco, Inc.
- *
- * Licensed under the Mozilla Public License version 1.1
- * with a permitted attribution clause. You may obtain a
- * copy of the License at
- *
- * http://www.alfresco.org/legal/license.txt
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
- * either express or implied. See the License for the specific
- * language governing permissions and limitations under the
- * License.
- */
-package org.alfresco.repo.domain;
-
-import java.util.Set;
-
-import org.alfresco.repo.domain.hibernate.DbAccessControlEntryImpl;
-
-
-/**
- * The interface to support persistence of node access control entries in hibernate
- *
- * @author andyh
- */
-public interface DbAccessControlList
-{
- public long getId();
-
- /**
- *
- * @return Returns the access control entries for this access control list
- */
- public Set getEntries();
-
- /**
- * Get inheritance behaviour
- * @return Returns the inheritance status of this list
- */
- public boolean getInherits();
-
- /**
- * Set inheritance behaviour
- * @param inherits true to set the permissions to inherit
- */
- public void setInherits(boolean inherits);
-
- public int deleteEntriesForAuthority(String authorityKey);
-
- public int deleteEntriesForPermission(DbPermissionKey permissionKey);
-
- public int deleteEntry(String authorityKey, DbPermissionKey permissionKey);
-
- /**
- * Delete the entries related to this access control list
- *
- * @return Returns the number of entries deleted
- */
- public int deleteEntries();
-
- public DbAccessControlEntry getEntry(String authorityKey, DbPermissionKey permissionKey);
-
- /**
- * Factory method to create an entry and wire it up.
- * Note that the returned value may still be transient. Saving it should be fine, but
- * is not required.
- *
- * @param permission the mandatory permission association with this entry
- * @param authority the mandatory authority. Must not be transient.
- * @param allowed allowed or disallowed. Must not be transient.
- * @return Returns the new entry
- */
- public DbAccessControlEntryImpl newEntry(DbPermission permission, DbAuthority authority, boolean allowed);
-
- /**
- * Make a copy of this ACL (persistently)
- * @return The copy.
- */
- public DbAccessControlList getCopy();
-}
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.domain;
+
+import java.util.Set;
+
+import org.alfresco.repo.domain.hibernate.DbAccessControlEntryImpl;
+
+
+/**
+ * The interface to support persistence of node access control entries in hibernate
+ *
+ * @author andyh
+ */
+public interface DbAccessControlList
+{
+ public long getId();
+
+ /**
+ *
+ * @return Returns the access control entries for this access control list
+ */
+ public Set getEntries();
+
+ /**
+ * Get inheritance behaviour
+ * @return Returns the inheritance status of this list
+ */
+ public boolean getInherits();
+
+ /**
+ * Set inheritance behaviour
+ * @param inherits true to set the permissions to inherit
+ */
+ public void setInherits(boolean inherits);
+
+ public int deleteEntriesForAuthority(String authorityKey);
+
+ public int deleteEntriesForPermission(DbPermissionKey permissionKey);
+
+ public int deleteEntry(String authorityKey, DbPermissionKey permissionKey);
+
+ /**
+ * Delete the entries related to this access control list
+ *
+ * @return Returns the number of entries deleted
+ */
+ public int deleteEntries();
+
+ public DbAccessControlEntry getEntry(String authorityKey, DbPermissionKey permissionKey);
+
+ /**
+ * Factory method to create an entry and wire it up.
+ * Note that the returned value may still be transient. Saving it should be fine, but
+ * is not required.
+ *
+ * @param permission the mandatory permission association with this entry
+ * @param authority the mandatory authority. Must not be transient.
+ * @param allowed allowed or disallowed. Must not be transient.
+ * @return Returns the new entry
+ */
+ public DbAccessControlEntryImpl newEntry(DbPermission permission, DbAuthority authority, boolean allowed);
+
+ /**
+ * Make a copy of this ACL (persistently)
+ * @return The copy.
+ */
+ public DbAccessControlList getCopy();
+}
diff --git a/source/java/org/alfresco/repo/domain/hibernate/ChildAssocImpl.java b/source/java/org/alfresco/repo/domain/hibernate/ChildAssocImpl.java
index 46dafacc2d..8df8f271a2 100644
--- a/source/java/org/alfresco/repo/domain/hibernate/ChildAssocImpl.java
+++ b/source/java/org/alfresco/repo/domain/hibernate/ChildAssocImpl.java
@@ -148,24 +148,12 @@ public class ChildAssocImpl implements ChildAssoc, Serializable
return false;
}
ChildAssoc that = (ChildAssoc) obj;
- if (EqualsHelper.nullSafeEquals(id, that.getId()))
- {
- return true;
- }
- else
- {
- return (
- EqualsHelper.nullSafeEquals(this.getChild().getId(), that.getChild().getId())
- && EqualsHelper.nullSafeEquals(this.getQname(), that.getQname())
- && EqualsHelper.nullSafeEquals(this.getParent().getId(), that.getParent().getId())
- && EqualsHelper.nullSafeEquals(this.getTypeQName(), that.getTypeQName())
- );
- }
+ return EqualsHelper.nullSafeEquals(id, that.getId());
}
public int hashCode()
{
- return (qName == null ? 0 : qName.hashCode());
+ return (id == null ? 0 : id.hashCode());
}
/**
diff --git a/source/java/org/alfresco/repo/domain/hibernate/DbAccessControlListImpl.java b/source/java/org/alfresco/repo/domain/hibernate/DbAccessControlListImpl.java
index 7139d6a8bc..6576a88117 100644
--- a/source/java/org/alfresco/repo/domain/hibernate/DbAccessControlListImpl.java
+++ b/source/java/org/alfresco/repo/domain/hibernate/DbAccessControlListImpl.java
@@ -1,250 +1,250 @@
-/*
- * Copyright (C) 2005 Alfresco, Inc.
- *
- * Licensed under the Mozilla Public License version 1.1
- * with a permitted attribution clause. You may obtain a
- * copy of the License at
- *
- * http://www.alfresco.org/legal/license.txt
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
- * either express or implied. See the License for the specific
- * language governing permissions and limitations under the
- * License.
- */
-package org.alfresco.repo.domain.hibernate;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import org.alfresco.repo.domain.DbAccessControlEntry;
-import org.alfresco.repo.domain.DbAccessControlList;
-import org.alfresco.repo.domain.DbAuthority;
-import org.alfresco.repo.domain.DbPermission;
-import org.alfresco.repo.domain.DbPermissionKey;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.hibernate.Session;
-
-/**
- * The hibernate persisted class for node permission entries.
- *
- * @author andyh
- */
-public class DbAccessControlListImpl extends LifecycleAdapter
- implements DbAccessControlList, Serializable
-{
- private static final long serialVersionUID = 3123277428227075648L;
-
- private static Log logger = LogFactory.getLog(DbAccessControlListImpl.class);
-
- private long id;
- private Set entries;
- private boolean inherits;
-
- public DbAccessControlListImpl()
- {
- entries = new HashSet(5);
- }
-
- @Override
- public String toString()
- {
- StringBuilder sb = new StringBuilder(128);
- sb.append("DbAccessControlListImpl")
- .append("[ id=").append(id)
- .append(", entries=").append(entries.size())
- .append(", inherits=").append(inherits)
- .append("]");
- return sb.toString();
- }
-
- @Override
- public boolean equals(Object o)
- {
- if (this == o)
- {
- return true;
- }
- if (!(o instanceof DbAccessControlList))
- {
- return false;
- }
- DbAccessControlList other = (DbAccessControlList) o;
-
- return (this.inherits == other.getInherits());
- }
-
- @Override
- public int hashCode()
- {
- return (inherits == false ? 0 : 17);
- }
-
- public long getId()
- {
- return id;
- }
-
- /**
- * Hibernate use
- */
- @SuppressWarnings("unused")
- private void setId(long id)
- {
- this.id = id;
- }
-
- public Set getEntries()
- {
- return entries;
- }
-
- /**
- * For Hibernate use
- */
- @SuppressWarnings("unused")
- private void setEntries(Set entries)
- {
- this.entries = entries;
- }
-
- public boolean getInherits()
- {
- return inherits;
- }
-
- public void setInherits(boolean inherits)
- {
- this.inherits = inherits;
- }
-
- /**
- * @see #deleteEntry(String, DbPermissionKey)
- */
- public int deleteEntriesForAuthority(String authority)
- {
- return deleteEntry(authority, null);
- }
-
- /**
- * @see #deleteEntry(String, DbPermissionKey)
- */
- public int deleteEntriesForPermission(DbPermissionKey permissionKey)
- {
- return deleteEntry(null, permissionKey);
- }
-
- public int deleteEntry(String authority, DbPermissionKey permissionKey)
- {
- List toDelete = new ArrayList(2);
- for (DbAccessControlEntry entry : entries)
- {
- if (authority != null && !authority.equals(entry.getAuthority().getRecipient()))
- {
- // authority is not a match
- continue;
- }
- else if (permissionKey != null && !permissionKey.equals(entry.getPermission().getKey()))
- {
- // permission is not a match
- continue;
- }
- toDelete.add(entry);
- }
- // delete them
- for (DbAccessControlEntry entry : toDelete)
- {
- // remove from the entry list
- entry.delete();
- }
- // done
- if (logger.isDebugEnabled())
- {
- logger.debug("Deleted " + toDelete.size() + " access entries: \n" +
- " access control list: " + id + "\n" +
- " authority: " + authority + "\n" +
- " permission: " + permissionKey);
- }
- return toDelete.size();
- }
-
- public int deleteEntries()
- {
- /*
- * We don't do the full delete-remove-from-set thing here. Just delete each child entity
- * and then clear the entry set.
- */
-
- Session session = getSession();
- List toDelete = new ArrayList(entries);
- // delete each entry
- for (DbAccessControlEntry entry : toDelete)
- {
- session.delete(entry);
- }
- // clear the list
- int count = entries.size();
- entries.clear();
- // done
- if (logger.isDebugEnabled())
- {
- logger.debug("Deleted " + count + " access entries for access control list " + this.id);
- }
- return count;
- }
-
- public DbAccessControlEntry getEntry(String authority, DbPermissionKey permissionKey)
- {
- for (DbAccessControlEntry entry : entries)
- {
- DbAuthority authorityEntity = entry.getAuthority();
- DbPermission permissionEntity = entry.getPermission();
- // check for a match
- if (authorityEntity.getRecipient().equals(authority)
- && permissionEntity.getKey().equals(permissionKey))
- {
- // found it
- return entry;
- }
- }
- return null;
- }
-
- public DbAccessControlEntryImpl newEntry(DbPermission permission, DbAuthority authority, boolean allowed)
- {
- DbAccessControlEntryImpl accessControlEntry = new DbAccessControlEntryImpl();
- // fill
- accessControlEntry.setAccessControlList(this);
- accessControlEntry.setPermission(permission);
- accessControlEntry.setAuthority(authority);
- accessControlEntry.setAllowed(allowed);
- // save it
- getSession().save(accessControlEntry);
- // maintain inverse set on the acl
- getEntries().add(accessControlEntry);
- // done
- return accessControlEntry;
- }
-
- /**
- * Make a copy of this ACL.
- * @return The copy.
- */
- public DbAccessControlList getCopy()
- {
- DbAccessControlList newAcl =
- new DbAccessControlListImpl();
- getSession().save(newAcl);
- for (DbAccessControlEntry entry : entries)
- {
- newAcl.newEntry(entry.getPermission(), entry.getAuthority(), entry.isAllowed());
- }
- return newAcl;
- }
-}
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.domain.hibernate;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.alfresco.repo.domain.DbAccessControlEntry;
+import org.alfresco.repo.domain.DbAccessControlList;
+import org.alfresco.repo.domain.DbAuthority;
+import org.alfresco.repo.domain.DbPermission;
+import org.alfresco.repo.domain.DbPermissionKey;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.Session;
+
+/**
+ * The hibernate persisted class for node permission entries.
+ *
+ * @author andyh
+ */
+public class DbAccessControlListImpl extends LifecycleAdapter
+ implements DbAccessControlList, Serializable
+{
+ private static final long serialVersionUID = 3123277428227075648L;
+
+ private static Log logger = LogFactory.getLog(DbAccessControlListImpl.class);
+
+ private long id;
+ private Set entries;
+ private boolean inherits;
+
+ public DbAccessControlListImpl()
+ {
+ entries = new HashSet(5);
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("DbAccessControlListImpl")
+ .append("[ id=").append(id)
+ .append(", entries=").append(entries.size())
+ .append(", inherits=").append(inherits)
+ .append("]");
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o)
+ {
+ return true;
+ }
+ if (!(o instanceof DbAccessControlList))
+ {
+ return false;
+ }
+ DbAccessControlList other = (DbAccessControlList) o;
+
+ return (this.inherits == other.getInherits());
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return (inherits == false ? 0 : 17);
+ }
+
+ public long getId()
+ {
+ return id;
+ }
+
+ /**
+ * Hibernate use
+ */
+ @SuppressWarnings("unused")
+ private void setId(long id)
+ {
+ this.id = id;
+ }
+
+ public Set getEntries()
+ {
+ return entries;
+ }
+
+ /**
+ * For Hibernate use
+ */
+ @SuppressWarnings("unused")
+ private void setEntries(Set entries)
+ {
+ this.entries = entries;
+ }
+
+ public boolean getInherits()
+ {
+ return inherits;
+ }
+
+ public void setInherits(boolean inherits)
+ {
+ this.inherits = inherits;
+ }
+
+ /**
+ * @see #deleteEntry(String, DbPermissionKey)
+ */
+ public int deleteEntriesForAuthority(String authority)
+ {
+ return deleteEntry(authority, null);
+ }
+
+ /**
+ * @see #deleteEntry(String, DbPermissionKey)
+ */
+ public int deleteEntriesForPermission(DbPermissionKey permissionKey)
+ {
+ return deleteEntry(null, permissionKey);
+ }
+
+ public int deleteEntry(String authority, DbPermissionKey permissionKey)
+ {
+ List toDelete = new ArrayList(2);
+ for (DbAccessControlEntry entry : entries)
+ {
+ if (authority != null && !authority.equals(entry.getAuthority().getRecipient()))
+ {
+ // authority is not a match
+ continue;
+ }
+ else if (permissionKey != null && !permissionKey.equals(entry.getPermission().getKey()))
+ {
+ // permission is not a match
+ continue;
+ }
+ toDelete.add(entry);
+ }
+ // delete them
+ for (DbAccessControlEntry entry : toDelete)
+ {
+ // remove from the entry list
+ entry.delete();
+ }
+ // done
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Deleted " + toDelete.size() + " access entries: \n" +
+ " access control list: " + id + "\n" +
+ " authority: " + authority + "\n" +
+ " permission: " + permissionKey);
+ }
+ return toDelete.size();
+ }
+
+ public int deleteEntries()
+ {
+ /*
+ * We don't do the full delete-remove-from-set thing here. Just delete each child entity
+ * and then clear the entry set.
+ */
+
+ Session session = getSession();
+ List toDelete = new ArrayList(entries);
+ // delete each entry
+ for (DbAccessControlEntry entry : toDelete)
+ {
+ session.delete(entry);
+ }
+ // clear the list
+ int count = entries.size();
+ entries.clear();
+ // done
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Deleted " + count + " access entries for access control list " + this.id);
+ }
+ return count;
+ }
+
+ public DbAccessControlEntry getEntry(String authority, DbPermissionKey permissionKey)
+ {
+ for (DbAccessControlEntry entry : entries)
+ {
+ DbAuthority authorityEntity = entry.getAuthority();
+ DbPermission permissionEntity = entry.getPermission();
+ // check for a match
+ if (authorityEntity.getRecipient().equals(authority)
+ && permissionEntity.getKey().equals(permissionKey))
+ {
+ // found it
+ return entry;
+ }
+ }
+ return null;
+ }
+
+ public DbAccessControlEntryImpl newEntry(DbPermission permission, DbAuthority authority, boolean allowed)
+ {
+ DbAccessControlEntryImpl accessControlEntry = new DbAccessControlEntryImpl();
+ // fill
+ accessControlEntry.setAccessControlList(this);
+ accessControlEntry.setPermission(permission);
+ accessControlEntry.setAuthority(authority);
+ accessControlEntry.setAllowed(allowed);
+ // save it
+ getSession().save(accessControlEntry);
+ // maintain inverse set on the acl
+ getEntries().add(accessControlEntry);
+ // done
+ return accessControlEntry;
+ }
+
+ /**
+ * Make a copy of this ACL.
+ * @return The copy.
+ */
+ public DbAccessControlList getCopy()
+ {
+ DbAccessControlList newAcl =
+ new DbAccessControlListImpl();
+ getSession().save(newAcl);
+ for (DbAccessControlEntry entry : entries)
+ {
+ newAcl.newEntry(entry.getPermission(), entry.getAuthority(), entry.isAllowed());
+ }
+ return newAcl;
+ }
+}
diff --git a/source/java/org/alfresco/repo/domain/hibernate/HibernateNodeTest.java b/source/java/org/alfresco/repo/domain/hibernate/HibernateNodeTest.java
index fc9f9535ba..1419a3c413 100644
--- a/source/java/org/alfresco/repo/domain/hibernate/HibernateNodeTest.java
+++ b/source/java/org/alfresco/repo/domain/hibernate/HibernateNodeTest.java
@@ -1,456 +1,456 @@
-/*
- * Copyright (C) 2005 Alfresco, Inc.
- *
- * Licensed under the Mozilla Public License version 1.1
- * with a permitted attribution clause. You may obtain a
- * copy of the License at
- *
- * http://www.alfresco.org/legal/license.txt
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
- * either express or implied. See the License for the specific
- * language governing permissions and limitations under the
- * License.
- */
-package org.alfresco.repo.domain.hibernate;
-
-import java.io.Serializable;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-import javax.transaction.UserTransaction;
-
-import org.alfresco.model.ContentModel;
-import org.alfresco.repo.domain.ChildAssoc;
-import org.alfresco.repo.domain.Node;
-import org.alfresco.repo.domain.NodeKey;
-import org.alfresco.repo.domain.NodeStatus;
-import org.alfresco.repo.domain.PropertyValue;
-import org.alfresco.repo.domain.Server;
-import org.alfresco.repo.domain.Store;
-import org.alfresco.repo.domain.StoreKey;
-import org.alfresco.repo.domain.Transaction;
-import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
-import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
-import org.alfresco.service.cmr.repository.StoreRef;
-import org.alfresco.service.namespace.QName;
-import org.alfresco.service.transaction.TransactionService;
-import org.alfresco.util.BaseSpringTest;
-import org.alfresco.util.GUID;
-import org.hibernate.CacheMode;
-import org.hibernate.exception.ConstraintViolationException;
-
-/**
- * Test persistence and retrieval of Hibernate-specific implementations of the
- * {@link org.alfresco.repo.domain.Node} interface
- *
- * @author Derek Hulley
- */
-@SuppressWarnings("unused")
-public class HibernateNodeTest extends BaseSpringTest
-{
- private static final String TEST_NAMESPACE = "http://www.alfresco.org/test/HibernateNodeTest";
- private static int i = 0;
-
- private Store store;
- private Server server;
- private Transaction transaction;
-
- public HibernateNodeTest()
- {
- }
-
- protected void onSetUpInTransaction() throws Exception
- {
- store = new StoreImpl();
- StoreKey storeKey = new StoreKey(StoreRef.PROTOCOL_WORKSPACE,
- "TestWorkspace@" + System.currentTimeMillis() + " - " + System.nanoTime());
- store.setKey(storeKey);
- // persist so that it is present in the hibernate cache
- getSession().save(store);
-
- server = (Server) getSession().get(ServerImpl.class, new Long(1));
- if (server == null)
- {
- server = new ServerImpl();
- server.setIpAddress("" + "i_" + System.currentTimeMillis());
- getSession().save(server);
- }
- transaction = new TransactionImpl();
- transaction.setServer(server);
- transaction.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId());
- getSession().save(transaction);
- }
-
- protected void onTearDownInTransaction()
- {
- // force a flush to ensure that the database updates succeed
- getSession().flush();
- getSession().clear();
- }
-
- public void testSetUp() throws Exception
- {
- assertNotNull("Workspace not initialised", store);
- }
-
- public void testGetStore() throws Exception
- {
- // create a new Node
- Node node = new NodeImpl();
- node.setStore(store);
- node.setUuid(GUID.generate());
- node.setTypeQName(ContentModel.TYPE_CONTAINER);
-
- // now it should work
- Serializable id = getSession().save(node);
-
- // throw the reference away and get the a new one for the id
- node = (Node) getSession().load(NodeImpl.class, id);
- assertNotNull("Node not found", node);
- // check that the store has been loaded
- Store loadedStore = node.getStore();
- assertNotNull("Store not present on node", loadedStore);
- assertEquals("Incorrect store key", store, loadedStore);
- }
-
- public void testNodeStatus()
- {
- NodeKey key = new NodeKey(store.getKey(), "AAA");
- // create the node status
- NodeStatus nodeStatus = new NodeStatusImpl();
- nodeStatus.setKey(key);
- nodeStatus.setTransaction(transaction);
- getSession().save(nodeStatus);
-
- // create a new Node
- Node node = new NodeImpl();
- node.setStore(store);
- node.setUuid(GUID.generate());
- node.setTypeQName(ContentModel.TYPE_CONTAINER);
- Serializable nodeId = getSession().save(node);
-
- // This should all be fine. The node does not HAVE to have a status.
- flushAndClear();
-
- // set the node
- nodeStatus = (NodeStatus) getSession().get(NodeStatusImpl.class, key);
- nodeStatus.setNode(node);
- flushAndClear();
-
- // is the node retrievable?
- nodeStatus = (NodeStatus) getSession().get(NodeStatusImpl.class, key);
- node = nodeStatus.getNode();
- assertNotNull("Node was not attached to status", node);
- // change the values
- transaction.setChangeTxnId("txn:456");
- // delete the node
- getSession().delete(node);
-
- try
- {
- flushAndClear();
- fail("Node status may not refer to non-existent node");
- }
- catch(ConstraintViolationException e)
- {
- // expected
- }
- }
-
- /**
- * Check that properties can be persisted and retrieved
- */
- public void testProperties() throws Exception
- {
- // create a new Node
- Node node = new NodeImpl();
- node.setStore(store);
- node.setUuid(GUID.generate());
- node.setTypeQName(ContentModel.TYPE_CONTAINER);
- // give it a property map
- Map propertyMap = new HashMap(5);
- QName propertyQName = QName.createQName("{}A");
- PropertyValue propertyValue = new PropertyValue(DataTypeDefinition.TEXT, "AAA");
- propertyMap.put(propertyQName, propertyValue);
- node.getProperties().putAll(propertyMap);
- // persist it
- Serializable id = getSession().save(node);
-
- // throw the reference away and get the a new one for the id
- node = (Node) getSession().load(NodeImpl.class, id);
- assertNotNull("Node not found", node);
- // extract the Map
- propertyMap = node.getProperties();
- assertNotNull("Map not persisted", propertyMap);
- // ensure that the value is present
- assertNotNull("Property value not present in map", QName.createQName("{}A"));
- }
-
- /**
- * Check that aspect qnames can be added and removed from a node and that they
- * are persisted correctly
- */
- public void testAspects() throws Exception
- {
- // make a real node
- Node node = new NodeImpl();
- node.setStore(store);
- node.setUuid(GUID.generate());
- node.setTypeQName(ContentModel.TYPE_CMOBJECT);
-
- // add some aspects
- QName aspect1 = QName.createQName(TEST_NAMESPACE, "1");
- QName aspect2 = QName.createQName(TEST_NAMESPACE, "2");
- QName aspect3 = QName.createQName(TEST_NAMESPACE, "3");
- QName aspect4 = QName.createQName(TEST_NAMESPACE, "4");
- Set aspects = node.getAspects();
- aspects.add(aspect1);
- aspects.add(aspect2);
- aspects.add(aspect3);
- aspects.add(aspect4);
- assertFalse("Set did not eliminate duplicate aspect qname", aspects.add(aspect4));
-
- // persist
- Serializable id = getSession().save(node);
-
- // flush and clear
- flushAndClear();
-
- // get node and check aspects
- node = (Node) getSession().get(NodeImpl.class, id);
- assertNotNull("Node not persisted", node);
- aspects = node.getAspects();
- assertEquals("Not all aspects persisted", 4, aspects.size());
- }
-
- public void testChildAssoc() throws Exception
- {
- // make a content node
- Node contentNode = new NodeImpl();
- contentNode.setStore(store);
- contentNode.setUuid(GUID.generate());
- contentNode.setTypeQName(ContentModel.TYPE_CONTENT);
- Serializable contentNodeId = getSession().save(contentNode);
-
- // make a container node
- Node containerNode = new NodeImpl();
- containerNode.setStore(store);
- containerNode.setUuid(GUID.generate());
- containerNode.setTypeQName(ContentModel.TYPE_CONTAINER);
- Serializable containerNodeId = getSession().save(containerNode);
- // create an association to the content
- ChildAssoc assoc1 = new ChildAssocImpl();
- assoc1.setIsPrimary(true);
- assoc1.setTypeQName(QName.createQName(null, "type1"));
- assoc1.setQname(QName.createQName(null, "number1"));
- assoc1.setChildNodeName("number1");
- assoc1.setChildNodeNameCrc(1);
- assoc1.buildAssociation(containerNode, contentNode);
- getSession().save(assoc1);
-
- // make another association between the same two parent and child nodes
- ChildAssoc assoc2 = new ChildAssocImpl();
- assoc2.setIsPrimary(true);
- assoc2.setTypeQName(QName.createQName(null, "type2"));
- assoc2.setQname(QName.createQName(null, "number2"));
- assoc2.setChildNodeName("number2");
- assoc2.setChildNodeNameCrc(2);
- assoc2.buildAssociation(containerNode, contentNode);
- getSession().save(assoc2);
-
- assertFalse("Hashcode incorrent", assoc2.hashCode() == 0);
- assertNotSame("Assoc equals failure", assoc1, assoc2);
-
- // reload the container
- containerNode = (Node) getSession().get(NodeImpl.class, containerNodeId);
- assertNotNull("Node not found", containerNode);
-
- // check that we can traverse the association from the child
- Collection parentAssocs = contentNode.getParentAssocs();
- assertEquals("Expected exactly 2 parent assocs", 2, parentAssocs.size());
- parentAssocs = new HashSet(parentAssocs);
- for (ChildAssoc assoc : parentAssocs)
- {
- // maintain inverse assoc sets
- assoc.removeAssociation();
- // remove the assoc
- getSession().delete(assoc);
- }
-
- // check that the child now has zero parents
- parentAssocs = contentNode.getParentAssocs();
- assertEquals("Expected exactly 0 parent assocs", 0, parentAssocs.size());
- }
-
- /**
- * Allows tracing of L2 cache
- */
- public void testCaching() throws Exception
- {
- // make a node
- Node node = new NodeImpl();
- node.setStore(store);
- node.setUuid(GUID.generate());
- node.setTypeQName(ContentModel.TYPE_CONTENT);
- Serializable nodeId = getSession().save(node);
-
- // add some aspects to the node
- Set aspects = node.getAspects();
- aspects.add(ContentModel.ASPECT_AUDITABLE);
-
- // add some properties
- Map properties = node.getProperties();
- properties.put(ContentModel.PROP_NAME, new PropertyValue(DataTypeDefinition.TEXT, "ABC"));
-
- // check that the session hands back the same instance
- Node checkNode = (Node) getSession().get(NodeImpl.class, nodeId);
- assertNotNull(checkNode);
- assertTrue("Node retrieved was not same instance", checkNode == node);
-
- Set checkAspects = checkNode.getAspects();
- assertTrue("Aspect set retrieved was not the same instance", checkAspects == aspects);
- assertEquals("Incorrect number of aspects", 1, checkAspects.size());
- QName checkQName = (QName) checkAspects.toArray()[0];
- assertTrue("QName retrieved was not the same instance", checkQName == ContentModel.ASPECT_AUDITABLE);
-
- Map checkProperties = checkNode.getProperties();
- assertTrue("Propery map retrieved was not the same instance", checkProperties == properties);
- assertTrue("Property not found", checkProperties.containsKey(ContentModel.PROP_NAME));
-
- flushAndClear();
- // commit the transaction
- setComplete();
- endTransaction();
-
- TransactionService transactionService = (TransactionService) applicationContext.getBean("transactionComponent");
- UserTransaction txn = transactionService.getUserTransaction();
- try
- {
- txn.begin();
-
- // check that the L2 cache hands back the same instance
- checkNode = (Node) getSession().get(NodeImpl.class, nodeId);
- assertNotNull(checkNode);
- checkAspects = checkNode.getAspects();
-
- txn.commit();
- }
- catch (Throwable e)
- {
- txn.rollback();
- }
- }
-
- /**
- * Create some simple parent-child relationships and flush them. Then read them back in without
- * using the L2 cache.
- */
- public void testQueryJoins() throws Exception
- {
- getSession().setCacheMode(CacheMode.IGNORE);
-
- // make a container node
- Node containerNode = new NodeImpl();
- containerNode.setStore(store);
- containerNode.setUuid(GUID.generate());
- containerNode.setTypeQName(ContentModel.TYPE_CONTAINER);
- containerNode.getProperties().put(ContentModel.PROP_AUTHOR, new PropertyValue(DataTypeDefinition.TEXT, "ABC"));
- containerNode.getProperties().put(ContentModel.PROP_ARCHIVED_BY, new PropertyValue(DataTypeDefinition.TEXT, "ABC"));
- containerNode.getAspects().add(ContentModel.ASPECT_AUDITABLE);
- Serializable containerNodeId = getSession().save(containerNode);
- NodeKey containerNodeKey = new NodeKey(containerNode.getNodeRef());
- NodeStatus containerNodeStatus = new NodeStatusImpl();
- containerNodeStatus.setKey(containerNodeKey);
- containerNodeStatus.setNode(containerNode);
- containerNodeStatus.setTransaction(transaction);
- getSession().save(containerNodeStatus);
- // make content node 1
- Node contentNode1 = new NodeImpl();
- contentNode1.setStore(store);
- contentNode1.setUuid(GUID.generate());
- contentNode1.setTypeQName(ContentModel.TYPE_CONTENT);
- contentNode1.getProperties().put(ContentModel.PROP_AUTHOR, new PropertyValue(DataTypeDefinition.TEXT, "ABC"));
- contentNode1.getProperties().put(ContentModel.PROP_ARCHIVED_BY, new PropertyValue(DataTypeDefinition.TEXT, "ABC"));
- contentNode1.getAspects().add(ContentModel.ASPECT_AUDITABLE);
- Serializable contentNode1Id = getSession().save(contentNode1);
- NodeKey contentNodeKey1 = new NodeKey(contentNode1.getNodeRef());
- NodeStatus contentNodeStatus1 = new NodeStatusImpl();
- contentNodeStatus1.setKey(contentNodeKey1);
- contentNodeStatus1.setNode(contentNode1);
- contentNodeStatus1.setTransaction(transaction);
- getSession().save(contentNodeStatus1);
- // make content node 2
- Node contentNode2 = new NodeImpl();
- contentNode2.setStore(store);
- contentNode2.setUuid(GUID.generate());
- contentNode2.setTypeQName(ContentModel.TYPE_CONTENT);
- Serializable contentNode2Id = getSession().save(contentNode2);
- contentNode2.getProperties().put(ContentModel.PROP_AUTHOR, new PropertyValue(DataTypeDefinition.TEXT, "ABC"));
- contentNode2.getProperties().put(ContentModel.PROP_ARCHIVED_BY, new PropertyValue(DataTypeDefinition.TEXT, "ABC"));
- contentNode2.getAspects().add(ContentModel.ASPECT_AUDITABLE);
- NodeKey contentNodeKey2 = new NodeKey(contentNode2.getNodeRef());
- NodeStatus contentNodeStatus2 = new NodeStatusImpl();
- contentNodeStatus2.setKey(contentNodeKey2);
- contentNodeStatus2.setNode(contentNode2);
- contentNodeStatus2.setTransaction(transaction);
- getSession().save(contentNodeStatus2);
- // create an association to content 1
- ChildAssoc assoc1 = new ChildAssocImpl();
- assoc1.setIsPrimary(true);
- assoc1.setTypeQName(QName.createQName(null, "type1"));
- assoc1.setQname(QName.createQName(null, "number1"));
- assoc1.setChildNodeName("number1");
- assoc1.setChildNodeNameCrc(1);
- assoc1.buildAssociation(containerNode, contentNode1);
- getSession().save(assoc1);
- // create an association to content 2
- ChildAssoc assoc2 = new ChildAssocImpl();
- assoc2.setIsPrimary(true);
- assoc2.setTypeQName(QName.createQName(null, "type2"));
- assoc2.setQname(QName.createQName(null, "number2"));
- assoc2.setChildNodeName("number2");
- assoc2.setChildNodeNameCrc(2);
- assoc2.buildAssociation(containerNode, contentNode2);
- getSession().save(assoc2);
-
- // make sure that there are no entities cached in either L1 or L2
- getSession().flush();
- getSession().clear();
-
- // now read the structure back in from the container down
- containerNodeStatus = (NodeStatus) getSession().get(NodeStatusImpl.class, containerNodeKey);
- containerNode = containerNodeStatus.getNode();
-
- // clear out again
- getSession().clear();
-
- // expect that just the specific property gets removed in the delete statement
- getSession().flush();
- getSession().clear();
-
- // Create a second association to content 2
- // create an association to content 2
- containerNodeStatus = (NodeStatus) getSession().get(NodeStatusImpl.class, containerNodeKey);
- containerNode = containerNodeStatus.getNode();
- contentNodeStatus2 = (NodeStatus) getSession().get(NodeStatusImpl.class, contentNodeKey2);
- contentNode2 = contentNodeStatus2.getNode();
- ChildAssoc assoc3 = new ChildAssocImpl();
- assoc3.setIsPrimary(false);
- assoc3.setTypeQName(QName.createQName(null, "type3"));
- assoc3.setQname(QName.createQName(null, "number3"));
- assoc3.setChildNodeName("number3");
- assoc3.setChildNodeNameCrc(2);
- assoc3.buildAssociation(containerNode, contentNode2); // check whether the children are pulled in for this
- getSession().save(assoc3);
-
- // flush it
- getSession().flush();
- getSession().clear();
- }
-}
\ No newline at end of file
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.domain.hibernate;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.transaction.UserTransaction;
+
+import org.alfresco.model.ContentModel;
+import org.alfresco.repo.domain.ChildAssoc;
+import org.alfresco.repo.domain.Node;
+import org.alfresco.repo.domain.NodeKey;
+import org.alfresco.repo.domain.NodeStatus;
+import org.alfresco.repo.domain.PropertyValue;
+import org.alfresco.repo.domain.Server;
+import org.alfresco.repo.domain.Store;
+import org.alfresco.repo.domain.StoreKey;
+import org.alfresco.repo.domain.Transaction;
+import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
+import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
+import org.alfresco.service.cmr.repository.StoreRef;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.service.transaction.TransactionService;
+import org.alfresco.util.BaseSpringTest;
+import org.alfresco.util.GUID;
+import org.hibernate.CacheMode;
+import org.hibernate.exception.ConstraintViolationException;
+
+/**
+ * Test persistence and retrieval of Hibernate-specific implementations of the
+ * {@link org.alfresco.repo.domain.Node} interface
+ *
+ * @author Derek Hulley
+ */
+@SuppressWarnings("unused")
+public class HibernateNodeTest extends BaseSpringTest
+{
+ private static final String TEST_NAMESPACE = "http://www.alfresco.org/test/HibernateNodeTest";
+ private static int i = 0;
+
+ private Store store;
+ private Server server;
+ private Transaction transaction;
+
+ public HibernateNodeTest()
+ {
+ }
+
+ protected void onSetUpInTransaction() throws Exception
+ {
+ store = new StoreImpl();
+ StoreKey storeKey = new StoreKey(StoreRef.PROTOCOL_WORKSPACE,
+ "TestWorkspace@" + System.currentTimeMillis() + " - " + System.nanoTime());
+ store.setKey(storeKey);
+ // persist so that it is present in the hibernate cache
+ getSession().save(store);
+
+ server = (Server) getSession().get(ServerImpl.class, new Long(1));
+ if (server == null)
+ {
+ server = new ServerImpl();
+ server.setIpAddress("" + "i_" + System.currentTimeMillis());
+ getSession().save(server);
+ }
+ transaction = new TransactionImpl();
+ transaction.setServer(server);
+ transaction.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId());
+ getSession().save(transaction);
+ }
+
+ protected void onTearDownInTransaction()
+ {
+ // force a flush to ensure that the database updates succeed
+ getSession().flush();
+ getSession().clear();
+ }
+
+ public void testSetUp() throws Exception
+ {
+ assertNotNull("Workspace not initialised", store);
+ }
+
+ public void testGetStore() throws Exception
+ {
+ // create a new Node
+ Node node = new NodeImpl();
+ node.setStore(store);
+ node.setUuid(GUID.generate());
+ node.setTypeQName(ContentModel.TYPE_CONTAINER);
+
+ // now it should work
+ Serializable id = getSession().save(node);
+
+ // throw the reference away and get the a new one for the id
+ node = (Node) getSession().load(NodeImpl.class, id);
+ assertNotNull("Node not found", node);
+ // check that the store has been loaded
+ Store loadedStore = node.getStore();
+ assertNotNull("Store not present on node", loadedStore);
+ assertEquals("Incorrect store key", store, loadedStore);
+ }
+
+ public void testNodeStatus()
+ {
+ NodeKey key = new NodeKey(store.getKey(), "AAA");
+ // create the node status
+ NodeStatus nodeStatus = new NodeStatusImpl();
+ nodeStatus.setKey(key);
+ nodeStatus.setTransaction(transaction);
+ getSession().save(nodeStatus);
+
+ // create a new Node
+ Node node = new NodeImpl();
+ node.setStore(store);
+ node.setUuid(GUID.generate());
+ node.setTypeQName(ContentModel.TYPE_CONTAINER);
+ Serializable nodeId = getSession().save(node);
+
+ // This should all be fine. The node does not HAVE to have a status.
+ flushAndClear();
+
+ // set the node
+ nodeStatus = (NodeStatus) getSession().get(NodeStatusImpl.class, key);
+ nodeStatus.setNode(node);
+ flushAndClear();
+
+ // is the node retrievable?
+ nodeStatus = (NodeStatus) getSession().get(NodeStatusImpl.class, key);
+ node = nodeStatus.getNode();
+ assertNotNull("Node was not attached to status", node);
+ // change the values
+ transaction.setChangeTxnId("txn:456");
+ // delete the node
+ getSession().delete(node);
+
+ try
+ {
+ flushAndClear();
+ fail("Node status may not refer to non-existent node");
+ }
+ catch(ConstraintViolationException e)
+ {
+ // expected
+ }
+ }
+
+ /**
+ * Check that properties can be persisted and retrieved
+ */
+ public void testProperties() throws Exception
+ {
+ // create a new Node
+ Node node = new NodeImpl();
+ node.setStore(store);
+ node.setUuid(GUID.generate());
+ node.setTypeQName(ContentModel.TYPE_CONTAINER);
+ // give it a property map
+ Map propertyMap = new HashMap(5);
+ QName propertyQName = QName.createQName("{}A");
+ PropertyValue propertyValue = new PropertyValue(DataTypeDefinition.TEXT, "AAA");
+ propertyMap.put(propertyQName, propertyValue);
+ node.getProperties().putAll(propertyMap);
+ // persist it
+ Serializable id = getSession().save(node);
+
+ // throw the reference away and get the a new one for the id
+ node = (Node) getSession().load(NodeImpl.class, id);
+ assertNotNull("Node not found", node);
+ // extract the Map
+ propertyMap = node.getProperties();
+ assertNotNull("Map not persisted", propertyMap);
+ // ensure that the value is present
+ assertNotNull("Property value not present in map", QName.createQName("{}A"));
+ }
+
+ /**
+ * Check that aspect qnames can be added and removed from a node and that they
+ * are persisted correctly
+ */
+ public void testAspects() throws Exception
+ {
+ // make a real node
+ Node node = new NodeImpl();
+ node.setStore(store);
+ node.setUuid(GUID.generate());
+ node.setTypeQName(ContentModel.TYPE_CMOBJECT);
+
+ // add some aspects
+ QName aspect1 = QName.createQName(TEST_NAMESPACE, "1");
+ QName aspect2 = QName.createQName(TEST_NAMESPACE, "2");
+ QName aspect3 = QName.createQName(TEST_NAMESPACE, "3");
+ QName aspect4 = QName.createQName(TEST_NAMESPACE, "4");
+ Set aspects = node.getAspects();
+ aspects.add(aspect1);
+ aspects.add(aspect2);
+ aspects.add(aspect3);
+ aspects.add(aspect4);
+ assertFalse("Set did not eliminate duplicate aspect qname", aspects.add(aspect4));
+
+ // persist
+ Serializable id = getSession().save(node);
+
+ // flush and clear
+ flushAndClear();
+
+ // get node and check aspects
+ node = (Node) getSession().get(NodeImpl.class, id);
+ assertNotNull("Node not persisted", node);
+ aspects = node.getAspects();
+ assertEquals("Not all aspects persisted", 4, aspects.size());
+ }
+
+ public void testChildAssoc() throws Exception
+ {
+ // make a content node
+ Node contentNode = new NodeImpl();
+ contentNode.setStore(store);
+ contentNode.setUuid(GUID.generate());
+ contentNode.setTypeQName(ContentModel.TYPE_CONTENT);
+ Serializable contentNodeId = getSession().save(contentNode);
+
+ // make a container node
+ Node containerNode = new NodeImpl();
+ containerNode.setStore(store);
+ containerNode.setUuid(GUID.generate());
+ containerNode.setTypeQName(ContentModel.TYPE_CONTAINER);
+ Serializable containerNodeId = getSession().save(containerNode);
+ // create an association to the content
+ ChildAssoc assoc1 = new ChildAssocImpl();
+ assoc1.setIsPrimary(true);
+ assoc1.setTypeQName(QName.createQName(null, "type1"));
+ assoc1.setQname(QName.createQName(null, "number1"));
+ assoc1.setChildNodeName("number1");
+ assoc1.setChildNodeNameCrc(1);
+ getSession().save(assoc1);
+ assoc1.buildAssociation(containerNode, contentNode);
+
+ // make another association between the same two parent and child nodes
+ ChildAssoc assoc2 = new ChildAssocImpl();
+ assoc2.setIsPrimary(true);
+ assoc2.setTypeQName(QName.createQName(null, "type2"));
+ assoc2.setQname(QName.createQName(null, "number2"));
+ assoc2.setChildNodeName("number2");
+ assoc2.setChildNodeNameCrc(2);
+ getSession().save(assoc2);
+ assoc2.buildAssociation(containerNode, contentNode);
+
+ assertFalse("Hashcode incorrent", assoc2.hashCode() == 0);
+ assertNotSame("Assoc equals failure", assoc1, assoc2);
+
+ // reload the container
+ containerNode = (Node) getSession().get(NodeImpl.class, containerNodeId);
+ assertNotNull("Node not found", containerNode);
+
+ // check that we can traverse the association from the child
+ Collection parentAssocs = contentNode.getParentAssocs();
+ assertEquals("Expected exactly 2 parent assocs", 2, parentAssocs.size());
+ parentAssocs = new HashSet(parentAssocs);
+ for (ChildAssoc assoc : parentAssocs)
+ {
+ // maintain inverse assoc sets
+ assoc.removeAssociation();
+ // remove the assoc
+ getSession().delete(assoc);
+ }
+
+ // check that the child now has zero parents
+ parentAssocs = contentNode.getParentAssocs();
+ assertEquals("Expected exactly 0 parent assocs", 0, parentAssocs.size());
+ }
+
+ /**
+ * Allows tracing of L2 cache
+ */
+ public void testCaching() throws Exception
+ {
+ // make a node
+ Node node = new NodeImpl();
+ node.setStore(store);
+ node.setUuid(GUID.generate());
+ node.setTypeQName(ContentModel.TYPE_CONTENT);
+ Serializable nodeId = getSession().save(node);
+
+ // add some aspects to the node
+ Set aspects = node.getAspects();
+ aspects.add(ContentModel.ASPECT_AUDITABLE);
+
+ // add some properties
+ Map properties = node.getProperties();
+ properties.put(ContentModel.PROP_NAME, new PropertyValue(DataTypeDefinition.TEXT, "ABC"));
+
+ // check that the session hands back the same instance
+ Node checkNode = (Node) getSession().get(NodeImpl.class, nodeId);
+ assertNotNull(checkNode);
+ assertTrue("Node retrieved was not same instance", checkNode == node);
+
+ Set checkAspects = checkNode.getAspects();
+ assertTrue("Aspect set retrieved was not the same instance", checkAspects == aspects);
+ assertEquals("Incorrect number of aspects", 1, checkAspects.size());
+ QName checkQName = (QName) checkAspects.toArray()[0];
+ assertTrue("QName retrieved was not the same instance", checkQName == ContentModel.ASPECT_AUDITABLE);
+
+ Map checkProperties = checkNode.getProperties();
+ assertTrue("Propery map retrieved was not the same instance", checkProperties == properties);
+ assertTrue("Property not found", checkProperties.containsKey(ContentModel.PROP_NAME));
+
+ flushAndClear();
+ // commit the transaction
+ setComplete();
+ endTransaction();
+
+ TransactionService transactionService = (TransactionService) applicationContext.getBean("transactionComponent");
+ UserTransaction txn = transactionService.getUserTransaction();
+ try
+ {
+ txn.begin();
+
+ // check that the L2 cache hands back the same instance
+ checkNode = (Node) getSession().get(NodeImpl.class, nodeId);
+ assertNotNull(checkNode);
+ checkAspects = checkNode.getAspects();
+
+ txn.commit();
+ }
+ catch (Throwable e)
+ {
+ txn.rollback();
+ }
+ }
+
+ /**
+ * Create some simple parent-child relationships and flush them. Then read them back in without
+ * using the L2 cache.
+ */
+ public void testQueryJoins() throws Exception
+ {
+ getSession().setCacheMode(CacheMode.IGNORE);
+
+ // make a container node
+ Node containerNode = new NodeImpl();
+ containerNode.setStore(store);
+ containerNode.setUuid(GUID.generate());
+ containerNode.setTypeQName(ContentModel.TYPE_CONTAINER);
+ containerNode.getProperties().put(ContentModel.PROP_AUTHOR, new PropertyValue(DataTypeDefinition.TEXT, "ABC"));
+ containerNode.getProperties().put(ContentModel.PROP_ARCHIVED_BY, new PropertyValue(DataTypeDefinition.TEXT, "ABC"));
+ containerNode.getAspects().add(ContentModel.ASPECT_AUDITABLE);
+ Serializable containerNodeId = getSession().save(containerNode);
+ NodeKey containerNodeKey = new NodeKey(containerNode.getNodeRef());
+ NodeStatus containerNodeStatus = new NodeStatusImpl();
+ containerNodeStatus.setKey(containerNodeKey);
+ containerNodeStatus.setNode(containerNode);
+ containerNodeStatus.setTransaction(transaction);
+ getSession().save(containerNodeStatus);
+ // make content node 1
+ Node contentNode1 = new NodeImpl();
+ contentNode1.setStore(store);
+ contentNode1.setUuid(GUID.generate());
+ contentNode1.setTypeQName(ContentModel.TYPE_CONTENT);
+ contentNode1.getProperties().put(ContentModel.PROP_AUTHOR, new PropertyValue(DataTypeDefinition.TEXT, "ABC"));
+ contentNode1.getProperties().put(ContentModel.PROP_ARCHIVED_BY, new PropertyValue(DataTypeDefinition.TEXT, "ABC"));
+ contentNode1.getAspects().add(ContentModel.ASPECT_AUDITABLE);
+ Serializable contentNode1Id = getSession().save(contentNode1);
+ NodeKey contentNodeKey1 = new NodeKey(contentNode1.getNodeRef());
+ NodeStatus contentNodeStatus1 = new NodeStatusImpl();
+ contentNodeStatus1.setKey(contentNodeKey1);
+ contentNodeStatus1.setNode(contentNode1);
+ contentNodeStatus1.setTransaction(transaction);
+ getSession().save(contentNodeStatus1);
+ // make content node 2
+ Node contentNode2 = new NodeImpl();
+ contentNode2.setStore(store);
+ contentNode2.setUuid(GUID.generate());
+ contentNode2.setTypeQName(ContentModel.TYPE_CONTENT);
+ Serializable contentNode2Id = getSession().save(contentNode2);
+ contentNode2.getProperties().put(ContentModel.PROP_AUTHOR, new PropertyValue(DataTypeDefinition.TEXT, "ABC"));
+ contentNode2.getProperties().put(ContentModel.PROP_ARCHIVED_BY, new PropertyValue(DataTypeDefinition.TEXT, "ABC"));
+ contentNode2.getAspects().add(ContentModel.ASPECT_AUDITABLE);
+ NodeKey contentNodeKey2 = new NodeKey(contentNode2.getNodeRef());
+ NodeStatus contentNodeStatus2 = new NodeStatusImpl();
+ contentNodeStatus2.setKey(contentNodeKey2);
+ contentNodeStatus2.setNode(contentNode2);
+ contentNodeStatus2.setTransaction(transaction);
+ getSession().save(contentNodeStatus2);
+ // create an association to content 1
+ ChildAssoc assoc1 = new ChildAssocImpl();
+ assoc1.setIsPrimary(true);
+ assoc1.setTypeQName(QName.createQName(null, "type1"));
+ assoc1.setQname(QName.createQName(null, "number1"));
+ assoc1.setChildNodeName("number1");
+ assoc1.setChildNodeNameCrc(1);
+ assoc1.buildAssociation(containerNode, contentNode1);
+ getSession().save(assoc1);
+ // create an association to content 2
+ ChildAssoc assoc2 = new ChildAssocImpl();
+ assoc2.setIsPrimary(true);
+ assoc2.setTypeQName(QName.createQName(null, "type2"));
+ assoc2.setQname(QName.createQName(null, "number2"));
+ assoc2.setChildNodeName("number2");
+ assoc2.setChildNodeNameCrc(2);
+ assoc2.buildAssociation(containerNode, contentNode2);
+ getSession().save(assoc2);
+
+ // make sure that there are no entities cached in either L1 or L2
+ getSession().flush();
+ getSession().clear();
+
+ // now read the structure back in from the container down
+ containerNodeStatus = (NodeStatus) getSession().get(NodeStatusImpl.class, containerNodeKey);
+ containerNode = containerNodeStatus.getNode();
+
+ // clear out again
+ getSession().clear();
+
+ // expect that just the specific property gets removed in the delete statement
+ getSession().flush();
+ getSession().clear();
+
+ // Create a second association to content 2
+ // create an association to content 2
+ containerNodeStatus = (NodeStatus) getSession().get(NodeStatusImpl.class, containerNodeKey);
+ containerNode = containerNodeStatus.getNode();
+ contentNodeStatus2 = (NodeStatus) getSession().get(NodeStatusImpl.class, contentNodeKey2);
+ contentNode2 = contentNodeStatus2.getNode();
+ ChildAssoc assoc3 = new ChildAssocImpl();
+ assoc3.setIsPrimary(false);
+ assoc3.setTypeQName(QName.createQName(null, "type3"));
+ assoc3.setQname(QName.createQName(null, "number3"));
+ assoc3.setChildNodeName("number3");
+ assoc3.setChildNodeNameCrc(2);
+ assoc3.buildAssociation(containerNode, contentNode2); // check whether the children are pulled in for this
+ getSession().save(assoc3);
+
+ // flush it
+ getSession().flush();
+ getSession().clear();
+ }
+}
diff --git a/source/java/org/alfresco/repo/domain/hibernate/PermissionsDaoComponentImpl.java b/source/java/org/alfresco/repo/domain/hibernate/PermissionsDaoComponentImpl.java
index 8e2a68ccfb..3879739aff 100644
--- a/source/java/org/alfresco/repo/domain/hibernate/PermissionsDaoComponentImpl.java
+++ b/source/java/org/alfresco/repo/domain/hibernate/PermissionsDaoComponentImpl.java
@@ -1,600 +1,600 @@
-/*
- * Copyright (C) 2005 Alfresco, Inc.
- *
- * Licensed under the Mozilla Public License version 1.1
- * with a permitted attribution clause. You may obtain a
- * copy of the License at
- *
- * http://www.alfresco.org/legal/license.txt
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
- * either express or implied. See the License for the specific
- * language governing permissions and limitations under the
- * License.
- */
-package org.alfresco.repo.domain.hibernate;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-import org.alfresco.repo.domain.AccessControlListDAO;
-import org.alfresco.repo.domain.DbAccessControlEntry;
-import org.alfresco.repo.domain.DbAccessControlList;
-import org.alfresco.repo.domain.DbAuthority;
-import org.alfresco.repo.domain.DbPermission;
-import org.alfresco.repo.domain.DbPermissionKey;
-import org.alfresco.repo.security.permissions.NodePermissionEntry;
-import org.alfresco.repo.security.permissions.PermissionEntry;
-import org.alfresco.repo.security.permissions.PermissionReference;
-import org.alfresco.repo.security.permissions.impl.PermissionsDaoComponent;
-import org.alfresco.repo.security.permissions.impl.SimpleNodePermissionEntry;
-import org.alfresco.repo.security.permissions.impl.SimplePermissionEntry;
-import org.alfresco.repo.security.permissions.impl.SimplePermissionReference;
-import org.alfresco.repo.transaction.TransactionalDao;
-import org.alfresco.service.cmr.repository.InvalidNodeRefException;
-import org.alfresco.service.cmr.repository.NodeRef;
-import org.alfresco.service.cmr.security.AccessStatus;
-import org.alfresco.service.namespace.QName;
-import org.alfresco.util.GUID;
-import org.hibernate.Query;
-import org.hibernate.Session;
-import org.springframework.orm.hibernate3.HibernateCallback;
-import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
-
-/**
- * Support for accessing persisted permission information.
- *
- * This class maps between persisted objects and the external API defined in the
- * PermissionsDAO interface.
- *
- * @author andyh
- */
-public class PermissionsDaoComponentImpl extends HibernateDaoSupport implements PermissionsDaoComponent, TransactionalDao
-{
- private static final boolean INHERIT_PERMISSIONS_DEFAULT = true;
- public static final String QUERY_GET_PERMISSION = "permission.GetPermission";
- public static final String QUERY_GET_AC_ENTRIES_FOR_AUTHORITY = "permission.GetAccessControlEntriesForAuthority";
- public static final String QUERY_GET_AC_ENTRIES_FOR_PERMISSION = "permission.GetAccessControlEntriesForPermission";
-
- private Map fProtocolToACLDAO;
-
- private AccessControlListDAO fDefaultACLDAO;
-
- /** a uuid identifying this unique instance */
- private String uuid;
-
- /**
- *
- */
- public PermissionsDaoComponentImpl()
- {
- this.uuid = GUID.generate();
- }
-
- /**
- * Checks equality by type and uuid
- */
- public boolean equals(Object obj)
- {
- if (obj == null)
- {
- return false;
- }
- else if (!(obj instanceof PermissionsDaoComponentImpl))
- {
- return false;
- }
- PermissionsDaoComponentImpl that = (PermissionsDaoComponentImpl) obj;
- return this.uuid.equals(that.uuid);
- }
-
- /**
- * @see #uuid
- */
- public int hashCode()
- {
- return uuid.hashCode();
- }
-
- /**
- * Does this Session contain any changes which must be
- * synchronized with the store?
- *
- * @return true => changes are pending
- */
- public boolean isDirty()
- {
- // create a callback for the task
- HibernateCallback callback = new HibernateCallback()
- {
- public Object doInHibernate(Session session)
- {
- return session.isDirty();
- }
- };
- // execute the callback
- return ((Boolean)getHibernateTemplate().execute(callback)).booleanValue();
- }
-
- /**
- * Just flushes the session
- */
- public void flush()
- {
- getSession().flush();
- }
-
- public void setProtocolToACLDAO(Map map)
- {
- fProtocolToACLDAO = map;
- }
-
- public void setDefaultACLDAO(AccessControlListDAO defaultACLDAO)
- {
- fDefaultACLDAO = defaultACLDAO;
- }
-
- public NodePermissionEntry getPermissions(NodeRef nodeRef)
- {
- // Create the object if it is not found.
- // Null objects are not cached in hibernate
- // If the object does not exist it will repeatedly query to check its
- // non existence.
- NodePermissionEntry npe = null;
- DbAccessControlList acl = null;
- try
- {
- acl = getAccessControlList(nodeRef, false);
- }
- catch (InvalidNodeRefException e)
- {
- // Do nothing.
- }
- if (acl == null)
- {
- // there isn't an access control list for the node - spoof a null one
- SimpleNodePermissionEntry snpe = new SimpleNodePermissionEntry(
- nodeRef,
- true,
- Collections. emptySet());
- npe = snpe;
- }
- else
- {
- npe = createSimpleNodePermissionEntry(nodeRef);
- }
- // done
- if (logger.isDebugEnabled())
- {
- logger.debug(
- "Created access control list for node: \n" +
- " node: " + nodeRef + "\n" +
- " acl: " + npe);
- }
- return npe;
- }
-
- /**
- * Get the persisted access control list or create it if required.
- *
- * @param nodeRef - the node for which to create the list
- * @param create - create the object if it is missing
- * @return Returns the current access control list or null if not found
- */
- private DbAccessControlList getAccessControlList(NodeRef nodeRef, boolean create)
- {
- DbAccessControlList acl =
- getACLDAO(nodeRef).getAccessControlList(nodeRef);
- if (acl == null && create)
- {
- acl = createAccessControlList(nodeRef);
- }
- // done
- if (logger.isDebugEnabled())
- {
- logger.debug("Retrieved access control list: \n" +
- " node: " + nodeRef + "\n" +
- " list: " + acl);
- }
- return acl;
- }
-
- /**
- * Creates an access control list for the node and removes the entry from
- * the nullPermsionCache.
- */
- private DbAccessControlList createAccessControlList(NodeRef nodeRef)
- {
- DbAccessControlList acl = new DbAccessControlListImpl();
- acl.setInherits(INHERIT_PERMISSIONS_DEFAULT);
- getHibernateTemplate().save(acl);
-
- // maintain inverse
- getACLDAO(nodeRef).setAccessControlList(nodeRef, acl);
-
- // done
- if (logger.isDebugEnabled())
- {
- logger.debug("Created Access Control List: \n" +
- " node: " + nodeRef + "\n" +
- " list: " + acl);
- }
- return acl;
- }
-
- public void deletePermissions(NodeRef nodeRef)
- {
- DbAccessControlList acl = null;
- try
- {
- acl = getAccessControlList(nodeRef, false);
- }
- catch (InvalidNodeRefException e)
- {
- return;
- }
- if (acl != null)
- {
- // maintain referencial integrity
- getACLDAO(nodeRef).setAccessControlList(nodeRef, null);
- // delete the access control list - it will cascade to the entries
- getHibernateTemplate().delete(acl);
- }
- }
-
- @SuppressWarnings("unchecked")
- public void deletePermissions(final String authority)
- {
- // get the authority
- HibernateCallback callback = new HibernateCallback()
- {
- public Object doInHibernate(Session session)
- {
- Query query = session
- .getNamedQuery(QUERY_GET_AC_ENTRIES_FOR_AUTHORITY)
- .setString("authorityRecipient", authority);
- return (Integer) HibernateHelper.deleteDbAccessControlEntries(session, query);
- }
- };
- Integer deletedCount = (Integer) getHibernateTemplate().execute(callback);
- // done
- if (logger.isDebugEnabled())
- {
- logger.debug("Deleted " + deletedCount + " entries for authority " + authority);
- }
- }
-
- public void deletePermissions(final NodeRef nodeRef, final String authority)
- {
- DbAccessControlList acl = null;
- try
- {
- acl = getACLDAO(nodeRef).getAccessControlList(nodeRef);
- }
- catch (InvalidNodeRefException e)
- {
- return;
- }
- int deletedCount = 0;
- if (acl != null)
- {
- deletedCount = acl.deleteEntriesForAuthority(authority);
- }
- // done
- if (logger.isDebugEnabled())
- {
- logger.debug("Deleted " + deletedCount + "entries for criteria: \n" +
- " node: " + nodeRef + "\n" +
- " authority: " + authority);
- }
- }
-
- /**
- * Deletes all permission entries (access control list entries) that match
- * the given criteria. Note that the access control list for the node is
- * not deleted.
- */
- public void deletePermission(NodeRef nodeRef, String authority, PermissionReference permission)
- {
- DbAccessControlList acl = null;
- try
- {
- acl = getACLDAO(nodeRef).getAccessControlList(nodeRef);
- }
- catch (InvalidNodeRefException e)
- {
- return;
- }
- int deletedCount = 0;
- if (acl != null)
- {
- DbPermissionKey permissionKey = new DbPermissionKey(permission.getQName(), permission.getName());
- deletedCount = acl.deleteEntry(authority, permissionKey);
- }
- // done
- if (logger.isDebugEnabled())
- {
- logger.debug("Deleted " + deletedCount + "entries for criteria: \n" +
- " node: " + nodeRef + "\n" +
- " permission: " + permission + "\n" +
- " authority: " + authority);
- }
- }
-
- public void setPermission(NodeRef nodeRef, String authority, PermissionReference permission, boolean allow)
- {
- // get the entry
- DbAccessControlEntry entry = getAccessControlEntry(nodeRef, authority, permission);
- if (entry == null)
- {
- // need to create it
- DbAccessControlList dbAccessControlList = getAccessControlList(nodeRef, true);
- DbPermission dbPermission = getPermission(permission, true);
- DbAuthority dbAuthority = getAuthority(authority, true);
- // set persistent objects
- entry = dbAccessControlList.newEntry(dbPermission, dbAuthority, allow);
- // done
- if (logger.isDebugEnabled())
- {
- logger.debug("Created new access control entry: " + entry);
- }
- }
- else
- {
- entry.setAllowed(allow);
- // done
- if (logger.isDebugEnabled())
- {
- logger.debug("Updated access control entry: " + entry);
- }
- }
- }
-
- /**
- * @param nodeRef the node against which to join
- * @param authority the authority against which to join
- * @param perm the permission against which to join
- * @return Returns all access control entries that match the criteria
- */
- private DbAccessControlEntry getAccessControlEntry(
- NodeRef nodeRef,
- String authority,
- PermissionReference permission)
- {
- DbAccessControlList acl = getAccessControlList(nodeRef, false);
- DbAccessControlEntry entry = null;
- if (acl != null)
- {
- DbPermissionKey permissionKey = new DbPermissionKey(permission.getQName(), permission.getName());
- entry = acl.getEntry(authority, permissionKey);
- }
- // done
- if (logger.isDebugEnabled())
- {
- logger.debug("" + (entry == null ? "Did not find" : "Found") + " entry for criteria: \n" +
- " node: " + nodeRef + "\n" +
- " authority: " + authority + "\n" +
- " permission: " + permission);
- }
- return entry;
- }
-
- /**
- * Utility method to find or create a persisted authority
- */
- private DbAuthority getAuthority(String authority, boolean create)
- {
- DbAuthority entity = (DbAuthority) getHibernateTemplate().get(DbAuthorityImpl.class, authority);
- if ((entity == null) && create)
- {
- entity = new DbAuthorityImpl();
- entity.setRecipient(authority);
- getHibernateTemplate().save(entity);
- return entity;
- }
- else
- {
- return entity;
- }
- }
-
- /**
- * Utility method to find and optionally create a persisted permission.
- */
- private DbPermission getPermission(PermissionReference permissionRef, final boolean create)
- {
- final QName qname = permissionRef.getQName();
- final String name = permissionRef.getName();
- Session session = getSession();
-
- DbPermission dbPermission = DbPermissionImpl.find(session, qname, name);
-
- // create if necessary
- if ((dbPermission == null) && create)
- {
- dbPermission = new DbPermissionImpl();
- dbPermission.setTypeQname(qname);
- dbPermission.setName(name);
- getHibernateTemplate().save(dbPermission);
- }
- return dbPermission;
- }
-
- public void setPermission(PermissionEntry permissionEntry)
- {
- setPermission(
- permissionEntry.getNodeRef(),
- permissionEntry.getAuthority(),
- permissionEntry.getPermissionReference(),
- permissionEntry.isAllowed());
- }
-
- public void setPermission(NodePermissionEntry nodePermissionEntry)
- {
- NodeRef nodeRef = nodePermissionEntry.getNodeRef();
-
- // Get the access control list
- // Note the logic here requires to know whether it was created or not
- DbAccessControlList acl = getAccessControlList(nodeRef, false);
- if (acl != null)
- {
- // maintain referencial integrity
- getACLDAO(nodeRef).setAccessControlList(nodeRef, null);
- // drop the list
- getHibernateTemplate().delete(acl);
- }
- // create the access control list
- acl = createAccessControlList(nodeRef);
-
- // set attributes
- acl.setInherits(nodePermissionEntry.inheritPermissions());
-
- // add all entries
- for (PermissionEntry pe : nodePermissionEntry.getPermissionEntries())
- {
- PermissionReference permission = pe.getPermissionReference();
- String authority = pe.getAuthority();
- boolean isAllowed = pe.isAllowed();
-
- DbPermission permissionEntity = getPermission(permission, true);
- DbAuthority authorityEntity = getAuthority(authority, true);
-
- @SuppressWarnings("unused")
- DbAccessControlEntryImpl entry = acl.newEntry(permissionEntity, authorityEntity, isAllowed);
- }
- }
-
- public void setInheritParentPermissions(NodeRef nodeRef, boolean inheritParentPermissions)
- {
- DbAccessControlList acl = null;
- if (!inheritParentPermissions)
- {
- // Inheritance == true is the default, so only force a create of the ACL if the value false
- acl = getAccessControlList(nodeRef, true);
- acl.setInherits(false);
- }
- else
- {
- acl = getAccessControlList(nodeRef, false);
- if (acl != null)
- {
- acl.setInherits(true);
- }
- }
- }
-
- public boolean getInheritParentPermissions(NodeRef nodeRef)
- {
- DbAccessControlList acl = null;
- try
- {
- acl = getAccessControlList(nodeRef, false);
- }
- catch (InvalidNodeRefException e)
- {
- return INHERIT_PERMISSIONS_DEFAULT;
- }
- if (acl == null)
- {
- return true;
- }
- else
- {
- return acl.getInherits();
- }
- }
-
- // Utility methods to create simple detached objects for the outside world
- // We do not pass out the hibernate objects
-
- private SimpleNodePermissionEntry createSimpleNodePermissionEntry(NodeRef nodeRef)
- {
- DbAccessControlList acl =
- getACLDAO(nodeRef).getAccessControlList(nodeRef);
- if (acl == null)
- {
- // there isn't an access control list for the node - spoof a null one
- SimpleNodePermissionEntry snpe = new SimpleNodePermissionEntry(
- nodeRef,
- true,
- Collections. emptySet());
- return snpe;
- }
- else
- {
- Set entries = acl.getEntries();
- SimpleNodePermissionEntry snpe = new SimpleNodePermissionEntry(
- nodeRef,
- acl.getInherits(),
- createSimplePermissionEntries(nodeRef, entries));
- return snpe;
- }
- }
-
- /**
- * @param entries access control entries
- * @return Returns a unique set of entries that can be given back to the outside world
- */
- private Set createSimplePermissionEntries(NodeRef nodeRef,
- Collection entries)
- {
- if (entries == null)
- {
- return null;
- }
- HashSet spes = new HashSet(entries.size(), 1.0f);
- if (entries.size() != 0)
- {
- for (DbAccessControlEntry entry : entries)
- {
- spes.add(createSimplePermissionEntry(nodeRef, entry));
- }
- }
- return spes;
- }
-
- private static SimplePermissionEntry createSimplePermissionEntry(NodeRef nodeRef,
- DbAccessControlEntry ace)
- {
- if (ace == null)
- {
- return null;
- }
- return new SimplePermissionEntry(
- nodeRef,
- createSimplePermissionReference(ace.getPermission()),
- ace.getAuthority().getRecipient(),
- ace.isAllowed() ? AccessStatus.ALLOWED : AccessStatus.DENIED);
- }
-
- private static SimplePermissionReference createSimplePermissionReference(DbPermission perm)
- {
- if (perm == null)
- {
- return null;
- }
- return new SimplePermissionReference(
- perm.getTypeQname(),
- perm.getName());
- }
-
- /**
- * Helper to choose appropriate NodeService for the given NodeRef
- * @param nodeRef The NodeRef to dispatch from.
- * @return The appropriate NodeService.
- */
- private AccessControlListDAO getACLDAO(NodeRef nodeRef)
- {
- AccessControlListDAO ret = fProtocolToACLDAO.get(nodeRef.getStoreRef().getProtocol());
- if (ret == null)
- {
- return fDefaultACLDAO;
- }
- return ret;
- }
-}
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.domain.hibernate;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.alfresco.repo.domain.AccessControlListDAO;
+import org.alfresco.repo.domain.DbAccessControlEntry;
+import org.alfresco.repo.domain.DbAccessControlList;
+import org.alfresco.repo.domain.DbAuthority;
+import org.alfresco.repo.domain.DbPermission;
+import org.alfresco.repo.domain.DbPermissionKey;
+import org.alfresco.repo.security.permissions.NodePermissionEntry;
+import org.alfresco.repo.security.permissions.PermissionEntry;
+import org.alfresco.repo.security.permissions.PermissionReference;
+import org.alfresco.repo.security.permissions.impl.PermissionsDaoComponent;
+import org.alfresco.repo.security.permissions.impl.SimpleNodePermissionEntry;
+import org.alfresco.repo.security.permissions.impl.SimplePermissionEntry;
+import org.alfresco.repo.security.permissions.impl.SimplePermissionReference;
+import org.alfresco.repo.transaction.TransactionalDao;
+import org.alfresco.service.cmr.repository.InvalidNodeRefException;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.security.AccessStatus;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.util.GUID;
+import org.hibernate.Query;
+import org.hibernate.Session;
+import org.springframework.orm.hibernate3.HibernateCallback;
+import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
+
+/**
+ * Support for accessing persisted permission information.
+ *
+ * This class maps between persisted objects and the external API defined in the
+ * PermissionsDAO interface.
+ *
+ * @author andyh
+ */
+public class PermissionsDaoComponentImpl extends HibernateDaoSupport implements PermissionsDaoComponent, TransactionalDao
+{
+ private static final boolean INHERIT_PERMISSIONS_DEFAULT = true;
+ public static final String QUERY_GET_PERMISSION = "permission.GetPermission";
+ public static final String QUERY_GET_AC_ENTRIES_FOR_AUTHORITY = "permission.GetAccessControlEntriesForAuthority";
+ public static final String QUERY_GET_AC_ENTRIES_FOR_PERMISSION = "permission.GetAccessControlEntriesForPermission";
+
+ private Map fProtocolToACLDAO;
+
+ private AccessControlListDAO fDefaultACLDAO;
+
+ /** a uuid identifying this unique instance */
+ private String uuid;
+
+ /**
+ *
+ */
+ public PermissionsDaoComponentImpl()
+ {
+ this.uuid = GUID.generate();
+ }
+
+ /**
+ * Checks equality by type and uuid
+ */
+ public boolean equals(Object obj)
+ {
+ if (obj == null)
+ {
+ return false;
+ }
+ else if (!(obj instanceof PermissionsDaoComponentImpl))
+ {
+ return false;
+ }
+ PermissionsDaoComponentImpl that = (PermissionsDaoComponentImpl) obj;
+ return this.uuid.equals(that.uuid);
+ }
+
+ /**
+ * @see #uuid
+ */
+ public int hashCode()
+ {
+ return uuid.hashCode();
+ }
+
+ /**
+ * Does this Session contain any changes which must be
+ * synchronized with the store?
+ *
+ * @return true => changes are pending
+ */
+ public boolean isDirty()
+ {
+ // create a callback for the task
+ HibernateCallback callback = new HibernateCallback()
+ {
+ public Object doInHibernate(Session session)
+ {
+ return session.isDirty();
+ }
+ };
+ // execute the callback
+ return ((Boolean)getHibernateTemplate().execute(callback)).booleanValue();
+ }
+
+ /**
+ * Just flushes the session
+ */
+ public void flush()
+ {
+ getSession().flush();
+ }
+
+ public void setProtocolToACLDAO(Map map)
+ {
+ fProtocolToACLDAO = map;
+ }
+
+ public void setDefaultACLDAO(AccessControlListDAO defaultACLDAO)
+ {
+ fDefaultACLDAO = defaultACLDAO;
+ }
+
+ public NodePermissionEntry getPermissions(NodeRef nodeRef)
+ {
+ // Create the object if it is not found.
+ // Null objects are not cached in hibernate
+ // If the object does not exist it will repeatedly query to check its
+ // non existence.
+ NodePermissionEntry npe = null;
+ DbAccessControlList acl = null;
+ try
+ {
+ acl = getAccessControlList(nodeRef, false);
+ }
+ catch (InvalidNodeRefException e)
+ {
+ // Do nothing.
+ }
+ if (acl == null)
+ {
+ // there isn't an access control list for the node - spoof a null one
+ SimpleNodePermissionEntry snpe = new SimpleNodePermissionEntry(
+ nodeRef,
+ true,
+ Collections. emptySet());
+ npe = snpe;
+ }
+ else
+ {
+ npe = createSimpleNodePermissionEntry(nodeRef);
+ }
+ // done
+ if (logger.isDebugEnabled())
+ {
+ logger.debug(
+ "Created access control list for node: \n" +
+ " node: " + nodeRef + "\n" +
+ " acl: " + npe);
+ }
+ return npe;
+ }
+
+ /**
+ * Get the persisted access control list or create it if required.
+ *
+ * @param nodeRef - the node for which to create the list
+ * @param create - create the object if it is missing
+ * @return Returns the current access control list or null if not found
+ */
+ private DbAccessControlList getAccessControlList(NodeRef nodeRef, boolean create)
+ {
+ DbAccessControlList acl =
+ getACLDAO(nodeRef).getAccessControlList(nodeRef);
+ if (acl == null && create)
+ {
+ acl = createAccessControlList(nodeRef);
+ }
+ // done
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Retrieved access control list: \n" +
+ " node: " + nodeRef + "\n" +
+ " list: " + acl);
+ }
+ return acl;
+ }
+
+ /**
+ * Creates an access control list for the node and removes the entry from
+ * the nullPermsionCache.
+ */
+ private DbAccessControlList createAccessControlList(NodeRef nodeRef)
+ {
+ DbAccessControlList acl = new DbAccessControlListImpl();
+ acl.setInherits(INHERIT_PERMISSIONS_DEFAULT);
+ getHibernateTemplate().save(acl);
+
+ // maintain inverse
+ getACLDAO(nodeRef).setAccessControlList(nodeRef, acl);
+
+ // done
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Created Access Control List: \n" +
+ " node: " + nodeRef + "\n" +
+ " list: " + acl);
+ }
+ return acl;
+ }
+
+ public void deletePermissions(NodeRef nodeRef)
+ {
+ DbAccessControlList acl = null;
+ try
+ {
+ acl = getAccessControlList(nodeRef, false);
+ }
+ catch (InvalidNodeRefException e)
+ {
+ return;
+ }
+ if (acl != null)
+ {
+ // maintain referencial integrity
+ getACLDAO(nodeRef).setAccessControlList(nodeRef, null);
+ // delete the access control list - it will cascade to the entries
+ getHibernateTemplate().delete(acl);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public void deletePermissions(final String authority)
+ {
+ // get the authority
+ HibernateCallback callback = new HibernateCallback()
+ {
+ public Object doInHibernate(Session session)
+ {
+ Query query = session
+ .getNamedQuery(QUERY_GET_AC_ENTRIES_FOR_AUTHORITY)
+ .setString("authorityRecipient", authority);
+ return (Integer) HibernateHelper.deleteDbAccessControlEntries(session, query);
+ }
+ };
+ Integer deletedCount = (Integer) getHibernateTemplate().execute(callback);
+ // done
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Deleted " + deletedCount + " entries for authority " + authority);
+ }
+ }
+
+ public void deletePermissions(final NodeRef nodeRef, final String authority)
+ {
+ DbAccessControlList acl = null;
+ try
+ {
+ acl = getACLDAO(nodeRef).getAccessControlList(nodeRef);
+ }
+ catch (InvalidNodeRefException e)
+ {
+ return;
+ }
+ int deletedCount = 0;
+ if (acl != null)
+ {
+ deletedCount = acl.deleteEntriesForAuthority(authority);
+ }
+ // done
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Deleted " + deletedCount + "entries for criteria: \n" +
+ " node: " + nodeRef + "\n" +
+ " authority: " + authority);
+ }
+ }
+
+ /**
+ * Deletes all permission entries (access control list entries) that match
+ * the given criteria. Note that the access control list for the node is
+ * not deleted.
+ */
+ public void deletePermission(NodeRef nodeRef, String authority, PermissionReference permission)
+ {
+ DbAccessControlList acl = null;
+ try
+ {
+ acl = getACLDAO(nodeRef).getAccessControlList(nodeRef);
+ }
+ catch (InvalidNodeRefException e)
+ {
+ return;
+ }
+ int deletedCount = 0;
+ if (acl != null)
+ {
+ DbPermissionKey permissionKey = new DbPermissionKey(permission.getQName(), permission.getName());
+ deletedCount = acl.deleteEntry(authority, permissionKey);
+ }
+ // done
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Deleted " + deletedCount + "entries for criteria: \n" +
+ " node: " + nodeRef + "\n" +
+ " permission: " + permission + "\n" +
+ " authority: " + authority);
+ }
+ }
+
+ public void setPermission(NodeRef nodeRef, String authority, PermissionReference permission, boolean allow)
+ {
+ // get the entry
+ DbAccessControlEntry entry = getAccessControlEntry(nodeRef, authority, permission);
+ if (entry == null)
+ {
+ // need to create it
+ DbAccessControlList dbAccessControlList = getAccessControlList(nodeRef, true);
+ DbPermission dbPermission = getPermission(permission, true);
+ DbAuthority dbAuthority = getAuthority(authority, true);
+ // set persistent objects
+ entry = dbAccessControlList.newEntry(dbPermission, dbAuthority, allow);
+ // done
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Created new access control entry: " + entry);
+ }
+ }
+ else
+ {
+ entry.setAllowed(allow);
+ // done
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Updated access control entry: " + entry);
+ }
+ }
+ }
+
+ /**
+ * @param nodeRef the node against which to join
+ * @param authority the authority against which to join
+ * @param perm the permission against which to join
+ * @return Returns all access control entries that match the criteria
+ */
+ private DbAccessControlEntry getAccessControlEntry(
+ NodeRef nodeRef,
+ String authority,
+ PermissionReference permission)
+ {
+ DbAccessControlList acl = getAccessControlList(nodeRef, false);
+ DbAccessControlEntry entry = null;
+ if (acl != null)
+ {
+ DbPermissionKey permissionKey = new DbPermissionKey(permission.getQName(), permission.getName());
+ entry = acl.getEntry(authority, permissionKey);
+ }
+ // done
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("" + (entry == null ? "Did not find" : "Found") + " entry for criteria: \n" +
+ " node: " + nodeRef + "\n" +
+ " authority: " + authority + "\n" +
+ " permission: " + permission);
+ }
+ return entry;
+ }
+
+ /**
+ * Utility method to find or create a persisted authority
+ */
+ private DbAuthority getAuthority(String authority, boolean create)
+ {
+ DbAuthority entity = (DbAuthority) getHibernateTemplate().get(DbAuthorityImpl.class, authority);
+ if ((entity == null) && create)
+ {
+ entity = new DbAuthorityImpl();
+ entity.setRecipient(authority);
+ getHibernateTemplate().save(entity);
+ return entity;
+ }
+ else
+ {
+ return entity;
+ }
+ }
+
+ /**
+ * Utility method to find and optionally create a persisted permission.
+ */
+ private DbPermission getPermission(PermissionReference permissionRef, final boolean create)
+ {
+ final QName qname = permissionRef.getQName();
+ final String name = permissionRef.getName();
+ Session session = getSession();
+
+ DbPermission dbPermission = DbPermissionImpl.find(session, qname, name);
+
+ // create if necessary
+ if ((dbPermission == null) && create)
+ {
+ dbPermission = new DbPermissionImpl();
+ dbPermission.setTypeQname(qname);
+ dbPermission.setName(name);
+ getHibernateTemplate().save(dbPermission);
+ }
+ return dbPermission;
+ }
+
+ public void setPermission(PermissionEntry permissionEntry)
+ {
+ setPermission(
+ permissionEntry.getNodeRef(),
+ permissionEntry.getAuthority(),
+ permissionEntry.getPermissionReference(),
+ permissionEntry.isAllowed());
+ }
+
+ public void setPermission(NodePermissionEntry nodePermissionEntry)
+ {
+ NodeRef nodeRef = nodePermissionEntry.getNodeRef();
+
+ // Get the access control list
+ // Note the logic here requires to know whether it was created or not
+ DbAccessControlList acl = getAccessControlList(nodeRef, false);
+ if (acl != null)
+ {
+ // maintain referencial integrity
+ getACLDAO(nodeRef).setAccessControlList(nodeRef, null);
+ // drop the list
+ getHibernateTemplate().delete(acl);
+ }
+ // create the access control list
+ acl = createAccessControlList(nodeRef);
+
+ // set attributes
+ acl.setInherits(nodePermissionEntry.inheritPermissions());
+
+ // add all entries
+ for (PermissionEntry pe : nodePermissionEntry.getPermissionEntries())
+ {
+ PermissionReference permission = pe.getPermissionReference();
+ String authority = pe.getAuthority();
+ boolean isAllowed = pe.isAllowed();
+
+ DbPermission permissionEntity = getPermission(permission, true);
+ DbAuthority authorityEntity = getAuthority(authority, true);
+
+ @SuppressWarnings("unused")
+ DbAccessControlEntryImpl entry = acl.newEntry(permissionEntity, authorityEntity, isAllowed);
+ }
+ }
+
+ public void setInheritParentPermissions(NodeRef nodeRef, boolean inheritParentPermissions)
+ {
+ DbAccessControlList acl = null;
+ if (!inheritParentPermissions)
+ {
+ // Inheritance == true is the default, so only force a create of the ACL if the value false
+ acl = getAccessControlList(nodeRef, true);
+ acl.setInherits(false);
+ }
+ else
+ {
+ acl = getAccessControlList(nodeRef, false);
+ if (acl != null)
+ {
+ acl.setInherits(true);
+ }
+ }
+ }
+
+ public boolean getInheritParentPermissions(NodeRef nodeRef)
+ {
+ DbAccessControlList acl = null;
+ try
+ {
+ acl = getAccessControlList(nodeRef, false);
+ }
+ catch (InvalidNodeRefException e)
+ {
+ return INHERIT_PERMISSIONS_DEFAULT;
+ }
+ if (acl == null)
+ {
+ return true;
+ }
+ else
+ {
+ return acl.getInherits();
+ }
+ }
+
+ // Utility methods to create simple detached objects for the outside world
+ // We do not pass out the hibernate objects
+
+ private SimpleNodePermissionEntry createSimpleNodePermissionEntry(NodeRef nodeRef)
+ {
+ DbAccessControlList acl =
+ getACLDAO(nodeRef).getAccessControlList(nodeRef);
+ if (acl == null)
+ {
+ // there isn't an access control list for the node - spoof a null one
+ SimpleNodePermissionEntry snpe = new SimpleNodePermissionEntry(
+ nodeRef,
+ true,
+ Collections. emptySet());
+ return snpe;
+ }
+ else
+ {
+ Set entries = acl.getEntries();
+ SimpleNodePermissionEntry snpe = new SimpleNodePermissionEntry(
+ nodeRef,
+ acl.getInherits(),
+ createSimplePermissionEntries(nodeRef, entries));
+ return snpe;
+ }
+ }
+
+ /**
+ * @param entries access control entries
+ * @return Returns a unique set of entries that can be given back to the outside world
+ */
+ private Set createSimplePermissionEntries(NodeRef nodeRef,
+ Collection entries)
+ {
+ if (entries == null)
+ {
+ return null;
+ }
+ HashSet spes = new HashSet(entries.size(), 1.0f);
+ if (entries.size() != 0)
+ {
+ for (DbAccessControlEntry entry : entries)
+ {
+ spes.add(createSimplePermissionEntry(nodeRef, entry));
+ }
+ }
+ return spes;
+ }
+
+ private static SimplePermissionEntry createSimplePermissionEntry(NodeRef nodeRef,
+ DbAccessControlEntry ace)
+ {
+ if (ace == null)
+ {
+ return null;
+ }
+ return new SimplePermissionEntry(
+ nodeRef,
+ createSimplePermissionReference(ace.getPermission()),
+ ace.getAuthority().getRecipient(),
+ ace.isAllowed() ? AccessStatus.ALLOWED : AccessStatus.DENIED);
+ }
+
+ private static SimplePermissionReference createSimplePermissionReference(DbPermission perm)
+ {
+ if (perm == null)
+ {
+ return null;
+ }
+ return new SimplePermissionReference(
+ perm.getTypeQname(),
+ perm.getName());
+ }
+
+ /**
+ * Helper to choose appropriate NodeService for the given NodeRef
+ * @param nodeRef The NodeRef to dispatch from.
+ * @return The appropriate NodeService.
+ */
+ private AccessControlListDAO getACLDAO(NodeRef nodeRef)
+ {
+ AccessControlListDAO ret = fProtocolToACLDAO.get(nodeRef.getStoreRef().getProtocol());
+ if (ret == null)
+ {
+ return fDefaultACLDAO;
+ }
+ return ret;
+ }
+}
diff --git a/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java b/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java
index e17a13e6ae..c3822a51bc 100644
--- a/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java
+++ b/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java
@@ -20,8 +20,8 @@ import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.FileWriter;
+import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Writer;
import java.sql.Connection;
@@ -47,8 +47,10 @@ import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.orm.hibernate3.LocalSessionFactoryBean;
-import org.springframework.util.ResourceUtils;
/**
* Bootstraps the schema and schema update. The schema is considered missing if the applied patch table
@@ -316,7 +318,8 @@ public class SchemaBootstrap implements ApplicationListener
// perform a full update using Hibernate-generated statements
File tempFile = TempFileProvider.createTempFile("AlfrescoSchemaCreate", ".sql");
dumpSchemaCreate(cfg, tempFile);
- executeScriptFile(cfg, connection, tempFile);
+ FileInputStream tempInputStream = new FileInputStream(tempFile);
+ executeScriptFile(cfg, connection, tempInputStream, tempFile.getPath());
// execute post-create scripts (not patches)
for (String scriptUrl : this.postCreateScriptUrls)
{
@@ -357,7 +360,8 @@ public class SchemaBootstrap implements ApplicationListener
// execute if there were changes raised by Hibernate
if (tempFile != null)
{
- executeScriptFile(cfg, connection, tempFile);
+ InputStream tempInputStream = new FileInputStream(tempFile);
+ executeScriptFile(cfg, connection, tempInputStream, tempFile.getPath());
}
}
}
@@ -406,14 +410,14 @@ public class SchemaBootstrap implements ApplicationListener
private void executeScriptUrl(Configuration cfg, Connection connection, String scriptUrl) throws Exception
{
Dialect dialect = Dialect.getDialect(cfg.getProperties());
- File scriptFile = getScriptFile(dialect.getClass(), scriptUrl);
+ InputStream scriptInputStream = getScriptInputStream(dialect.getClass(), scriptUrl);
// check that it exists
- if (scriptFile == null)
+ if (scriptInputStream == null)
{
throw AlfrescoRuntimeException.create(ERR_SCRIPT_NOT_FOUND, scriptUrl);
}
// now execute it
- executeScriptFile(cfg, connection, scriptFile);
+ executeScriptFile(cfg, connection, scriptInputStream, scriptUrl);
}
/**
@@ -421,45 +425,46 @@ public class SchemaBootstrap implements ApplicationListener
* it. If not found, the dialect hierarchy will be walked until a compatible script is
* found. This makes it possible to have scripts that are generic to all dialects.
*
- * @return Returns the file if found, otherwise null
+ * @return Returns an input stream onto the script, otherwise null
*/
- private File getScriptFile(Class dialectClazz, String scriptUrl) throws Exception
+ private InputStream getScriptInputStream(Class dialectClazz, String scriptUrl) throws Exception
{
// replace the dialect placeholder
String dialectScriptUrl = scriptUrl.replaceAll(PLACEHOLDER_SCRIPT_DIALECT, dialectClazz.getName());
// get a handle on the resource
- try
+ ResourcePatternResolver rpr = new PathMatchingResourcePatternResolver(this.getClass().getClassLoader());
+ Resource resource = rpr.getResource(dialectScriptUrl);
+ if (!resource.exists())
{
- File scriptFile = ResourceUtils.getFile(dialectScriptUrl);
- if (scriptFile.exists())
+ // it wasn't found. Get the superclass of the dialect and try again
+ Class superClazz = dialectClazz.getSuperclass();
+ if (Dialect.class.isAssignableFrom(superClazz))
{
- // found a compatible dialect version
- return scriptFile;
+ // we still have a Dialect - try again
+ return getScriptInputStream(superClazz, scriptUrl);
+ }
+ else
+ {
+ // we have exhausted all options
+ return null;
}
- }
- catch (FileNotFoundException e)
- {
- // doesn't exist
- }
- // it wasn't found. Get the superclass of the dialect and try again
- Class superClazz = dialectClazz.getSuperclass();
- if (Dialect.class.isAssignableFrom(superClazz))
- {
- // we still have a Dialect - try again
- return getScriptFile(superClazz, scriptUrl);
}
else
{
- // we have exhausted all options
- return null;
+ // we have a handle to it
+ return resource.getInputStream();
}
}
- private void executeScriptFile(Configuration cfg, Connection connection, File scriptFile) throws Exception
+ private void executeScriptFile(
+ Configuration cfg,
+ Connection connection,
+ InputStream scriptInputStream,
+ String scriptUrl) throws Exception
{
- logger.info(I18NUtil.getMessage(MSG_EXECUTING_SCRIPT, scriptFile));
+ logger.info(I18NUtil.getMessage(MSG_EXECUTING_SCRIPT, scriptUrl));
- BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(scriptFile), "UTF8"));
+ BufferedReader reader = new BufferedReader(new InputStreamReader(scriptInputStream, "UTF8"));
try
{
int line = 0;
@@ -486,7 +491,7 @@ public class SchemaBootstrap implements ApplicationListener
if (sb.length() > 0)
{
// we have an unterminated statement
- throw AlfrescoRuntimeException.create(ERR_STATEMENT_TERMINATOR, (line - 1), scriptFile);
+ throw AlfrescoRuntimeException.create(ERR_STATEMENT_TERMINATOR, (line - 1), scriptUrl);
}
// there has not been anything to execute - it's just a comment line
continue;
@@ -524,6 +529,7 @@ public class SchemaBootstrap implements ApplicationListener
finally
{
try { reader.close(); } catch (Throwable e) {}
+ try { scriptInputStream.close(); } catch (Throwable e) {}
}
}
}
diff --git a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java
index 92a4abbf4f..de3710f9ae 100644
--- a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java
+++ b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java
@@ -1,1805 +1,1829 @@
-/*
- * Copyright (C) 2005 Alfresco, Inc.
- *
- * Licensed under the Mozilla Public License version 1.1
- * with a permitted attribution clause. You may obtain a
- * copy of the License at
- *
- * http://www.alfresco.org/legal/license.txt
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
- * either express or implied. See the License for the specific
- * language governing permissions and limitations under the
- * License.
- */
-package org.alfresco.repo.node.db;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.Stack;
-
-import org.alfresco.error.AlfrescoRuntimeException;
-import org.alfresco.model.ContentModel;
-import org.alfresco.repo.domain.ChildAssoc;
-import org.alfresco.repo.domain.Node;
-import org.alfresco.repo.domain.NodeAssoc;
-import org.alfresco.repo.domain.NodeStatus;
-import org.alfresco.repo.domain.PropertyValue;
-import org.alfresco.repo.domain.Store;
-import org.alfresco.repo.node.AbstractNodeServiceImpl;
-import org.alfresco.repo.node.StoreArchiveMap;
-import org.alfresco.repo.security.authentication.AuthenticationUtil;
-import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
-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.ClassDefinition;
-import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
-import org.alfresco.service.cmr.dictionary.InvalidAspectException;
-import org.alfresco.service.cmr.dictionary.InvalidTypeException;
-import org.alfresco.service.cmr.dictionary.PropertyDefinition;
-import org.alfresco.service.cmr.dictionary.TypeDefinition;
-import org.alfresco.service.cmr.repository.AssociationExistsException;
-import org.alfresco.service.cmr.repository.AssociationRef;
-import org.alfresco.service.cmr.repository.ChildAssociationRef;
-import org.alfresco.service.cmr.repository.CyclicChildRelationshipException;
-import org.alfresco.service.cmr.repository.InvalidChildAssociationRefException;
-import org.alfresco.service.cmr.repository.InvalidNodeRefException;
-import org.alfresco.service.cmr.repository.InvalidStoreRefException;
-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.StoreExistsException;
-import org.alfresco.service.cmr.repository.StoreRef;
-import org.alfresco.service.cmr.repository.NodeRef.Status;
-import org.alfresco.service.namespace.NamespaceService;
-import org.alfresco.service.namespace.QName;
-import org.alfresco.service.namespace.QNamePattern;
-import org.alfresco.util.ParameterCheck;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.springframework.dao.DataIntegrityViolationException;
-import org.springframework.util.Assert;
-
-/**
- * Node service using database persistence layer to fulfill functionality
- *
- * @author Derek Hulley
- */
-public class DbNodeServiceImpl extends AbstractNodeServiceImpl
-{
- private static Log logger = LogFactory.getLog(DbNodeServiceImpl.class);
-
- private NodeDaoService nodeDaoService;
- private StoreArchiveMap storeArchiveMap;
- private NodeService avmNodeService;
-
- public DbNodeServiceImpl()
- {
- storeArchiveMap = new StoreArchiveMap(); // in case it is not set
- }
-
- public void setNodeDaoService(NodeDaoService nodeDaoService)
- {
- this.nodeDaoService = nodeDaoService;
- }
-
- public void setStoreArchiveMap(StoreArchiveMap storeArchiveMap)
- {
- this.storeArchiveMap = storeArchiveMap;
- }
-
- public void setAvmNodeService(NodeService avmNodeService)
- {
- this.avmNodeService = avmNodeService;
- }
-
- /**
- * Performs a null-safe get of the node
- *
- * @param nodeRef the node to retrieve
- * @return Returns the node entity (never null)
- * @throws InvalidNodeRefException if the referenced node could not be found
- */
- private Node getNodeNotNull(NodeRef nodeRef) throws InvalidNodeRefException
- {
- Node unchecked = nodeDaoService.getNode(nodeRef);
- if (unchecked == null)
- {
- throw new InvalidNodeRefException("Node does not exist: " + nodeRef, nodeRef);
- }
- return unchecked;
- }
-
- public boolean exists(StoreRef storeRef)
- {
- Store store = nodeDaoService.getStore(storeRef.getProtocol(), storeRef.getIdentifier());
- boolean exists = (store != null);
- // done
- return exists;
- }
-
- public boolean exists(NodeRef nodeRef)
- {
- Node node = nodeDaoService.getNode(nodeRef);
- boolean exists = (node != null);
- // done
- return exists;
- }
-
- public Status getNodeStatus(NodeRef nodeRef)
- {
- NodeStatus nodeStatus = nodeDaoService.getNodeStatus(nodeRef, false);
- if (nodeStatus == null) // node never existed
- {
- return null;
- }
- else
- {
- return new NodeRef.Status(
- nodeStatus.getTransaction().getChangeTxnId(),
- nodeStatus.isDeleted());
- }
- }
-
- /**
- * @see NodeDaoService#getStores()
- */
- public List getStores()
- {
- List stores = nodeDaoService.getStores();
- List storeRefs = new ArrayList(stores.size());
- for (Store store : stores)
- {
- storeRefs.add(store.getStoreRef());
- }
- // Now get the AVMStores.
- List avmStores = avmNodeService.getStores();
- storeRefs.addAll(avmStores);
- // Return them all.
- return storeRefs;
- }
-
- /**
- * Defers to the typed service
- * @see StoreDaoService#createWorkspace(String)
- */
- public StoreRef createStore(String protocol, String identifier)
- {
- StoreRef storeRef = new StoreRef(protocol, identifier);
- // check that the store does not already exist
- Store store = nodeDaoService.getStore(protocol, identifier);
- if (store != null)
- {
- throw new StoreExistsException("Unable to create a store that already exists: " + storeRef, storeRef);
- }
-
- // invoke policies
- invokeBeforeCreateStore(ContentModel.TYPE_STOREROOT, storeRef);
-
- // create a new one
- store = nodeDaoService.createStore(protocol, identifier);
- // get the root node
- Node rootNode = store.getRootNode();
- // assign the root aspect - this is expected of all roots, even store roots
- addAspect(rootNode.getNodeRef(),
- ContentModel.ASPECT_ROOT,
- Collections.emptyMap());
-
- // invoke policies
- invokeOnCreateStore(rootNode.getNodeRef());
-
- // done
- if (!store.getStoreRef().equals(storeRef))
- {
- throw new RuntimeException("Incorrect store reference");
- }
- return storeRef;
- }
-
- public NodeRef getRootNode(StoreRef storeRef) throws InvalidStoreRefException
- {
- Store store = nodeDaoService.getStore(storeRef.getProtocol(), storeRef.getIdentifier());
- if (store == null)
- {
- throw new InvalidStoreRefException("Store does not exist", storeRef);
- }
- // get the root
- Node node = store.getRootNode();
- if (node == null)
- {
- throw new InvalidStoreRefException("Store does not have a root node", storeRef);
- }
- NodeRef nodeRef = node.getNodeRef();
- // done
- return nodeRef;
- }
-
- /**
- * @see #createNode(NodeRef, QName, QName, QName, Map)
- */
- public ChildAssociationRef createNode(
- NodeRef parentRef,
- QName assocTypeQName,
- QName assocQName,
- QName nodeTypeQName)
- {
- return this.createNode(parentRef, assocTypeQName, assocQName, nodeTypeQName, null);
- }
-
- /**
- * @see org.alfresco.service.cmr.repository.NodeService#createNode(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName, org.alfresco.service.namespace.QName, org.alfresco.service.namespace.QName, java.util.Map)
- */
- public ChildAssociationRef createNode(
- NodeRef parentRef,
- QName assocTypeQName,
- QName assocQName,
- QName nodeTypeQName,
- Map properties)
- {
- Assert.notNull(parentRef);
- Assert.notNull(assocTypeQName);
- Assert.notNull(assocQName);
-
- // null property map is allowed
- if (properties == null)
- {
- properties = new HashMap();
- }
- else
- {
- // Copy the incomming property map since we may need to modify it later
- properties = new HashMap(properties);
- }
-
- // Invoke policy behaviour
- invokeBeforeUpdateNode(parentRef);
- invokeBeforeCreateNode(parentRef, assocTypeQName, assocQName, nodeTypeQName);
-
- // get the store that the parent belongs to
- StoreRef storeRef = parentRef.getStoreRef();
- Store store = nodeDaoService.getStore(storeRef.getProtocol(), storeRef.getIdentifier());
- if (store == null)
- {
- throw new RuntimeException("No store found for parent node: " + parentRef);
- }
-
- // check the node type
- TypeDefinition nodeTypeDef = dictionaryService.getType(nodeTypeQName);
- if (nodeTypeDef == null)
- {
- throw new InvalidTypeException(nodeTypeQName);
- }
-
- // get/generate an ID for the node
- String newId = generateGuid(properties);
-
- // create the node instance
- Node childNode = nodeDaoService.newNode(store, newId, nodeTypeQName);
-
- // Set the default property values
- addDefaultPropertyValues(nodeTypeDef, properties);
-
- // Add the default aspects to the node
- addDefaultAspects(nodeTypeDef, childNode, properties);
-
- // set the properties - it is a new node so only set properties if there are any
- Map propertiesBefore = getPropertiesImpl(childNode);
- Map propertiesAfter = null;
- if (properties.size() > 0)
- {
- propertiesAfter = setPropertiesImpl(childNode, properties);
- }
-
- // get the parent node
- Node parentNode = getNodeNotNull(parentRef);
-
- // create the association
- ChildAssoc childAssoc = nodeDaoService.newChildAssoc(
- parentNode,
- childNode,
- true,
- assocTypeQName,
- assocQName);
- setChildUniqueName(childNode); // ensure uniqueness
- ChildAssociationRef childAssocRef = childAssoc.getChildAssocRef();
-
- // Invoke policy behaviour
- invokeOnCreateNode(childAssocRef);
- invokeOnUpdateNode(parentRef);
- if (propertiesAfter != null)
- {
- invokeOnUpdateProperties(childAssocRef.getChildRef(), propertiesBefore, propertiesAfter);
- }
-
- // done
- return childAssocRef;
- }
-
- /**
- * Add the default aspects to a given node
- *
- * @param nodeTypeDef
- */
- private void addDefaultAspects(ClassDefinition classDefinition, Node node, Map properties)
- {
- NodeRef nodeRef = node.getNodeRef();
-
- // get the mandatory aspects for the node type
- List defaultAspectDefs = classDefinition.getDefaultAspects();
-
- // add all the aspects to the node
- Set nodeAspects = node.getAspects();
- for (AspectDefinition defaultAspectDef : defaultAspectDefs)
- {
- invokeBeforeAddAspect(nodeRef, defaultAspectDef.getName());
- nodeAspects.add(defaultAspectDef.getName());
- addDefaultPropertyValues(defaultAspectDef, properties);
- invokeOnAddAspect(nodeRef, defaultAspectDef.getName());
-
- // Now add any default aspects for this aspect
- addDefaultAspects(defaultAspectDef, node, properties);
- }
- }
-
-
- /**
- * Drops the old primary association and creates a new one
- */
- public ChildAssociationRef moveNode(
- NodeRef nodeToMoveRef,
- NodeRef newParentRef,
- QName assocTypeQName,
- QName assocQName)
- throws InvalidNodeRefException
- {
- Assert.notNull(nodeToMoveRef);
- Assert.notNull(newParentRef);
- Assert.notNull(assocTypeQName);
- Assert.notNull(assocQName);
-
- // check the node references
- Node nodeToMove = getNodeNotNull(nodeToMoveRef);
- Node newParentNode = getNodeNotNull(newParentRef);
- // get the primary parent assoc
- ChildAssoc oldAssoc = nodeDaoService.getPrimaryParentAssoc(nodeToMove);
- ChildAssociationRef oldAssocRef = oldAssoc.getChildAssocRef();
- // get the old parent
- Node oldParentNode = oldAssoc.getParent();
-
- boolean movingStore = !nodeToMoveRef.getStoreRef().equals(newParentRef.getStoreRef());
-
- // data needed for policy invocation
- QName nodeToMoveTypeQName = nodeToMove.getTypeQName();
- Set nodeToMoveAspects = nodeToMove.getAspects();
-
- // Invoke policy behaviour
- if (movingStore)
- {
- invokeBeforeDeleteNode(nodeToMoveRef);
- invokeBeforeCreateNode(newParentRef, assocTypeQName, assocQName, nodeToMoveTypeQName);
- }
- else
- {
- invokeBeforeDeleteChildAssociation(oldAssocRef);
- invokeBeforeCreateChildAssociation(newParentRef, nodeToMoveRef, assocTypeQName, assocQName);
- invokeBeforeUpdateNode(oldParentNode.getNodeRef()); // old parent will be updated
- invokeBeforeUpdateNode(newParentRef); // new parent ditto
- }
-
- // remove the child assoc from the old parent
- // don't cascade as we will still need the node afterwards
- nodeDaoService.deleteChildAssoc(oldAssoc, false);
-
- // create a new assoc
- ChildAssoc newAssoc = nodeDaoService.newChildAssoc(
- newParentNode,
- nodeToMove,
- true,
- assocTypeQName,
- assocQName);
- setChildUniqueName(nodeToMove); // ensure uniqueness
- ChildAssociationRef newAssocRef = newAssoc.getChildAssocRef();
-
- // If the node is moving stores, then drag the node hierarchy with it
- if (movingStore)
- {
- // do the move
- Store newStore = newParentNode.getStore();
- moveNodeToStore(nodeToMove, newStore);
- // the node reference will have changed too
- nodeToMoveRef = nodeToMove.getNodeRef();
- }
-
- // check that no cyclic relationships have been created
- getPaths(nodeToMoveRef, false);
-
- // invoke policy behaviour
- if (movingStore)
- {
- // TODO for now indicate that the node has been archived to prevent the version history from being removed
- // in the future a onMove policy could be added and remove the need for onDelete and onCreate to be fired here
- invokeOnDeleteNode(oldAssocRef, nodeToMoveTypeQName, nodeToMoveAspects, true);
- invokeOnCreateNode(newAssoc.getChildAssocRef());
- }
- else
- {
- invokeOnCreateChildAssociation(newAssoc.getChildAssocRef());
- invokeOnDeleteChildAssociation(oldAssoc.getChildAssocRef());
- invokeOnUpdateNode(oldParentNode.getNodeRef());
- invokeOnUpdateNode(newParentRef);
- }
- invokeOnMoveNode(oldAssocRef, newAssocRef);
-
- // update the node status
- nodeDaoService.recordChangeId(nodeToMoveRef);
-
- // done
- return newAssoc.getChildAssocRef();
- }
-
- public void setChildAssociationIndex(ChildAssociationRef childAssocRef, int index)
- {
- // get nodes
- Node parentNode = getNodeNotNull(childAssocRef.getParentRef());
- Node childNode = getNodeNotNull(childAssocRef.getChildRef());
-
- ChildAssoc assoc = nodeDaoService.getChildAssoc(
- parentNode,
- childNode,
- childAssocRef.getTypeQName(),
- childAssocRef.getQName());
- if (assoc == null)
- {
- throw new InvalidChildAssociationRefException("Unable to set child association index: \n" +
- " assoc: " + childAssocRef + "\n" +
- " index: " + index,
- childAssocRef);
- }
- // set the index
- assoc.setIndex(index);
- }
-
- public QName getType(NodeRef nodeRef) throws InvalidNodeRefException
- {
- Node node = getNodeNotNull(nodeRef);
- return node.getTypeQName();
- }
-
- /**
- * @see org.alfresco.service.cmr.repository.NodeService#setType(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName)
- */
- public void setType(NodeRef nodeRef, QName typeQName) throws InvalidNodeRefException
- {
- // check the node type
- TypeDefinition nodeTypeDef = dictionaryService.getType(typeQName);
- if (nodeTypeDef == null)
- {
- throw new InvalidTypeException(typeQName);
- }
-
- // Invoke policies
- invokeBeforeUpdateNode(nodeRef);
-
- // Get the node and set the new type
- Node node = getNodeNotNull(nodeRef);
- node.setTypeQName(typeQName);
-
- // Add the default aspects to the node (update the properties with any new default values)
- Map properties = this.getPropertiesImpl(node);
- addDefaultAspects(nodeTypeDef, node, properties);
- this.setProperties(nodeRef, properties);
-
- // Invoke policies
- invokeOnUpdateNode(nodeRef);
- }
-
- /**
- * @see Node#getAspects()
- */
- public void addAspect(
- NodeRef nodeRef,
- QName aspectTypeQName,
- Map aspectProperties)
- throws InvalidNodeRefException, InvalidAspectException
- {
- // check that the aspect is legal
- AspectDefinition aspectDef = dictionaryService.getAspect(aspectTypeQName);
- if (aspectDef == null)
- {
- throw new InvalidAspectException("The aspect is invalid: " + aspectTypeQName, aspectTypeQName);
- }
-
- // Invoke policy behaviours
- invokeBeforeUpdateNode(nodeRef);
- invokeBeforeAddAspect(nodeRef, aspectTypeQName);
-
- Node node = getNodeNotNull(nodeRef);
-
- // attach the properties to the current node properties
- Map nodeProperties = getPropertiesImpl(node);
-
- if (aspectProperties != null)
- {
- nodeProperties.putAll(aspectProperties);
- }
-
- // Set any default property values that appear on the aspect
- addDefaultPropertyValues(aspectDef, nodeProperties);
-
- // Add any dependant aspect
- addDefaultAspects(aspectDef, node, nodeProperties);
-
- // Set the property values back on the node
- setProperties(nodeRef, nodeProperties);
-
- // physically attach the aspect to the node
- if (node.getAspects().add(aspectTypeQName) == true)
- {
- // Invoke policy behaviours
- invokeOnUpdateNode(nodeRef);
- invokeOnAddAspect(nodeRef, aspectTypeQName);
-
- // update the node status
- nodeDaoService.recordChangeId(nodeRef);
- }
- }
-
- /**
- * @see Node#getAspects()
- */
- public void removeAspect(NodeRef nodeRef, QName aspectTypeQName)
- throws InvalidNodeRefException, InvalidAspectException
- {
- // Invoke policy behaviours
- invokeBeforeUpdateNode(nodeRef);
- invokeBeforeRemoveAspect(nodeRef, aspectTypeQName);
-
- // get the aspect
- AspectDefinition aspectDef = dictionaryService.getAspect(aspectTypeQName);
- if (aspectDef == null)
- {
- throw new InvalidAspectException(aspectTypeQName);
- }
- // get the node
- Node node = getNodeNotNull(nodeRef);
-
- // remove the aspect, if present
- boolean removed = node.getAspects().remove(aspectTypeQName);
- // if the aspect was present, remove the associated properties
- if (removed)
- {
- Map nodeProperties = node.getProperties();
- Map propertyDefs = aspectDef.getProperties();
- for (QName propertyName : propertyDefs.keySet())
- {
- nodeProperties.remove(propertyName);
- }
-
- // Invoke policy behaviours
- invokeOnUpdateNode(nodeRef);
- invokeOnRemoveAspect(nodeRef, aspectTypeQName);
-
- // update the node status
- nodeDaoService.recordChangeId(nodeRef);
- }
- }
-
- /**
- * Performs a check on the set of node aspects
- *
- * @see Node#getAspects()
- */
- public boolean hasAspect(NodeRef nodeRef, QName aspectRef) throws InvalidNodeRefException, InvalidAspectException
- {
- Node node = getNodeNotNull(nodeRef);
- Set aspectQNames = node.getAspects();
- boolean hasAspect = aspectQNames.contains(aspectRef);
- // done
- return hasAspect;
- }
-
- public Set getAspects(NodeRef nodeRef) throws InvalidNodeRefException
- {
- Node node = getNodeNotNull(nodeRef);
- Set aspectQNames = node.getAspects();
- // copy the set to ensure initialization
- Set ret = new HashSet(aspectQNames.size());
- ret.addAll(aspectQNames);
- // done
- return ret;
- }
-
- public void deleteNode(NodeRef nodeRef)
- {
- boolean isArchivedNode = false;
- boolean requiresDelete = false;
-
- // Invoke policy behaviours
- invokeBeforeDeleteNode(nodeRef);
-
- // get the node
- Node node = getNodeNotNull(nodeRef);
- // get the primary parent-child relationship before it is gone
- ChildAssociationRef childAssocRef = getPrimaryParent(nodeRef);
- // get type and aspect QNames as they will be unavailable after the delete
- QName nodeTypeQName = node.getTypeQName();
- Set nodeAspectQNames = node.getAspects();
-
- // check if we need to archive the node
- StoreRef archiveStoreRef = null;
- if (nodeAspectQNames.contains(ContentModel.ASPECT_TEMPORARY))
- {
- // the node has the temporary aspect meaning
- // it can not be archived
- requiresDelete = true;
- isArchivedNode = false;
- }
- else
- {
- StoreRef storeRef = nodeRef.getStoreRef();
- archiveStoreRef = storeArchiveMap.getArchiveMap().get(storeRef);
- // get the type and check if we need archiving
- TypeDefinition typeDef = dictionaryService.getType(node.getTypeQName());
- if (typeDef == null || !typeDef.isArchive() || archiveStoreRef == null)
- {
- requiresDelete = true;
- }
- }
-
- if (requiresDelete)
- {
- // perform a normal deletion
- nodeDaoService.deleteNode(node, true);
- isArchivedNode = false;
- }
- else
- {
- // archive it
- archiveNode(nodeRef, archiveStoreRef);
- isArchivedNode = true;
- }
-
- // Invoke policy behaviours
- invokeOnDeleteNode(childAssocRef, nodeTypeQName, nodeAspectQNames, isArchivedNode);
- }
-
- public ChildAssociationRef addChild(NodeRef parentRef, NodeRef childRef, QName assocTypeQName, QName assocQName)
- {
- // Invoke policy behaviours
- invokeBeforeUpdateNode(parentRef);
- invokeBeforeCreateChildAssociation(parentRef, childRef, assocTypeQName, assocQName);
-
- // get the parent node and ensure that it is a container node
- Node parentNode = getNodeNotNull(parentRef);
- // get the child node
- Node childNode = getNodeNotNull(childRef);
- // make the association
- ChildAssoc assoc = nodeDaoService.newChildAssoc(
- parentNode,
- childNode,
- false,
- assocTypeQName,
- assocQName);
- // ensure name uniqueness
- setChildUniqueName(childNode);
- ChildAssociationRef assocRef = assoc.getChildAssocRef();
- NodeRef childNodeRef = assocRef.getChildRef();
-
- // check that the child addition of the child has not created a cyclic relationship
- // this functionality is provided for free in getPath
- getPaths(childNodeRef, false);
-
- // Invoke policy behaviours
- invokeOnCreateChildAssociation(assocRef);
- invokeOnUpdateNode(parentRef);
-
- return assoc.getChildAssocRef();
- }
-
- public void removeChild(NodeRef parentRef, NodeRef childRef) throws InvalidNodeRefException
- {
- Node parentNode = getNodeNotNull(parentRef);
-
- Node childNode = getNodeNotNull(childRef);
- Long childNodeId = childNode.getId();
-
- // get all the child assocs
- ChildAssociationRef primaryAssocRef = null;
- Collection assocs = nodeDaoService.getChildAssocs(parentNode);
- assocs = new HashSet(assocs); // copy set as we will be modifying it
- for (ChildAssoc assoc : assocs)
- {
- if (!assoc.getChild().getId().equals(childNodeId))
- {
- continue; // not a matching association
- }
- ChildAssociationRef assocRef = assoc.getChildAssocRef();
- // Is this a primary association?
- if (assoc.getIsPrimary())
- {
- // keep the primary associaton for last
- primaryAssocRef = assocRef;
- }
- else
- {
- // delete the association instance - it is not primary
- invokeBeforeDeleteChildAssociation(assocRef);
- nodeDaoService.deleteChildAssoc(assoc, true); // cascade
- invokeOnDeleteChildAssociation(assocRef);
- }
- }
- // remove the child if the primary association was a match
- if (primaryAssocRef != null)
- {
- deleteNode(primaryAssocRef.getChildRef());
- }
-
- // Invoke policy behaviours
- invokeOnUpdateNode(parentRef);
-
- // done
- }
-
- public Map getProperties(NodeRef nodeRef) throws InvalidNodeRefException
- {
- Node node = getNodeNotNull(nodeRef);
- return getPropertiesImpl(node);
- }
-
- private Map getPropertiesImpl(Node node) throws InvalidNodeRefException
- {
- NodeRef nodeRef = node.getNodeRef();
-
- Map nodeProperties = node.getProperties();
- Map ret = new HashMap(nodeProperties.size());
- // copy values
- for (Map.Entry entry: nodeProperties.entrySet())
- {
- QName propertyQName = entry.getKey();
- PropertyValue propertyValue = entry.getValue();
- // get the property definition
- PropertyDefinition propertyDef = dictionaryService.getProperty(propertyQName);
- // convert to the correct type
- Serializable value = makeSerializableValue(propertyDef, propertyValue);
- // copy across
- ret.put(propertyQName, value);
- }
- // spoof referencable properties
- addReferencableProperties(nodeRef, node.getId(), ret);
- // done
- return ret;
- }
-
- public Serializable getProperty(NodeRef nodeRef, QName qname) throws InvalidNodeRefException
- {
- // spoof referencable properties
- if (qname.equals(ContentModel.PROP_STORE_PROTOCOL))
- {
- return nodeRef.getStoreRef().getProtocol();
- }
- else if (qname.equals(ContentModel.PROP_STORE_IDENTIFIER))
- {
- return nodeRef.getStoreRef().getIdentifier();
- }
- else if (qname.equals(ContentModel.PROP_NODE_UUID))
- {
- return nodeRef.getId();
- }
-
- // get the property from the node
- Node node = getNodeNotNull(nodeRef);
-
- if (qname.equals(ContentModel.PROP_NODE_DBID))
- {
- return node.getId();
- }
-
- Map properties = node.getProperties();
- PropertyValue propertyValue = properties.get(qname);
-
- // get the property definition
- PropertyDefinition propertyDef = dictionaryService.getProperty(qname);
- // convert to the correct type
- Serializable value = makeSerializableValue(propertyDef, propertyValue);
- // done
- return value;
- }
-
- /**
- * Ensures that all required properties are present on the node and copies the
- * property values to the Node
.
- *
- * To remove a property, remove it from the map before calling this method.
- * Null-valued properties are allowed.
- *
- * If any of the values are null, a marker object is put in to mimic nulls. They will be turned back into
- * a real nulls when the properties are requested again.
- *
- * @see Node#getProperties()
- */
- public void setProperties(NodeRef nodeRef, Map properties) throws InvalidNodeRefException
- {
- Node node = getNodeNotNull(nodeRef);
-
- // Invoke policy behaviours
- invokeBeforeUpdateNode(nodeRef);
-
- // Do the set properties
- Map propertiesBefore = getPropertiesImpl(node);
- Map propertiesAfter = setPropertiesImpl(node, properties);
-
- setChildUniqueName(node); // ensure uniqueness
-
- // Invoke policy behaviours
- invokeOnUpdateNode(nodeRef);
- invokeOnUpdateProperties(nodeRef, propertiesBefore, propertiesAfter);
- }
-
- /**
- * Does the work of setting the property values. Returns a map containing the state of the properties after the set
- * operation is complete.
- *
- * @param node the node
- * @param properties the map of property values
- * @return the map of property values after the set operation is complete
- * @throws InvalidNodeRefException
- */
- private Map setPropertiesImpl(Node node, Map properties) throws InvalidNodeRefException
- {
- ParameterCheck.mandatory("properties", properties);
-
- // remove referencable properties
- removeReferencableProperties(properties);
-
- // copy properties onto node
- Map nodeProperties = node.getProperties();
- nodeProperties.clear();
-
- // check the property type and copy the values across
- for (QName propertyQName : properties.keySet())
- {
- PropertyDefinition propertyDef = dictionaryService.getProperty(propertyQName);
- Serializable value = properties.get(propertyQName);
- // get a persistable value
- PropertyValue propertyValue = makePropertyValue(propertyDef, value);
- nodeProperties.put(propertyQName, propertyValue);
- }
-
- // update the node status
- NodeRef nodeRef = node.getNodeRef();
- nodeDaoService.recordChangeId(nodeRef);
-
- // Return the properties after
- return Collections.unmodifiableMap(properties);
- }
-
- /**
- * Gets the properties map, sets the value (null is allowed) and checks that the new set
- * of properties is valid.
- *
- * @see DbNodeServiceImpl.NullPropertyValue
- */
- public void setProperty(NodeRef nodeRef, QName qname, Serializable value) throws InvalidNodeRefException
- {
- Assert.notNull(qname);
-
- // Invoke policy behaviours
- invokeBeforeUpdateNode(nodeRef);
-
- // get the node
- Node node = getNodeNotNull(nodeRef);
-
- // Do the set operation
- Map propertiesBefore = getPropertiesImpl(node);
- Map propertiesAfter = setPropertyImpl(node, qname, value);
-
- if (qname.equals(ContentModel.PROP_NAME))
- {
- setChildUniqueName(node); // ensure uniqueness
- }
-
- // Invoke policy behaviours
- invokeOnUpdateNode(nodeRef);
- invokeOnUpdateProperties(nodeRef, propertiesBefore, propertiesAfter);
- }
-
- /**
- * Does the work of setting a property value. Returns the values of the properties after the set operation is
- * complete.
- *
- * @param node the node
- * @param qname the qname of the property
- * @param value the value of the property
- * @return the values of the properties after the set operation is complete
- * @throws InvalidNodeRefException
- */
- public Map setPropertyImpl(Node node, QName qname, Serializable value) throws InvalidNodeRefException
- {
- NodeRef nodeRef = node.getNodeRef();
-
- Map properties = node.getProperties();
- PropertyDefinition propertyDef = dictionaryService.getProperty(qname);
- // get a persistable value
- PropertyValue propertyValue = makePropertyValue(propertyDef, value);
- properties.put(qname, propertyValue);
-
- // update the node status
- nodeDaoService.recordChangeId(nodeRef);
-
- return getPropertiesImpl(node);
- }
-
- /**
- * Transforms {@link Node#getParentAssocs()} to a new collection
- */
- public Collection getParents(NodeRef nodeRef) throws InvalidNodeRefException
- {
- Node node = getNodeNotNull(nodeRef);
- // get the assocs pointing to it
- Collection parentAssocs = node.getParentAssocs();
- // list of results
- Collection results = new ArrayList(parentAssocs.size());
- for (ChildAssoc assoc : parentAssocs)
- {
- // get the parent
- Node parentNode = assoc.getParent();
- results.add(parentNode.getNodeRef());
- }
- // done
- return results;
- }
-
- /**
- * Filters out any associations if their qname is not a match to the given pattern.
- */
- public List getParentAssocs(NodeRef nodeRef, QNamePattern typeQNamePattern, QNamePattern qnamePattern)
- {
- Node node = getNodeNotNull(nodeRef);
- // get the assocs pointing to it
- Collection parentAssocs = node.getParentAssocs();
- // shortcut if there are no assocs
- if (parentAssocs.size() == 0)
- {
- return Collections.emptyList();
- }
- // list of results
- List results = new ArrayList(parentAssocs.size());
- for (ChildAssoc assoc : parentAssocs)
- {
- // does the qname match the pattern?
- if (!qnamePattern.isMatch(assoc.getQname()) || !typeQNamePattern.isMatch(assoc.getTypeQName()))
- {
- // no match - ignore
- continue;
- }
- results.add(assoc.getChildAssocRef());
- }
- // done
- return results;
- }
-
- /**
- * Filters out any associations if their qname is not a match to the given pattern.
- */
- public List getChildAssocs(NodeRef nodeRef, QNamePattern typeQNamePattern, QNamePattern qnamePattern)
- {
- Node node = getNodeNotNull(nodeRef);
- // get the assocs pointing from it
- Collection childAssocRefs = nodeDaoService.getChildAssocRefs(node);
- // shortcut if there are no assocs
- if (childAssocRefs.size() == 0)
- {
- return Collections.emptyList();
- }
- // sort results
- ArrayList orderedList = new ArrayList(childAssocRefs);
- Collections.sort(orderedList);
-
- // list of results
- int nthSibling = 0;
- Iterator iterator = orderedList.iterator();
- while(iterator.hasNext())
- {
- ChildAssociationRef childAssocRef = iterator.next();
- // does the qname match the pattern?
- if (!qnamePattern.isMatch(childAssocRef.getQName()) || !typeQNamePattern.isMatch(childAssocRef.getTypeQName()))
- {
- // no match - remove
- iterator.remove();
- }
- else
- {
- childAssocRef.setNthSibling(nthSibling);
- nthSibling++;
- }
- }
- // done
- return orderedList;
- }
-
- public NodeRef getChildByName(NodeRef nodeRef, QName assocTypeQName, String childName)
- {
- Node node = getNodeNotNull(nodeRef);
- ChildAssoc childAssoc = nodeDaoService.getChildAssoc(node, assocTypeQName, childName);
- if (childAssoc != null)
- {
- return childAssoc.getChild().getNodeRef();
- }
- else
- {
- return null;
- }
- }
-
- public ChildAssociationRef getPrimaryParent(NodeRef nodeRef) throws InvalidNodeRefException
- {
- Node node = getNodeNotNull(nodeRef);
- // get the primary parent assoc
- ChildAssoc assoc = nodeDaoService.getPrimaryParentAssoc(node);
-
- // done - the assoc may be null for a root node
- ChildAssociationRef assocRef = null;
- if (assoc == null)
- {
- assocRef = new ChildAssociationRef(null, null, null, nodeRef);
- }
- else
- {
- assocRef = assoc.getChildAssocRef();
- }
- return assocRef;
- }
-
- public AssociationRef createAssociation(NodeRef sourceRef, NodeRef targetRef, QName assocTypeQName)
- throws InvalidNodeRefException, AssociationExistsException
- {
- // Invoke policy behaviours
- invokeBeforeUpdateNode(sourceRef);
-
- Node sourceNode = getNodeNotNull(sourceRef);
- Node targetNode = getNodeNotNull(targetRef);
- // see if it exists
- NodeAssoc assoc = nodeDaoService.getNodeAssoc(sourceNode, targetNode, assocTypeQName);
- if (assoc != null)
- {
- throw new AssociationExistsException(sourceRef, targetRef, assocTypeQName);
- }
- // we are sure that the association doesn't exist - make it
- assoc = nodeDaoService.newNodeAssoc(sourceNode, targetNode, assocTypeQName);
- AssociationRef assocRef = assoc.getNodeAssocRef();
-
- // Invoke policy behaviours
- invokeOnUpdateNode(sourceRef);
- invokeOnCreateAssociation(assocRef);
-
- return assocRef;
- }
-
- public void removeAssociation(NodeRef sourceRef, NodeRef targetRef, QName assocTypeQName)
- throws InvalidNodeRefException
- {
- Node sourceNode = getNodeNotNull(sourceRef);
- Node targetNode = getNodeNotNull(targetRef);
- // get the association
- NodeAssoc assoc = nodeDaoService.getNodeAssoc(sourceNode, targetNode, assocTypeQName);
- if (assoc == null)
- {
- // nothing to remove
- return;
- }
- AssociationRef assocRef = assoc.getNodeAssocRef();
-
- // Invoke policy behaviours
- invokeBeforeUpdateNode(sourceRef);
-
- // delete it
- nodeDaoService.deleteNodeAssoc(assoc);
-
- // Invoke policy behaviours
- invokeOnUpdateNode(sourceRef);
- invokeOnDeleteAssociation(assocRef);
- }
-
- public List getTargetAssocs(NodeRef sourceRef, QNamePattern qnamePattern)
- {
- Node sourceNode = getNodeNotNull(sourceRef);
- // get all assocs to target
- Collection assocs = nodeDaoService.getTargetNodeAssocs(sourceNode);
- List nodeAssocRefs = new ArrayList(assocs.size());
- for (NodeAssoc assoc : assocs)
- {
- // check qname pattern
- if (!qnamePattern.isMatch(assoc.getTypeQName()))
- {
- continue; // the assoc name doesn't match the pattern given
- }
- nodeAssocRefs.add(assoc.getNodeAssocRef());
- }
- // done
- return nodeAssocRefs;
- }
-
- public List getSourceAssocs(NodeRef targetRef, QNamePattern qnamePattern)
- {
- Node targetNode = getNodeNotNull(targetRef);
- // get all assocs to source
- Collection assocs = nodeDaoService.getSourceNodeAssocs(targetNode);
- List nodeAssocRefs = new ArrayList(assocs.size());
- for (NodeAssoc assoc : assocs)
- {
- // check qname pattern
- if (!qnamePattern.isMatch(assoc.getTypeQName()))
- {
- continue; // the assoc name doesn't match the pattern given
- }
- nodeAssocRefs.add(assoc.getNodeAssocRef());
- }
- // done
- return nodeAssocRefs;
- }
-
- /**
- * Recursive method used to build up paths from a given node to the root.
- *
- * Whilst walking up the hierarchy to the root, some nodes may have a root aspect.
- * Everytime one of these is encountered, a new path is farmed off, but the method
- * continues to walk up the hierarchy.
- *
- * @param currentNode the node to start from, i.e. the child node to work upwards from
- * @param currentPath the path from the current node to the descendent that we started from
- * @param completedPaths paths that have reached the root are added to this collection
- * @param assocStack the parent-child relationships traversed whilst building the path.
- * Used to detected cyclic relationships.
- * @param primaryOnly true if only the primary parent association must be traversed.
- * If this is true, then the only root is the top level node having no parents.
- * @throws CyclicChildRelationshipException
- */
- private void prependPaths(
- final Node currentNode,
- final Path currentPath,
- Collection completedPaths,
- Stack assocStack,
- boolean primaryOnly)
- throws CyclicChildRelationshipException
- {
- NodeRef currentNodeRef = currentNode.getNodeRef();
- // get the parent associations of the given node
- Collection parentAssocs = currentNode.getParentAssocs();
- // does the node have parents
- boolean hasParents = parentAssocs.size() > 0;
- // does the current node have a root aspect?
- boolean isRoot = hasAspect(currentNodeRef, ContentModel.ASPECT_ROOT);
- boolean isStoreRoot = currentNode.getTypeQName().equals(ContentModel.TYPE_STOREROOT);
-
- // look for a root. If we only want the primary root, then ignore all but the top-level root.
- if (isRoot && !(primaryOnly && hasParents)) // exclude primary search with parents present
- {
- // create a one-sided assoc ref for the root node and prepend to the stack
- // this effectively spoofs the fact that the current node is not below the root
- // - we put this assoc in as the first assoc in the path must be a one-sided
- // reference pointing to the root node
- ChildAssociationRef assocRef = new ChildAssociationRef(
- null,
- null,
- null,
- getRootNode(currentNode.getNodeRef().getStoreRef()));
- // create a path to save and add the 'root' assoc
- Path pathToSave = new Path();
- Path.ChildAssocElement first = null;
- for (Path.Element element: currentPath)
- {
- if (first == null)
- {
- first = (Path.ChildAssocElement) element;
- }
- else
- {
- pathToSave.append(element);
- }
- }
- if (first != null)
- {
- // mimic an association that would appear if the current node was below
- // the root node
- // or if first beneath the root node it will make the real thing
- ChildAssociationRef updateAssocRef = new ChildAssociationRef(
- isStoreRoot ? ContentModel.ASSOC_CHILDREN : first.getRef().getTypeQName(),
- getRootNode(currentNode.getNodeRef().getStoreRef()),
- first.getRef().getQName(),
- first.getRef().getChildRef());
- Path.Element newFirst = new Path.ChildAssocElement(updateAssocRef);
- pathToSave.prepend(newFirst);
- }
-
- Path.Element element = new Path.ChildAssocElement(assocRef);
- pathToSave.prepend(element);
-
- // store the path just built
- completedPaths.add(pathToSave);
- }
-
- if (parentAssocs.size() == 0 && !isRoot)
- {
- throw new RuntimeException("Node without parents does not have root aspect: " +
- currentNodeRef);
- }
- // walk up each parent association
- for (ChildAssoc assoc : parentAssocs)
- {
- // does the association already exist in the stack
- if (assocStack.contains(assoc))
- {
- // the association was present already
- throw new CyclicChildRelationshipException(
- "Cyclic parent-child relationship detected: \n" +
- " current node: " + currentNode + "\n" +
- " current path: " + currentPath + "\n" +
- " next assoc: " + assoc,
- assoc);
- }
- // do we consider only primary assocs?
- if (primaryOnly && !assoc.getIsPrimary())
- {
- continue;
- }
- // build a path element
- NodeRef parentRef = assoc.getParent().getNodeRef();
- QName qname = assoc.getQname();
- NodeRef childRef = assoc.getChild().getNodeRef();
- boolean isPrimary = assoc.getIsPrimary();
- // build a real association reference
- ChildAssociationRef assocRef = new ChildAssociationRef(assoc.getTypeQName(), parentRef, qname, childRef, isPrimary, -1);
- // Ordering is not important here: We are building distinct paths upwards
- Path.Element element = new Path.ChildAssocElement(assocRef);
- // create a new path that builds on the current path
- Path path = new Path();
- path.append(currentPath);
- // prepend element
- path.prepend(element);
- // get parent node
- Node parentNode = assoc.getParent();
-
- // push the assoc stack, recurse and pop
- assocStack.push(assoc);
- prependPaths(parentNode, path, completedPaths, assocStack, primaryOnly);
- assocStack.pop();
- }
- // done
- }
-
- /**
- * @see #getPaths(NodeRef, boolean)
- * @see #prependPaths(Node, Path, Collection, Stack, boolean)
- */
- public Path getPath(NodeRef nodeRef) throws InvalidNodeRefException
- {
- List paths = getPaths(nodeRef, true); // checks primary path count
- if (paths.size() == 1)
- {
- return paths.get(0); // we know there is only one
- }
- throw new RuntimeException("Primary path count not checked"); // checked by getPaths()
- }
-
- /**
- * When searching for primaryOnly == true
, checks that there is exactly
- * one path.
- * @see #prependPaths(Node, Path, Collection, Stack, boolean)
- */
- public List getPaths(NodeRef nodeRef, boolean primaryOnly) throws InvalidNodeRefException
- {
- // get the starting node
- Node node = getNodeNotNull(nodeRef);
- // create storage for the paths - only need 1 bucket if we are looking for the primary path
- List paths = new ArrayList(primaryOnly ? 1 : 10);
- // create an empty current path to start from
- Path currentPath = new Path();
- // create storage for touched associations
- Stack assocStack = new Stack();
- // call recursive method to sort it out
- prependPaths(node, currentPath, paths, assocStack, primaryOnly);
-
- // check that for the primary only case we have exactly one path
- if (primaryOnly && paths.size() != 1)
- {
- throw new RuntimeException("Node has " + paths.size() + " primary paths: " + nodeRef);
- }
-
- // done
- return paths;
- }
-
- private void archiveNode(NodeRef nodeRef, StoreRef archiveStoreRef)
- {
- Node node = getNodeNotNull(nodeRef);
- ChildAssoc primaryParentAssoc = nodeDaoService.getPrimaryParentAssoc(node);
-
- // add the aspect
- Set aspects = node.getAspects();
- aspects.add(ContentModel.ASPECT_ARCHIVED);
- Map properties = node.getProperties();
- PropertyValue archivedByProperty = makePropertyValue(
- dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_BY),
- AuthenticationUtil.getCurrentUserName());
- properties.put(ContentModel.PROP_ARCHIVED_BY, archivedByProperty);
- PropertyValue archivedDateProperty = makePropertyValue(
- dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_DATE),
- new Date());
- properties.put(ContentModel.PROP_ARCHIVED_DATE, archivedDateProperty);
- PropertyValue archivedPrimaryParentNodeRefProperty = makePropertyValue(
- dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC),
- primaryParentAssoc.getChildAssocRef());
- properties.put(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC, archivedPrimaryParentNodeRefProperty);
- PropertyValue originalOwnerProperty = properties.get(ContentModel.PROP_OWNER);
- PropertyValue originalCreatorProperty = properties.get(ContentModel.PROP_CREATOR);
- if (originalOwnerProperty != null || originalCreatorProperty != null)
- {
- properties.put(
- ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER,
- originalOwnerProperty != null ? originalOwnerProperty : originalCreatorProperty);
- }
-
- // change the node ownership
- aspects.add(ContentModel.ASPECT_OWNABLE);
- PropertyValue newOwnerProperty = makePropertyValue(
- dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER),
- AuthenticationUtil.getCurrentUserName());
- properties.put(ContentModel.PROP_OWNER, newOwnerProperty);
-
- // move the node
- NodeRef archiveStoreRootNodeRef = getRootNode(archiveStoreRef);
- moveNode(
- nodeRef,
- archiveStoreRootNodeRef,
- ContentModel.ASSOC_CHILDREN,
- QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedItem"));
-
- // get the IDs of all the node's primary children, including its own
- Map nodesById = getNodeHierarchy(node, null);
-
- // Archive all the associations between the archived nodes and non-archived nodes
- for (Node nodeToArchive : nodesById.values())
- {
- archiveAssocs(nodeToArchive, nodesById);
- }
-
- // the node reference has changed due to the store move
- nodeRef = node.getNodeRef();
- }
-
- /**
- * Performs all the necessary housekeeping involved in changing a node's store.
- * This method cascades down through all the primary children of the node as
- * well.
- *
- * @param node the node whose store is changing
- * @param store the new store for the node
- */
- private void moveNodeToStore(Node node, Store store)
- {
- // get the IDs of all the node's primary children, including its own
- Map nodesById = getNodeHierarchy(node, null);
-
- // move each node into the archive store
- for (Node nodeToMove : nodesById.values())
- {
- NodeRef oldNodeRef = nodeToMove.getNodeRef();
- nodeToMove.setStore(store);
- NodeRef newNodeRef = nodeToMove.getNodeRef();
-
- String txnId = AlfrescoTransactionSupport.getTransactionId();
- // update old status
- NodeStatus oldNodeStatus = nodeDaoService.getNodeStatus(oldNodeRef, true);
- oldNodeStatus.setNode(null);
- oldNodeStatus.getTransaction().setChangeTxnId(txnId);
- // create the new status
- NodeStatus newNodeStatus = nodeDaoService.getNodeStatus(newNodeRef, true);
- newNodeStatus.setNode(nodeToMove);
- newNodeStatus.getTransaction().setChangeTxnId(txnId);
- }
- }
-
- /**
- * Fill the map of all primary children below the given node.
- * The given node will be added to the map and the method is recursive
- * to all primary children.
- *
- * @param node the start of the hierarchy
- * @param nodesById a map of nodes that will be reused as the return value
- * @return Returns a map of nodes in the hierarchy keyed by their IDs
- */
- private Map getNodeHierarchy(Node node, Map nodesById)
- {
- if (nodesById == null)
- {
- nodesById = new HashMap(23);
- }
-
- Long id = node.getId();
- if (nodesById.containsKey(id))
- {
- // this ID was already added - circular reference
- logger.warn("Circular hierarchy found including node " + id);
- return nodesById;
- }
- // add the node to the map
- nodesById.put(id, node);
- // recurse into the primary children
- Collection childAssocs = nodeDaoService.getChildAssocs(node);
- for (ChildAssoc childAssoc : childAssocs)
- {
- // cascade into primary associations
- if (childAssoc.getIsPrimary())
- {
- Node primaryChild = childAssoc.getChild();
- nodesById = getNodeHierarchy(primaryChild, nodesById);
- }
- }
- return nodesById;
- }
-
- /**
- * Archive all associations to and from the given node, with the
- * exception of associations to or from nodes in the given map.
- *
- * Primary parent associations are also ignored.
- *
- * @param node the node whose associations must be archived
- * @param nodesById a map of nodes partaking in the archival process
- */
- private void archiveAssocs(Node node, Map nodesById)
- {
- List childAssocsToDelete = new ArrayList(5);
- // child associations
- ArrayList archivedChildAssocRefs = new ArrayList(5);
- Collection childAssocs = nodeDaoService.getChildAssocs(node);
- for (ChildAssoc assoc : childAssocs)
- {
- Long relatedNodeId = assoc.getChild().getId();
- if (nodesById.containsKey(relatedNodeId))
- {
- // a sibling in the archive process
- continue;
- }
- childAssocsToDelete.add(assoc);
- archivedChildAssocRefs.add(assoc.getChildAssocRef());
- }
- // parent associations
- ArrayList archivedParentAssocRefs = new ArrayList(5);
- for (ChildAssoc assoc : node.getParentAssocs())
- {
- Long relatedNodeId = assoc.getParent().getId();
- if (nodesById.containsKey(relatedNodeId))
- {
- // a sibling in the archive process
- continue;
- }
- else if (assoc.getIsPrimary())
- {
- // ignore the primary parent as this is handled more specifically
- continue;
- }
- childAssocsToDelete.add(assoc);
- archivedParentAssocRefs.add(assoc.getChildAssocRef());
- }
-
- List nodeAssocsToDelete = new ArrayList(5);
- // source associations
- ArrayList archivedSourceAssocRefs = new ArrayList(5);
- for (NodeAssoc assoc : nodeDaoService.getSourceNodeAssocs(node))
- {
- Long relatedNodeId = assoc.getSource().getId();
- if (nodesById.containsKey(relatedNodeId))
- {
- // a sibling in the archive process
- continue;
- }
- nodeAssocsToDelete.add(assoc);
- archivedSourceAssocRefs.add(assoc.getNodeAssocRef());
- }
- // target associations
- ArrayList archivedTargetAssocRefs = new ArrayList(5);
- for (NodeAssoc assoc : nodeDaoService.getTargetNodeAssocs(node))
- {
- Long relatedNodeId = assoc.getTarget().getId();
- if (nodesById.containsKey(relatedNodeId))
- {
- // a sibling in the archive process
- continue;
- }
- nodeAssocsToDelete.add(assoc);
- archivedTargetAssocRefs.add(assoc.getNodeAssocRef());
- }
- // delete child assocs
- for (ChildAssoc assoc : childAssocsToDelete)
- {
- nodeDaoService.deleteChildAssoc(assoc, false);
- }
- // delete node assocs
- for (NodeAssoc assoc : nodeAssocsToDelete)
- {
- nodeDaoService.deleteNodeAssoc(assoc);
- }
-
- // add archived aspect
- node.getAspects().add(ContentModel.ASPECT_ARCHIVED_ASSOCS);
- // set properties
- Map properties = node.getProperties();
-
- if (archivedParentAssocRefs.size() > 0)
- {
- PropertyDefinition propertyDef = dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_PARENT_ASSOCS);
- PropertyValue propertyValue = makePropertyValue(propertyDef, archivedParentAssocRefs);
- properties.put(ContentModel.PROP_ARCHIVED_PARENT_ASSOCS, propertyValue);
- }
- if (archivedChildAssocRefs.size() > 0)
- {
- PropertyDefinition propertyDef = dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_CHILD_ASSOCS);
- PropertyValue propertyValue = makePropertyValue(propertyDef, archivedChildAssocRefs);
- properties.put(ContentModel.PROP_ARCHIVED_CHILD_ASSOCS, propertyValue);
- }
- if (archivedSourceAssocRefs.size() > 0)
- {
- PropertyDefinition propertyDef = dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_SOURCE_ASSOCS);
- PropertyValue propertyValue = makePropertyValue(propertyDef, archivedSourceAssocRefs);
- properties.put(ContentModel.PROP_ARCHIVED_SOURCE_ASSOCS, propertyValue);
- }
- if (archivedTargetAssocRefs.size() > 0)
- {
- PropertyDefinition propertyDef = dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_TARGET_ASSOCS);
- PropertyValue propertyValue = makePropertyValue(propertyDef, archivedTargetAssocRefs);
- properties.put(ContentModel.PROP_ARCHIVED_TARGET_ASSOCS, propertyValue);
- }
- }
-
- public NodeRef getStoreArchiveNode(StoreRef storeRef)
- {
- StoreRef archiveStoreRef = storeArchiveMap.getArchiveMap().get(storeRef);
- if (archiveStoreRef == null)
- {
- // no mapping for the given store
- return null;
- }
- else
- {
- return getRootNode(archiveStoreRef);
- }
- }
-
- public NodeRef restoreNode(NodeRef archivedNodeRef, NodeRef destinationParentNodeRef, QName assocTypeQName, QName assocQName)
- {
- Node archivedNode = getNodeNotNull(archivedNodeRef);
- Set aspects = archivedNode.getAspects();
- Map properties = archivedNode.getProperties();
- // the node must be a top-level archive node
- if (!aspects.contains(ContentModel.ASPECT_ARCHIVED))
- {
- throw new AlfrescoRuntimeException("The node to archive is not an archive node");
- }
- ChildAssociationRef originalPrimaryParentAssocRef = (ChildAssociationRef) makeSerializableValue(
- dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC),
- properties.get(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC));
- PropertyValue originalOwnerProperty = properties.get(ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER);
- // remove the aspect archived aspect
- aspects.remove(ContentModel.ASPECT_ARCHIVED);
- properties.remove(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC);
- properties.remove(ContentModel.PROP_ARCHIVED_BY);
- properties.remove(ContentModel.PROP_ARCHIVED_DATE);
- properties.remove(ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER);
-
- // restore the original ownership
- if (originalOwnerProperty != null)
- {
- aspects.add(ContentModel.ASPECT_OWNABLE);
- properties.put(ContentModel.PROP_OWNER, originalOwnerProperty);
- }
-
- if (destinationParentNodeRef == null)
- {
- // we must restore to the original location
- destinationParentNodeRef = originalPrimaryParentAssocRef.getParentRef();
- }
- // check the associations
- if (assocTypeQName == null)
- {
- assocTypeQName = originalPrimaryParentAssocRef.getTypeQName();
- }
- if (assocQName == null)
- {
- assocQName = originalPrimaryParentAssocRef.getQName();
- }
-
- // move the node to the target parent, which may or may not be the original parent
- moveNode(
- archivedNodeRef,
- destinationParentNodeRef,
- assocTypeQName,
- assocQName);
-
- // get the IDs of all the node's primary children, including its own
- Map restoredNodesById = getNodeHierarchy(archivedNode, null);
- // Restore the archived associations, if required
- for (Node restoredNode : restoredNodesById.values())
- {
- restoreAssocs(restoredNode);
- }
-
- // the node reference has changed due to the store move
- NodeRef restoredNodeRef = archivedNode.getNodeRef();
-
- // done
- if (logger.isDebugEnabled())
- {
- logger.debug("Restored node: \n" +
- " original noderef: " + archivedNodeRef + "\n" +
- " restored noderef: " + restoredNodeRef + "\n" +
- " new parent: " + destinationParentNodeRef);
- }
- return restoredNodeRef;
- }
-
- private void restoreAssocs(Node node)
- {
- NodeRef nodeRef = node.getNodeRef();
- // set properties
- Map properties = node.getProperties();
-
- // restore parent associations
- Collection parentAssocRefs = (Collection) getProperty(
- nodeRef,
- ContentModel.PROP_ARCHIVED_PARENT_ASSOCS);
- if (parentAssocRefs != null)
- {
- for (ChildAssociationRef assocRef : parentAssocRefs)
- {
- NodeRef parentNodeRef = assocRef.getParentRef();
- if (!exists(parentNodeRef))
- {
- continue;
- }
- Node parentNode = getNodeNotNull(parentNodeRef);
- // get the name to use for the unique child check
- QName assocTypeQName = assocRef.getTypeQName();
- nodeDaoService.newChildAssoc(
- parentNode,
- node,
- assocRef.isPrimary(),
- assocTypeQName,
- assocRef.getQName());
- }
- properties.remove(ContentModel.PROP_ARCHIVED_PARENT_ASSOCS);
- }
-
- // make sure that the node name uniqueness is enforced
- setChildUniqueName(node);
-
- // restore child associations
- Collection childAssocRefs = (Collection) getProperty(
- nodeRef,
- ContentModel.PROP_ARCHIVED_CHILD_ASSOCS);
- if (childAssocRefs != null)
- {
- for (ChildAssociationRef assocRef : childAssocRefs)
- {
- NodeRef childNodeRef = assocRef.getChildRef();
- if (!exists(childNodeRef))
- {
- continue;
- }
- Node childNode = getNodeNotNull(childNodeRef);
- QName assocTypeQName = assocRef.getTypeQName();
- // get the name to use for the unique child check
- nodeDaoService.newChildAssoc(
- node,
- childNode,
- assocRef.isPrimary(),
- assocTypeQName,
- assocRef.getQName());
- // ensure that the name uniqueness is enforced for the child node
- setChildUniqueName(childNode);
- }
- properties.remove(ContentModel.PROP_ARCHIVED_CHILD_ASSOCS);
- }
- // restore source associations
- Collection sourceAssocRefs = (Collection) getProperty(
- nodeRef,
- ContentModel.PROP_ARCHIVED_SOURCE_ASSOCS);
- if (sourceAssocRefs != null)
- {
- for (AssociationRef assocRef : sourceAssocRefs)
- {
- NodeRef sourceNodeRef = assocRef.getSourceRef();
- if (!exists(sourceNodeRef))
- {
- continue;
- }
- Node sourceNode = getNodeNotNull(sourceNodeRef);
- nodeDaoService.newNodeAssoc(sourceNode, node, assocRef.getTypeQName());
- }
- properties.remove(ContentModel.PROP_ARCHIVED_SOURCE_ASSOCS);
- }
- // restore target associations
- Collection targetAssocRefs = (Collection) getProperty(
- nodeRef,
- ContentModel.PROP_ARCHIVED_TARGET_ASSOCS);
- if (targetAssocRefs != null)
- {
- for (AssociationRef assocRef : targetAssocRefs)
- {
- NodeRef targetNodeRef = assocRef.getTargetRef();
- if (!exists(targetNodeRef))
- {
- continue;
- }
- Node targetNode = getNodeNotNull(targetNodeRef);
- nodeDaoService.newNodeAssoc(node, targetNode, assocRef.getTypeQName());
- }
- properties.remove(ContentModel.PROP_ARCHIVED_TARGET_ASSOCS);
- }
- // remove the aspect
- node.getAspects().remove(ContentModel.ASPECT_ARCHIVED_ASSOCS);
- }
-
- /**
- * Checks the dictionary's definition of the association to assign a unique name to the child node.
- *
- * @param assocTypeQName the type of the child association
- * @param childNode the child node being added. The name will be extracted from it, if necessary.
- */
- private void setChildUniqueName(Node childNode)
- {
- // get the name property
- Map properties = childNode.getProperties();
- PropertyValue nameValue = properties.get(ContentModel.PROP_NAME);
- String useName = null;
- if (nameValue == null)
- {
- // no name has been assigned, so assign the ID of the child node
- useName = childNode.getUuid();
- }
- else
- {
- useName = (String) nameValue.getValue(DataTypeDefinition.TEXT);
- }
- // get all the parent assocs
- Collection parentAssocs = childNode.getParentAssocs();
- for (ChildAssoc assoc : parentAssocs)
- {
- QName assocTypeQName = assoc.getTypeQName();
- AssociationDefinition assocDef = dictionaryService.getAssociation(assocTypeQName);
- if (!assocDef.isChild())
- {
- throw new DataIntegrityViolationException("Child association has non-child type: " + assoc.getId());
- }
- ChildAssociationDefinition childAssocDef = (ChildAssociationDefinition) assocDef;
- if (childAssocDef.getDuplicateChildNamesAllowed())
- {
- // the name is irrelevant, so it doesn't need to be put into the unique key
- nodeDaoService.setChildNameUnique(assoc, null);
- }
- else
- {
- nodeDaoService.setChildNameUnique(assoc, useName);
- }
- }
- // done
- if (logger.isDebugEnabled())
- {
- logger.debug(
- "Unique name set for all " + parentAssocs.size() + " parent associations: \n" +
- " name: " + useName);
- }
- }
-}
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.node.db;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.model.ContentModel;
+import org.alfresco.repo.domain.ChildAssoc;
+import org.alfresco.repo.domain.Node;
+import org.alfresco.repo.domain.NodeAssoc;
+import org.alfresco.repo.domain.NodeStatus;
+import org.alfresco.repo.domain.PropertyValue;
+import org.alfresco.repo.domain.Store;
+import org.alfresco.repo.node.AbstractNodeServiceImpl;
+import org.alfresco.repo.node.StoreArchiveMap;
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
+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.ClassDefinition;
+import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
+import org.alfresco.service.cmr.dictionary.DictionaryService;
+import org.alfresco.service.cmr.dictionary.InvalidAspectException;
+import org.alfresco.service.cmr.dictionary.InvalidTypeException;
+import org.alfresco.service.cmr.dictionary.PropertyDefinition;
+import org.alfresco.service.cmr.dictionary.TypeDefinition;
+import org.alfresco.service.cmr.repository.AssociationExistsException;
+import org.alfresco.service.cmr.repository.AssociationRef;
+import org.alfresco.service.cmr.repository.ChildAssociationRef;
+import org.alfresco.service.cmr.repository.CyclicChildRelationshipException;
+import org.alfresco.service.cmr.repository.InvalidChildAssociationRefException;
+import org.alfresco.service.cmr.repository.InvalidNodeRefException;
+import org.alfresco.service.cmr.repository.InvalidStoreRefException;
+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.StoreExistsException;
+import org.alfresco.service.cmr.repository.StoreRef;
+import org.alfresco.service.cmr.repository.NodeRef.Status;
+import org.alfresco.service.namespace.NamespaceService;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.service.namespace.QNamePattern;
+import org.alfresco.util.ParameterCheck;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.dao.DataIntegrityViolationException;
+import org.springframework.util.Assert;
+
+/**
+ * Node service using database persistence layer to fulfill functionality
+ *
+ * @author Derek Hulley
+ */
+public class DbNodeServiceImpl extends AbstractNodeServiceImpl
+{
+ private static Log logger = LogFactory.getLog(DbNodeServiceImpl.class);
+ private static Log loggerPaths = LogFactory.getLog(DbNodeServiceImpl.class.getName() + ".paths");
+
+ private DictionaryService dictionaryService;
+ private NodeDaoService nodeDaoService;
+ private StoreArchiveMap storeArchiveMap;
+ private NodeService avmNodeService;
+
+ public DbNodeServiceImpl()
+ {
+ storeArchiveMap = new StoreArchiveMap(); // in case it is not set
+ }
+
+ public void setDictionaryService(DictionaryService dictionaryService)
+ {
+ this.dictionaryService = dictionaryService;
+ }
+
+ public void setNodeDaoService(NodeDaoService nodeDaoService)
+ {
+ this.nodeDaoService = nodeDaoService;
+ }
+
+ public void setStoreArchiveMap(StoreArchiveMap storeArchiveMap)
+ {
+ this.storeArchiveMap = storeArchiveMap;
+ }
+
+ public void setAvmNodeService(NodeService avmNodeService)
+ {
+ this.avmNodeService = avmNodeService;
+ }
+
+ /**
+ * Performs a null-safe get of the node
+ *
+ * @param nodeRef the node to retrieve
+ * @return Returns the node entity (never null)
+ * @throws InvalidNodeRefException if the referenced node could not be found
+ */
+ private Node getNodeNotNull(NodeRef nodeRef) throws InvalidNodeRefException
+ {
+ Node unchecked = nodeDaoService.getNode(nodeRef);
+ if (unchecked == null)
+ {
+ throw new InvalidNodeRefException("Node does not exist: " + nodeRef, nodeRef);
+ }
+ return unchecked;
+ }
+
+ public boolean exists(StoreRef storeRef)
+ {
+ Store store = nodeDaoService.getStore(storeRef.getProtocol(), storeRef.getIdentifier());
+ boolean exists = (store != null);
+ // done
+ return exists;
+ }
+
+ public boolean exists(NodeRef nodeRef)
+ {
+ Node node = nodeDaoService.getNode(nodeRef);
+ boolean exists = (node != null);
+ // done
+ return exists;
+ }
+
+ public Status getNodeStatus(NodeRef nodeRef)
+ {
+ NodeStatus nodeStatus = nodeDaoService.getNodeStatus(nodeRef, false);
+ if (nodeStatus == null) // node never existed
+ {
+ return null;
+ }
+ else
+ {
+ return new NodeRef.Status(
+ nodeStatus.getTransaction().getChangeTxnId(),
+ nodeStatus.isDeleted());
+ }
+ }
+
+ /**
+ * @see NodeDaoService#getStores()
+ */
+ public List getStores()
+ {
+ List stores = nodeDaoService.getStores();
+ List storeRefs = new ArrayList(stores.size());
+ for (Store store : stores)
+ {
+ storeRefs.add(store.getStoreRef());
+ }
+ // Now get the AVMStores.
+ List avmStores = avmNodeService.getStores();
+ storeRefs.addAll(avmStores);
+ // Return them all.
+ return storeRefs;
+ }
+
+ /**
+ * Defers to the typed service
+ * @see StoreDaoService#createWorkspace(String)
+ */
+ public StoreRef createStore(String protocol, String identifier)
+ {
+ StoreRef storeRef = new StoreRef(protocol, identifier);
+ // check that the store does not already exist
+ Store store = nodeDaoService.getStore(protocol, identifier);
+ if (store != null)
+ {
+ throw new StoreExistsException("Unable to create a store that already exists: " + storeRef, storeRef);
+ }
+
+ // invoke policies
+ invokeBeforeCreateStore(ContentModel.TYPE_STOREROOT, storeRef);
+
+ // create a new one
+ store = nodeDaoService.createStore(protocol, identifier);
+ // get the root node
+ Node rootNode = store.getRootNode();
+ // assign the root aspect - this is expected of all roots, even store roots
+ addAspect(rootNode.getNodeRef(),
+ ContentModel.ASPECT_ROOT,
+ Collections.emptyMap());
+
+ // invoke policies
+ invokeOnCreateStore(rootNode.getNodeRef());
+
+ // done
+ if (!store.getStoreRef().equals(storeRef))
+ {
+ throw new RuntimeException("Incorrect store reference");
+ }
+ return storeRef;
+ }
+
+ public NodeRef getRootNode(StoreRef storeRef) throws InvalidStoreRefException
+ {
+ Store store = nodeDaoService.getStore(storeRef.getProtocol(), storeRef.getIdentifier());
+ if (store == null)
+ {
+ throw new InvalidStoreRefException("Store does not exist", storeRef);
+ }
+ // get the root
+ Node node = store.getRootNode();
+ if (node == null)
+ {
+ throw new InvalidStoreRefException("Store does not have a root node", storeRef);
+ }
+ NodeRef nodeRef = node.getNodeRef();
+ // done
+ return nodeRef;
+ }
+
+ /**
+ * @see #createNode(NodeRef, QName, QName, QName, Map)
+ */
+ public ChildAssociationRef createNode(
+ NodeRef parentRef,
+ QName assocTypeQName,
+ QName assocQName,
+ QName nodeTypeQName)
+ {
+ return this.createNode(parentRef, assocTypeQName, assocQName, nodeTypeQName, null);
+ }
+
+ /**
+ * @see org.alfresco.service.cmr.repository.NodeService#createNode(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName, org.alfresco.service.namespace.QName, org.alfresco.service.namespace.QName, java.util.Map)
+ */
+ public ChildAssociationRef createNode(
+ NodeRef parentRef,
+ QName assocTypeQName,
+ QName assocQName,
+ QName nodeTypeQName,
+ Map properties)
+ {
+ Assert.notNull(parentRef);
+ Assert.notNull(assocTypeQName);
+ Assert.notNull(assocQName);
+
+ // null property map is allowed
+ if (properties == null)
+ {
+ properties = new HashMap();
+ }
+ else
+ {
+ // Copy the incomming property map since we may need to modify it later
+ properties = new HashMap(properties);
+ }
+
+ // Invoke policy behaviour
+ invokeBeforeUpdateNode(parentRef);
+ invokeBeforeCreateNode(parentRef, assocTypeQName, assocQName, nodeTypeQName);
+
+ // get the store that the parent belongs to
+ StoreRef storeRef = parentRef.getStoreRef();
+ Store store = nodeDaoService.getStore(storeRef.getProtocol(), storeRef.getIdentifier());
+ if (store == null)
+ {
+ throw new RuntimeException("No store found for parent node: " + parentRef);
+ }
+
+ // check the node type
+ TypeDefinition nodeTypeDef = dictionaryService.getType(nodeTypeQName);
+ if (nodeTypeDef == null)
+ {
+ throw new InvalidTypeException(nodeTypeQName);
+ }
+
+ // get/generate an ID for the node
+ String newId = generateGuid(properties);
+
+ // create the node instance
+ Node childNode = nodeDaoService.newNode(store, newId, nodeTypeQName);
+
+ // get the parent node
+ Node parentNode = getNodeNotNull(parentRef);
+
+ // Set the default property values
+ addDefaultPropertyValues(nodeTypeDef, properties);
+
+ // Add the default aspects to the node
+ addDefaultAspects(nodeTypeDef, childNode, properties);
+
+ // set the properties - it is a new node so only set properties if there are any
+ Map propertiesBefore = getPropertiesImpl(childNode);
+ Map propertiesAfter = null;
+ if (properties.size() > 0)
+ {
+ propertiesAfter = setPropertiesImpl(childNode, properties);
+ }
+
+ // create the association
+ ChildAssoc childAssoc = nodeDaoService.newChildAssoc(
+ parentNode,
+ childNode,
+ true,
+ assocTypeQName,
+ assocQName);
+ setChildUniqueName(childNode); // ensure uniqueness
+ ChildAssociationRef childAssocRef = childAssoc.getChildAssocRef();
+
+ // Invoke policy behaviour
+ invokeOnCreateNode(childAssocRef);
+ invokeOnUpdateNode(parentRef);
+ if (propertiesAfter != null)
+ {
+ invokeOnUpdateProperties(childAssocRef.getChildRef(), propertiesBefore, propertiesAfter);
+ }
+
+ // done
+ return childAssocRef;
+ }
+
+ /**
+ * Add the default aspects to a given node
+ *
+ * @param nodeTypeDef
+ */
+ private void addDefaultAspects(ClassDefinition classDefinition, Node node, Map properties)
+ {
+ NodeRef nodeRef = node.getNodeRef();
+
+ // get the mandatory aspects for the node type
+ List defaultAspectDefs = classDefinition.getDefaultAspects();
+
+ // add all the aspects to the node
+ Set nodeAspects = node.getAspects();
+ for (AspectDefinition defaultAspectDef : defaultAspectDefs)
+ {
+ invokeBeforeAddAspect(nodeRef, defaultAspectDef.getName());
+ nodeAspects.add(defaultAspectDef.getName());
+ addDefaultPropertyValues(defaultAspectDef, properties);
+ invokeOnAddAspect(nodeRef, defaultAspectDef.getName());
+
+ // Now add any default aspects for this aspect
+ addDefaultAspects(defaultAspectDef, node, properties);
+ }
+ }
+
+ /**
+ * Drops the old primary association and creates a new one
+ */
+ public ChildAssociationRef moveNode(
+ NodeRef nodeToMoveRef,
+ NodeRef newParentRef,
+ QName assocTypeQName,
+ QName assocQName)
+ throws InvalidNodeRefException
+ {
+ Assert.notNull(nodeToMoveRef);
+ Assert.notNull(newParentRef);
+ Assert.notNull(assocTypeQName);
+ Assert.notNull(assocQName);
+
+ // check the node references
+ Node nodeToMove = getNodeNotNull(nodeToMoveRef);
+ Node newParentNode = getNodeNotNull(newParentRef);
+ // get the primary parent assoc
+ ChildAssoc oldAssoc = nodeDaoService.getPrimaryParentAssoc(nodeToMove);
+ ChildAssociationRef oldAssocRef = oldAssoc.getChildAssocRef();
+ // get the old parent
+ Node oldParentNode = oldAssoc.getParent();
+
+ boolean movingStore = !nodeToMoveRef.getStoreRef().equals(newParentRef.getStoreRef());
+
+ // data needed for policy invocation
+ QName nodeToMoveTypeQName = nodeToMove.getTypeQName();
+ Set nodeToMoveAspects = nodeToMove.getAspects();
+
+ // Invoke policy behaviour
+ if (movingStore)
+ {
+ invokeBeforeDeleteNode(nodeToMoveRef);
+ invokeBeforeCreateNode(newParentRef, assocTypeQName, assocQName, nodeToMoveTypeQName);
+ }
+ else
+ {
+ invokeBeforeDeleteChildAssociation(oldAssocRef);
+ invokeBeforeCreateChildAssociation(newParentRef, nodeToMoveRef, assocTypeQName, assocQName);
+ invokeBeforeUpdateNode(oldParentNode.getNodeRef()); // old parent will be updated
+ invokeBeforeUpdateNode(newParentRef); // new parent ditto
+ }
+
+ // remove the child assoc from the old parent
+ // don't cascade as we will still need the node afterwards
+ nodeDaoService.deleteChildAssoc(oldAssoc, false);
+
+ // create a new assoc
+ ChildAssoc newAssoc = nodeDaoService.newChildAssoc(
+ newParentNode,
+ nodeToMove,
+ true,
+ assocTypeQName,
+ assocQName);
+ setChildUniqueName(nodeToMove); // ensure uniqueness
+ ChildAssociationRef newAssocRef = newAssoc.getChildAssocRef();
+
+ // If the node is moving stores, then drag the node hierarchy with it
+ if (movingStore)
+ {
+ // do the move
+ Store newStore = newParentNode.getStore();
+ moveNodeToStore(nodeToMove, newStore);
+ // the node reference will have changed too
+ nodeToMoveRef = nodeToMove.getNodeRef();
+ }
+
+ // check that no cyclic relationships have been created
+ getPaths(nodeToMoveRef, false);
+
+ // invoke policy behaviour
+ if (movingStore)
+ {
+ // TODO for now indicate that the node has been archived to prevent the version history from being removed
+ // in the future a onMove policy could be added and remove the need for onDelete and onCreate to be fired here
+ invokeOnDeleteNode(oldAssocRef, nodeToMoveTypeQName, nodeToMoveAspects, true);
+ invokeOnCreateNode(newAssoc.getChildAssocRef());
+ }
+ else
+ {
+ invokeOnCreateChildAssociation(newAssoc.getChildAssocRef());
+ invokeOnDeleteChildAssociation(oldAssoc.getChildAssocRef());
+ invokeOnUpdateNode(oldParentNode.getNodeRef());
+ invokeOnUpdateNode(newParentRef);
+ }
+ invokeOnMoveNode(oldAssocRef, newAssocRef);
+
+ // update the node status
+ nodeDaoService.recordChangeId(nodeToMoveRef);
+
+ // done
+ return newAssoc.getChildAssocRef();
+ }
+
+ public void setChildAssociationIndex(ChildAssociationRef childAssocRef, int index)
+ {
+ // get nodes
+ Node parentNode = getNodeNotNull(childAssocRef.getParentRef());
+ Node childNode = getNodeNotNull(childAssocRef.getChildRef());
+
+ ChildAssoc assoc = nodeDaoService.getChildAssoc(
+ parentNode,
+ childNode,
+ childAssocRef.getTypeQName(),
+ childAssocRef.getQName());
+ if (assoc == null)
+ {
+ throw new InvalidChildAssociationRefException("Unable to set child association index: \n" +
+ " assoc: " + childAssocRef + "\n" +
+ " index: " + index,
+ childAssocRef);
+ }
+ // set the index
+ assoc.setIndex(index);
+ }
+
+ public QName getType(NodeRef nodeRef) throws InvalidNodeRefException
+ {
+ Node node = getNodeNotNull(nodeRef);
+ return node.getTypeQName();
+ }
+
+ /**
+ * @see org.alfresco.service.cmr.repository.NodeService#setType(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName)
+ */
+ public void setType(NodeRef nodeRef, QName typeQName) throws InvalidNodeRefException
+ {
+ // check the node type
+ TypeDefinition nodeTypeDef = dictionaryService.getType(typeQName);
+ if (nodeTypeDef == null)
+ {
+ throw new InvalidTypeException(typeQName);
+ }
+
+ // Invoke policies
+ invokeBeforeUpdateNode(nodeRef);
+
+ // Get the node and set the new type
+ Node node = getNodeNotNull(nodeRef);
+ node.setTypeQName(typeQName);
+
+ // Add the default aspects to the node (update the properties with any new default values)
+ Map properties = this.getPropertiesImpl(node);
+ addDefaultAspects(nodeTypeDef, node, properties);
+ this.setProperties(nodeRef, properties);
+
+ // Invoke policies
+ invokeOnUpdateNode(nodeRef);
+ }
+
+ /**
+ * @see Node#getAspects()
+ */
+ public void addAspect(
+ NodeRef nodeRef,
+ QName aspectTypeQName,
+ Map aspectProperties)
+ throws InvalidNodeRefException, InvalidAspectException
+ {
+ // check that the aspect is legal
+ AspectDefinition aspectDef = dictionaryService.getAspect(aspectTypeQName);
+ if (aspectDef == null)
+ {
+ throw new InvalidAspectException("The aspect is invalid: " + aspectTypeQName, aspectTypeQName);
+ }
+
+ // Invoke policy behaviours
+ invokeBeforeUpdateNode(nodeRef);
+ invokeBeforeAddAspect(nodeRef, aspectTypeQName);
+
+ Node node = getNodeNotNull(nodeRef);
+
+ // attach the properties to the current node properties
+ Map nodeProperties = getPropertiesImpl(node);
+
+ if (aspectProperties != null)
+ {
+ nodeProperties.putAll(aspectProperties);
+ }
+
+ // Set any default property values that appear on the aspect
+ addDefaultPropertyValues(aspectDef, nodeProperties);
+
+ // Add any dependant aspect
+ addDefaultAspects(aspectDef, node, nodeProperties);
+
+ // Set the property values back on the node
+ setProperties(nodeRef, nodeProperties);
+
+ // physically attach the aspect to the node
+ if (node.getAspects().add(aspectTypeQName) == true)
+ {
+ // Invoke policy behaviours
+ invokeOnUpdateNode(nodeRef);
+ invokeOnAddAspect(nodeRef, aspectTypeQName);
+
+ // update the node status
+ nodeDaoService.recordChangeId(nodeRef);
+ }
+ }
+
+ /**
+ * @see Node#getAspects()
+ */
+ public void removeAspect(NodeRef nodeRef, QName aspectTypeQName)
+ throws InvalidNodeRefException, InvalidAspectException
+ {
+ // Invoke policy behaviours
+ invokeBeforeUpdateNode(nodeRef);
+ invokeBeforeRemoveAspect(nodeRef, aspectTypeQName);
+
+ // get the aspect
+ AspectDefinition aspectDef = dictionaryService.getAspect(aspectTypeQName);
+ if (aspectDef == null)
+ {
+ throw new InvalidAspectException(aspectTypeQName);
+ }
+ // get the node
+ Node node = getNodeNotNull(nodeRef);
+
+ // remove the aspect, if present
+ boolean removed = node.getAspects().remove(aspectTypeQName);
+ // if the aspect was present, remove the associated properties
+ if (removed)
+ {
+ Map nodeProperties = node.getProperties();
+ Map propertyDefs = aspectDef.getProperties();
+ for (QName propertyName : propertyDefs.keySet())
+ {
+ nodeProperties.remove(propertyName);
+ }
+
+ // Invoke policy behaviours
+ invokeOnUpdateNode(nodeRef);
+ invokeOnRemoveAspect(nodeRef, aspectTypeQName);
+
+ // update the node status
+ nodeDaoService.recordChangeId(nodeRef);
+ }
+ }
+
+ /**
+ * Performs a check on the set of node aspects
+ *
+ * @see Node#getAspects()
+ */
+ public boolean hasAspect(NodeRef nodeRef, QName aspectRef) throws InvalidNodeRefException, InvalidAspectException
+ {
+ Node node = getNodeNotNull(nodeRef);
+ Set aspectQNames = node.getAspects();
+ boolean hasAspect = aspectQNames.contains(aspectRef);
+ // done
+ return hasAspect;
+ }
+
+ public Set getAspects(NodeRef nodeRef) throws InvalidNodeRefException
+ {
+ Node node = getNodeNotNull(nodeRef);
+ Set aspectQNames = node.getAspects();
+ // copy the set to ensure initialization
+ Set ret = new HashSet(aspectQNames.size());
+ ret.addAll(aspectQNames);
+ // done
+ return ret;
+ }
+
+ public void deleteNode(NodeRef nodeRef)
+ {
+ boolean isArchivedNode = false;
+ boolean requiresDelete = false;
+
+ // Invoke policy behaviours
+ invokeBeforeDeleteNode(nodeRef);
+
+ // get the node
+ Node node = getNodeNotNull(nodeRef);
+ // get the primary parent-child relationship before it is gone
+ ChildAssociationRef childAssocRef = getPrimaryParent(nodeRef);
+ // get type and aspect QNames as they will be unavailable after the delete
+ QName nodeTypeQName = node.getTypeQName();
+ Set nodeAspectQNames = node.getAspects();
+
+ // check if we need to archive the node
+ StoreRef archiveStoreRef = null;
+ if (nodeAspectQNames.contains(ContentModel.ASPECT_TEMPORARY))
+ {
+ // the node has the temporary aspect meaning
+ // it can not be archived
+ requiresDelete = true;
+ isArchivedNode = false;
+ }
+ else
+ {
+ StoreRef storeRef = nodeRef.getStoreRef();
+ archiveStoreRef = storeArchiveMap.getArchiveMap().get(storeRef);
+ // get the type and check if we need archiving
+ TypeDefinition typeDef = dictionaryService.getType(node.getTypeQName());
+ if (typeDef == null || !typeDef.isArchive() || archiveStoreRef == null)
+ {
+ requiresDelete = true;
+ }
+ }
+
+ if (requiresDelete)
+ {
+ // perform a normal deletion
+ nodeDaoService.deleteNode(node, true);
+ isArchivedNode = false;
+ }
+ else
+ {
+ // archive it
+ archiveNode(nodeRef, archiveStoreRef);
+ isArchivedNode = true;
+ }
+
+ // Invoke policy behaviours
+ invokeOnDeleteNode(childAssocRef, nodeTypeQName, nodeAspectQNames, isArchivedNode);
+ }
+
+ public ChildAssociationRef addChild(NodeRef parentRef, NodeRef childRef, QName assocTypeQName, QName assocQName)
+ {
+ // Invoke policy behaviours
+ invokeBeforeUpdateNode(parentRef);
+ invokeBeforeCreateChildAssociation(parentRef, childRef, assocTypeQName, assocQName);
+
+ // get the parent node and ensure that it is a container node
+ Node parentNode = getNodeNotNull(parentRef);
+ // get the child node
+ Node childNode = getNodeNotNull(childRef);
+ // make the association
+ ChildAssoc assoc = nodeDaoService.newChildAssoc(
+ parentNode,
+ childNode,
+ false,
+ assocTypeQName,
+ assocQName);
+ // ensure name uniqueness
+ setChildUniqueName(childNode);
+ ChildAssociationRef assocRef = assoc.getChildAssocRef();
+ NodeRef childNodeRef = assocRef.getChildRef();
+
+ // check that the child addition of the child has not created a cyclic relationship
+ // this functionality is provided for free in getPath
+ getPaths(childNodeRef, false);
+
+ // Invoke policy behaviours
+ invokeOnCreateChildAssociation(assocRef);
+ invokeOnUpdateNode(parentRef);
+
+ return assoc.getChildAssocRef();
+ }
+
+ public void removeChild(NodeRef parentRef, NodeRef childRef) throws InvalidNodeRefException
+ {
+ Node parentNode = getNodeNotNull(parentRef);
+ Node childNode = getNodeNotNull(childRef);
+ Long childNodeId = childNode.getId();
+
+ // get all the child assocs
+ ChildAssociationRef primaryAssocRef = null;
+ Collection assocs = nodeDaoService.getChildAssocs(parentNode);
+ assocs = new HashSet(assocs); // copy set as we will be modifying it
+ for (ChildAssoc assoc : assocs)
+ {
+ if (!assoc.getChild().getId().equals(childNodeId))
+ {
+ continue; // not a matching association
+ }
+ ChildAssociationRef assocRef = assoc.getChildAssocRef();
+ // Is this a primary association?
+ if (assoc.getIsPrimary())
+ {
+ // keep the primary associaton for last
+ primaryAssocRef = assocRef;
+ }
+ else
+ {
+ // delete the association instance - it is not primary
+ invokeBeforeDeleteChildAssociation(assocRef);
+ nodeDaoService.deleteChildAssoc(assoc, true); // cascade
+ invokeOnDeleteChildAssociation(assocRef);
+ }
+ }
+ // remove the child if the primary association was a match
+ if (primaryAssocRef != null)
+ {
+ deleteNode(primaryAssocRef.getChildRef());
+ }
+
+ // Invoke policy behaviours
+ invokeOnUpdateNode(parentRef);
+
+ // done
+ }
+
+ public Map getProperties(NodeRef nodeRef) throws InvalidNodeRefException
+ {
+ Node node = getNodeNotNull(nodeRef);
+ return getPropertiesImpl(node);
+ }
+
+ private Map getPropertiesImpl(Node node) throws InvalidNodeRefException
+ {
+ NodeRef nodeRef = node.getNodeRef();
+
+ Map nodeProperties = node.getProperties();
+ Map ret = new HashMap