newChanges = aclDaoComponent.mergeInheritedAccessControlList(mergeFrom, acl.getId());
- }
- else
- {
- setFixedAcls(child.getChildRef(), mergeFrom, true, nodeService, aclDaoComponent, nodeDaoService);
- }
- }
- }
- }
- }
-
- private static DbAccessControlList getAccessControlList(NodeRef nodeRef, NodeDaoService nodeDaoService)
- {
- Node node = nodeDaoService.getNode(nodeRef);
- if (node == null)
- {
- throw new InvalidNodeRefException(nodeRef);
- }
- return node.getAccessControlList();
- }
-
- private static void setAccessControlList(NodeRef nodeRef, DbAccessControlList acl, NodeDaoService nodeDaoService)
- {
- Node node = nodeDaoService.getNode(nodeRef);
- if (node == null)
- {
- throw new InvalidNodeRefException(nodeRef);
- }
- node.setAccessControlList(acl);
- }
-
}
diff --git a/source/java/org/alfresco/repo/domain/hibernate/DirtySessionAnnotation.java b/source/java/org/alfresco/repo/domain/hibernate/DirtySessionAnnotation.java
new file mode 100644
index 0000000000..b53bbcd2b5
--- /dev/null
+++ b/source/java/org/alfresco/repo/domain/hibernate/DirtySessionAnnotation.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2005-2008 Alfresco Software Limited.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ * As a special exception to the terms and conditions of version 2.0 of
+ * the GPL, you may redistribute this Program in connection with Free/Libre
+ * and Open Source Software ("FLOSS") applications as described in Alfresco's
+ * FLOSS exception. You should have recieved a copy of the text describing
+ * the FLOSS exception, and it is also available here:
+ * http://www.alfresco.com/legal/licensing"
+ */
+package org.alfresco.repo.domain.hibernate;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation defining Hibernate session flushing and dirty marking
+ *
+ * @since 2.1.5
+ * @author Derek Hulley
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface DirtySessionAnnotation
+{
+ /**
+ * Method must flush before execution.
+ * Default: false
+ */
+ boolean flushBefore() default false;
+
+ /**
+ * Method must flush after execution.
+ * Default: false
+ */
+ boolean flushAfter() default false;
+
+ /**
+ * The session must be flagged as dirty after execution.
+ * Default: false
+ */
+ boolean markDirty() default false;
+}
diff --git a/source/java/org/alfresco/repo/domain/hibernate/DirtySessionMethodInterceptor.java b/source/java/org/alfresco/repo/domain/hibernate/DirtySessionMethodInterceptor.java
new file mode 100644
index 0000000000..51fc7d01c9
--- /dev/null
+++ b/source/java/org/alfresco/repo/domain/hibernate/DirtySessionMethodInterceptor.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2005-20078 Alfresco Software Limited.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ * As a special exception to the terms and conditions of version 2.0 of
+ * the GPL, you may redistribute this Program in connection with Free/Libre
+ * and Open Source Software ("FLOSS") applications as described in Alfresco's
+ * FLOSS exception. You should have recieved a copy of the text describing
+ * the FLOSS exception, and it is also available here:
+ * http://www.alfresco.com/legal/licensing
+ */
+package org.alfresco.repo.domain.hibernate;
+
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Stack;
+
+import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
+import org.alfresco.util.Pair;
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.FlushMode;
+import org.hibernate.Query;
+import org.hibernate.Session;
+import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
+
+/**
+ * This method interceptor determines if a Hibernate flush is required and performs the
+ * flush if necessary. The primary purpose is to avoid the Hibernate "flush if required" checks
+ * that occur every time a query is made to the database - whether or not any actual modifications
+ * have been made to the session.
+ *
+ * Write methods (methods that modify the Hibernate Session) will flag the transaction as dirty.
+ * Methods that query the database can {@link #setQueryFlushMode(Session, Query) set the flush mode}
+ * without knowing whether the session is dirty or not.
+ *
+ * The interceptor uses the {@link DirtySessionAnnotation}. If the annotation is not used, then
+ * no session dirty checks will be done but a WARN message will be output.
+ *
+ * The flush data is kept as a transaction-local resource. For this reason, all calls must be made
+ * in the context of a transaction. For the same reason, the methods on the FlushData are
+ * not synchronized as access is only available by one thread.
+ *
+ * It is also possible to {@link #flushSession(Session) flush the session} manually. Using this method
+ * allows the dirty count to be updated properly, thus avoiding unecessary flushing.
+ *
+ * @see #setQueryFlushMode(Session, Query)
+ * @see #flushSession(Session)
+ *
+ * @author Derek Hulley
+ * @since 2.1.5
+ */
+public class DirtySessionMethodInterceptor extends HibernateDaoSupport implements MethodInterceptor
+{
+ private static final String KEY_FLUSH_DATA = "FlushIfRequiredMethodInterceptor.FlushData";
+
+ private static Log logger = LogFactory.getLog(DirtySessionMethodInterceptor.class);
+
+ /**
+ * Keep track of methods that have been warned about, i.e. methods that are not annotated.
+ */
+ private static Set unannotatedMethodNames;
+ static
+ {
+ unannotatedMethodNames = Collections.synchronizedSet(new HashSet(0));
+ }
+
+ /**
+ * Data on whether the session is dirty or not.
+ *
+ * @author Derek Hulley
+ */
+ private static class FlushData
+ {
+ private int dirtyCount;
+ private Stack> methodStack;
+ private FlushData()
+ {
+ dirtyCount = 0;
+ methodStack = new Stack>();
+ }
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder(64);
+ sb.append("FlushData")
+ .append("[dirtyCount=").append(dirtyCount)
+ .append(", methodStack=").append(methodStack)
+ .append("]");
+ return sb.toString();
+ }
+ public void incrementDirtyCount()
+ {
+ dirtyCount++;
+ }
+ public boolean isDirty()
+ {
+ return dirtyCount > 0;
+ }
+ public void resetDirtyCount()
+ {
+ dirtyCount = 0;
+ }
+ public void pushMethod(String methodName, boolean isAnnotated)
+ {
+ methodStack.push(new Pair(methodName, Boolean.valueOf(isAnnotated)));
+ }
+ public Pair popMethod()
+ {
+ return methodStack.pop();
+ }
+ public Pair currentMethod()
+ {
+ return methodStack.peek();
+ }
+ /**
+ * @return Returns true if all the methods in the method stack are annotated,
+ * otherwise false
+ */
+ public boolean isStackAnnotated()
+ {
+ for (Pair stackElement : methodStack)
+ {
+ if (stackElement.getSecond().equals(Boolean.FALSE))
+ {
+ // Found one that was not annotated
+ return false;
+ }
+ }
+ // All were annotated
+ return true;
+ }
+ }
+
+ /**
+ * @return Returns the transaction-local flush data
+ */
+ private static FlushData getFlushData()
+ {
+ FlushData flushData = (FlushData) AlfrescoTransactionSupport.getResource(KEY_FLUSH_DATA);
+ if (flushData == null)
+ {
+ flushData = new FlushData();
+ AlfrescoTransactionSupport.bindResource(KEY_FLUSH_DATA, flushData);
+ }
+ return flushData;
+ }
+
+ /**
+ * Set the query flush mode according to whether the session is dirty or not.
+ *
+ * @param session the Hibernate session
+ * @param query the Hibernate query that will be issued
+ */
+ public static void setQueryFlushMode(Session session, Query query)
+ {
+ FlushData flushData = DirtySessionMethodInterceptor.getFlushData();
+
+ // If all the methods in the method stack are annotated, then we can adjust the query and
+ // play with the session
+ if (!flushData.isStackAnnotated())
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug(
+ "Method stack is not annotated. Not setting query flush mode: \n" +
+ " Flush Data: " + flushData);
+ }
+ return;
+ }
+
+ // The stack is fully annotated, so flush if required and set the flush mode on the query
+ if (logger.isDebugEnabled())
+ {
+ logger.debug(
+ "Setting query flush mode: \n" +
+ " Query: " + query.getQueryString() + "\n" +
+ " Dirty: " + flushData);
+ }
+
+ if (flushData.isDirty())
+ {
+ // Flush the session
+ session.flush();
+ // Reset the dirty state
+ flushData.resetDirtyCount();
+ }
+ // Adjust the query flush mode
+ query.setFlushMode(FlushMode.MANUAL);
+ }
+
+ /**
+ * Flush and reset the dirty count for the current transaction. The session is
+ * only flushed if it currently dirty.
+ *
+ * @param session the Hibernate session
+ */
+ public static void flushSession(Session session)
+ {
+ flushSession(session, false);
+ }
+
+ /**
+ * Flush and reset the dirty count for the current transaction.
+ * Use this one if you know that the session has changeds that might not
+ * have been recorded by the DAO interceptors.
+ *
+ * @param session the Hibernate session
+ * @param force true to force a flush.
+ */
+ public static void flushSession(Session session, boolean force)
+ {
+ FlushData flushData = DirtySessionMethodInterceptor.getFlushData();
+ if (force)
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug(
+ "Flushing session forcefully: \n" +
+ " Dirty: " + flushData);
+ }
+ session.flush();
+ flushData.resetDirtyCount();
+ }
+ else
+ {
+ if (flushData.isDirty())
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug(
+ "Flushing dirty session: \n" +
+ " Dirty: " + flushData);
+ }
+ session.flush();
+ flushData.resetDirtyCount();
+ }
+ else
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug(
+ "Session is not dirty - no flush: \n" +
+ " Dirty: " + flushData);
+ }
+ }
+ }
+ }
+
+ /** Default constructor */
+ public DirtySessionMethodInterceptor()
+ {
+ }
+
+ public Object invoke(MethodInvocation invocation) throws Throwable
+ {
+ Method method = invocation.getMethod();
+ String methodName = method.getName();
+
+ // Get the flush and dirty mark requirements for the call
+ DirtySessionAnnotation annotation = method.getAnnotation(DirtySessionAnnotation.class);
+ boolean flushBefore = false;
+ boolean flushAfter = false;
+ boolean markDirty = false;
+ if (annotation != null)
+ {
+ flushBefore = annotation.flushBefore();
+ flushAfter = annotation.flushAfter();
+ markDirty = annotation.markDirty();
+ }
+ else if (unannotatedMethodNames.add(methodName))
+ {
+ logger.warn("Method has not been annotated with the DirtySessionAnnotation: " + method);
+ }
+
+ FlushData flushData = DirtySessionMethodInterceptor.getFlushData();
+
+ Session session = null;
+ if (flushBefore || flushAfter)
+ {
+ session = getSession(false);
+ }
+
+ if (flushBefore)
+ {
+ DirtySessionMethodInterceptor.flushSession(session);
+ }
+
+ boolean isAnnotated = (annotation != null);
+ Object ret = null;
+ try
+ {
+ // Push the method onto the stack
+ flushData.pushMethod(methodName, isAnnotated);
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug(
+ "Flush state and parameters for DirtySessionInterceptor: \n" +
+ " Method: " + methodName + "\n" +
+ " Annotated: BEFORE=" + flushBefore + ", AFTER=" + flushAfter + ", MARK-DIRTY=" + markDirty + "\n" +
+ " Session State: " + flushData);
+ }
+
+ // Do the call
+ ret = invocation.proceed();
+
+ if (flushAfter)
+ {
+ DirtySessionMethodInterceptor.flushSession(session);
+ }
+ else if (markDirty)
+ {
+ flushData.incrementDirtyCount();
+ }
+ }
+ finally
+ {
+ // Restore the dirty session awareness state
+ flushData.popMethod();
+ }
+
+ // Done
+ return ret;
+ }
+}
diff --git a/source/java/org/alfresco/repo/domain/hibernate/HibernateNodeTest.java b/source/java/org/alfresco/repo/domain/hibernate/HibernateNodeTest.java
index 26d5d75a78..87db219475 100644
--- a/source/java/org/alfresco/repo/domain/hibernate/HibernateNodeTest.java
+++ b/source/java/org/alfresco/repo/domain/hibernate/HibernateNodeTest.java
@@ -700,4 +700,48 @@ public class HibernateNodeTest extends BaseSpringTest
count++;
}
}
+
+ private static final String GET_NODE =
+ " select"+
+ " node" +
+ " from" +
+ " org.alfresco.repo.domain.hibernate.NodeImpl as node" +
+ " where" +
+ " node.id in (:nodeIds)";
+ @SuppressWarnings("unchecked")
+ public void testPropertiesViaJoin() throws Exception
+ {
+ getSession().setCacheMode(CacheMode.IGNORE);
+
+ List nodeIds = new ArrayList(10);
+
+ for (int i = 0; i < 100; i++)
+ {
+ // make a container node
+ Node node = new NodeImpl();
+ node.setStore(store);
+ node.setUuid(GUID.generate());
+ node.setTypeQName(containerQNameEntity);
+ node.getProperties().put(propAuthorQNameEntity.getId(), new PropertyValue(DataTypeDefinition.TEXT, "ABC"));
+ node.getProperties().put(propArchivedByQNameEntity.getId(), new PropertyValue(DataTypeDefinition.TEXT, "ABC"));
+ Long nodeId = (Long) getSession().save(node);
+ // Keep the ID
+ nodeIds.add(nodeId);
+ }
+ getSession().flush();
+ getSession().clear();
+
+ // Now select it
+ Query query = getSession()
+ .createQuery(GET_NODE)
+ .setParameterList("nodeIds", nodeIds)
+ .setCacheMode(CacheMode.IGNORE);
+ List queryList = (List) query.list();
+
+ for (Node node : queryList)
+ {
+ // Get the node properties - this should not execute a query to retrieve
+ node.getProperties().size();
+ }
+ }
}
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/domain/hibernate/HibernateUsageDeltaDAO.java b/source/java/org/alfresco/repo/domain/hibernate/HibernateUsageDeltaDAO.java
index ba66aa706f..f5973fbbbb 100644
--- a/source/java/org/alfresco/repo/domain/hibernate/HibernateUsageDeltaDAO.java
+++ b/source/java/org/alfresco/repo/domain/hibernate/HibernateUsageDeltaDAO.java
@@ -30,12 +30,13 @@ import java.util.Set;
import org.alfresco.repo.domain.Node;
import org.alfresco.repo.domain.UsageDelta;
+import org.alfresco.repo.domain.UsageDeltaDAO;
import org.alfresco.repo.node.db.NodeDaoService;
import org.alfresco.repo.transaction.TransactionalDao;
-import org.alfresco.repo.usage.UsageDeltaDAO;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.util.GUID;
+import org.alfresco.util.Pair;
import org.alfresco.util.ParameterCheck;
import org.hibernate.Query;
import org.hibernate.Session;
@@ -130,22 +131,22 @@ public class HibernateUsageDeltaDAO extends HibernateDaoSupport implements Usage
getSession().flush();
}
- private Node getNodeNotNull(NodeRef nodeRef) throws InvalidNodeRefException
+ private Long getNodeIdNotNull(NodeRef nodeRef) throws InvalidNodeRefException
{
ParameterCheck.mandatory("nodeRef", nodeRef);
- Node unchecked = nodeDaoService.getNode(nodeRef);
- if (unchecked == null)
+ Pair nodePair = nodeDaoService.getNodePair(nodeRef);
+ if (nodePair == null)
{
throw new InvalidNodeRefException("Node does not exist: " + nodeRef, nodeRef);
}
- return unchecked;
+ return nodePair.getFirst();
}
public int deleteDeltas(NodeRef nodeRef)
{
- Node node = getNodeNotNull(nodeRef);
- return deleteDeltas(node.getId());
+ Long nodeId = getNodeIdNotNull(nodeRef);
+ return deleteDeltas(nodeId);
}
@SuppressWarnings("unchecked")
@@ -170,13 +171,13 @@ public class HibernateUsageDeltaDAO extends HibernateDaoSupport implements Usage
@SuppressWarnings("unchecked")
public long getTotalDeltaSize(NodeRef nodeRef)
{
- final Node node = getNodeNotNull(nodeRef);
+ final Long nodeId = getNodeIdNotNull(nodeRef);
HibernateCallback callback = new HibernateCallback()
{
public Object doInHibernate(Session session)
{
Query query = session.getNamedQuery(QUERY_GET_TOTAL_DELTA_SIZE);
- query.setParameter("node", node);
+ query.setParameter("nodeId", nodeId);
query.setReadOnly(true);
return query.uniqueResult();
}
@@ -189,7 +190,8 @@ public class HibernateUsageDeltaDAO extends HibernateDaoSupport implements Usage
public void insertDelta(NodeRef usageNodeRef, long deltaSize)
{
- Node node = getNodeNotNull(usageNodeRef);
+ Long nodeId = getNodeIdNotNull(usageNodeRef);
+ Node node = (Node) getHibernateTemplate().get(NodeImpl.class, nodeId);
UsageDelta delta = new UsageDeltaImpl();
// delta properties
diff --git a/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml b/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml
index c9bee9c0a9..9fa2a14a65 100644
--- a/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml
+++ b/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml
@@ -28,7 +28,7 @@
name="store"
class="org.alfresco.repo.domain.hibernate.StoreImpl"
not-null="true"
- lazy="proxy"
+ lazy="proxy"
foreign-key="fk_alf_n_store"
optimistic-lock="true"
fetch="join">
@@ -157,20 +157,34 @@
-
-
+
+ not-null="true" >
+
+
+
+
+
+
+
-
-
-
-
@@ -243,17 +243,17 @@
not-null="true" >
-
-
+
+
@@ -282,12 +282,37 @@
from
org.alfresco.repo.domain.hibernate.ChildAssocImpl as assoc
where
- assoc.child = :child
+ assoc.child.id = :childId
order by
assoc.index,
assoc.id
+
+ delete
+ from
+ org.alfresco.repo.domain.hibernate.ChildAssocImpl as assoc
+ where
+ assoc.child.id = :childId
+
+
+
+ delete
+ from
+ org.alfresco.repo.domain.hibernate.ChildAssocImpl as assoc
+ where
+ assoc.parent.id = :parentId
+
+
+
+ delete
+ from
+ org.alfresco.repo.domain.hibernate.NodeAssocImpl as assoc
+ where
+ assoc.source.id = :nodeId or
+ assoc.target.id = :nodeId
+
+
select
status
@@ -296,9 +321,21 @@
org.alfresco.repo.domain.hibernate.ChildAssocImpl as assoc
join assoc.child as child
where
- assoc.parent = :parent and
+ assoc.parent.id = :parentId and
assoc.isPrimary = true and
- status.node = child
+ status.node.id = childId
+
+
+
+ select
+ child.id
+ from
+ org.alfresco.repo.domain.hibernate.ChildAssocImpl as assoc
+ join assoc.child as child
+ where
+ assoc.parent.id = :parentId
+ order by
+ child.id
@@ -307,8 +344,8 @@
from
org.alfresco.repo.domain.hibernate.ChildAssocImpl as assoc
where
- assoc.parent = :parent and
- assoc.child = :child and
+ assoc.parent.id = :parentId and
+ assoc.child.id = :childId and
assoc.typeQName = :typeQName and
assoc.qnameNamespace = :qnameNamespace and
assoc.qnameLocalName = :qnameLocalName
@@ -323,7 +360,7 @@
from
org.alfresco.repo.domain.hibernate.ChildAssocImpl as assoc
where
- assoc.parent = :parent
+ assoc.parent.id = :parentId
order by
assoc.index,
assoc.id
@@ -335,7 +372,7 @@
from
org.alfresco.repo.domain.hibernate.ChildAssocImpl as assoc
where
- assoc.parent = :parent and
+ assoc.parent.id = :parentId and
assoc.typeQName = :typeQName and
assoc.childNodeName = :childNodeName and
assoc.childNodeNameCrc = :childNodeNameCrc
@@ -347,7 +384,7 @@
from
org.alfresco.repo.domain.hibernate.ChildAssocImpl as assoc
where
- assoc.parent = :parent and
+ assoc.parent.id = :parentId and
assoc.typeQName = :typeQName and
assoc.childNodeName = :childNodeName and
assoc.childNodeNameCrc = :childNodeNameCrc
@@ -358,6 +395,7 @@
select
+ assoc.id,
assoc.typeQName,
assoc.qnameNamespace,
assoc.qnameLocalName,
@@ -372,7 +410,7 @@
join assoc.parent as parent
join assoc.child as child
where
- assoc.parent = :parent
+ assoc.parent.id = :parentId
order by
assoc.index,
assoc.id
@@ -380,6 +418,7 @@
select
+ assoc.id,
assoc.typeQName,
assoc.qnameNamespace,
assoc.qnameLocalName,
@@ -394,7 +433,7 @@
join assoc.parent as parent
join assoc.child as child
where
- assoc.parent = :parent and
+ assoc.parent.id = :parentId and
assoc.qnameNamespace = :qnameNamespace and
assoc.qnameLocalName = :qnameLocalName
order by
@@ -402,14 +441,152 @@
assoc.id
+
+ select
+ assoc.id,
+ assoc.typeQName,
+ assoc.qnameNamespace,
+ assoc.qnameLocalName,
+ assoc.isPrimary,
+ assoc.index,
+ child.id,
+ child.store.key.protocol,
+ child.store.key.identifier,
+ child.uuid
+ from
+ org.alfresco.repo.domain.hibernate.ChildAssocImpl as assoc
+ join assoc.parent as parent
+ join assoc.child as child
+ where
+ assoc.parent.id = :parentId and
+ assoc.typeQName.id in (:childAssocTypeQNameIds)
+ order by
+ assoc.index,
+ assoc.id
+
+
+
+ select
+ assoc.id,
+ assoc.typeQName,
+ assoc.qnameNamespace,
+ assoc.qnameLocalName,
+ assoc.isPrimary,
+ assoc.index,
+ child.id,
+ child.store.key.protocol,
+ child.store.key.identifier,
+ child.uuid
+ from
+ org.alfresco.repo.domain.hibernate.ChildAssocImpl as assoc
+ join assoc.parent as parent
+ join assoc.child as child
+ where
+ assoc.parent.id = :parentId and
+ assoc.typeQName = :typeQName and
+ assoc.qnameNamespace = :qnameNamespace and
+ assoc.qnameLocalName = :qnameLocalName
+ order by
+ assoc.index,
+ assoc.id
+
+
+
+ select
+ assoc.id,
+ assoc.typeQName,
+ assoc.qnameNamespace,
+ assoc.qnameLocalName,
+ assoc.isPrimary,
+ assoc.index,
+ child.id,
+ child.store.key.protocol,
+ child.store.key.identifier,
+ child.uuid
+ from
+ org.alfresco.repo.domain.hibernate.ChildAssocImpl as assoc
+ join assoc.parent as parent
+ join assoc.child as child
+ where
+ assoc.parent.id = :parentId and
+ assoc.isPrimary = true
+ order by
+ assoc.index,
+ assoc.id
+
+
+
+ select
+ assoc.id,
+ assoc.typeQName,
+ assoc.qnameNamespace,
+ assoc.qnameLocalName,
+ assoc.isPrimary,
+ assoc.index,
+ child.id,
+ child.store.key.protocol,
+ child.store.key.identifier,
+ child.uuid
+ from
+ org.alfresco.repo.domain.hibernate.ChildAssocImpl as assoc
+ join assoc.parent as parent
+ join assoc.child as child
+ where
+ assoc.parent.id = :parentId and
+ assoc.isPrimary = true and
+ (
+ child.store.key.protocol != parent.store.key.protocol or
+ child.store.key.identifier != parent.store.key.identifier
+ )
+ order by
+ assoc.index,
+ assoc.id
+
+
+
+ select
+ parent.id,
+ parent.store.key.protocol,
+ parent.store.key.identifier,
+ parent.uuid
+ from
+ org.alfresco.repo.domain.hibernate.ChildAssocImpl as assoc
+ join assoc.parent as parent
+ join assoc.child as child
+ where
+ parent.id > :minNodeId and
+ assoc.isPrimary = true and
+ (
+ child.store.key.protocol != parent.store.key.protocol or
+ child.store.key.identifier != parent.store.key.identifier
+ )
+ order by
+ parent.id
+
+
+
+ select
+ node.id,
+ node.store.key.protocol,
+ node.store.key.identifier,
+ node.uuid
+ from
+ org.alfresco.repo.domain.hibernate.NodeImpl as node
+ where
+ node.id > :minNodeId and
+ node.aspects.id = :aspectQName
+ order by
+ node.id
+
+
select
assoc
from
org.alfresco.repo.domain.hibernate.NodeAssocImpl as assoc
where
- assoc.source = :source and
- assoc.target = :target and
+ assoc.source.id = :sourceId and
+ assoc.target.id = :targetId and
assoc.typeQName = :assocTypeQName
@@ -419,8 +596,8 @@
from
org.alfresco.repo.domain.hibernate.NodeAssocImpl as assoc
where
- assoc.source = :node or
- assoc.target = :node
+ assoc.source.id = :nodeId or
+ assoc.target.id = :nodeId
@@ -431,7 +608,7 @@
join assoc.source as source
join assoc.target as target
where
- assoc.source = :source
+ assoc.source.id = :sourceId
@@ -442,7 +619,21 @@
join assoc.source as source
join assoc.target as target
where
- assoc.target = :target
+ assoc.target.id = :targetId
+
+
+
+ select
+ node,
+ node.typeQName
+ from
+ org.alfresco.repo.domain.hibernate.NodeImpl as node
+ join node.properties prop
+ where
+ node.store.key.protocol = :protocol and
+ node.store.key.identifier = :identifier and
+ index(prop) = :propQNameId and
+ prop.stringValue = :propStringValue
@@ -545,17 +736,4 @@
node.store.key.identifier = :identifier
-
- select
- node
- from
- org.alfresco.repo.domain.hibernate.NodeImpl as node
- join node.properties prop
- where
- node.store.key.protocol = :protocol and
- node.store.key.identifier = :identifier and
- index(prop) = :propQNameId and
- prop.stringValue = :propStringValue
-
-
diff --git a/source/java/org/alfresco/repo/domain/hibernate/NodeAccessControlListDAO.java b/source/java/org/alfresco/repo/domain/hibernate/NodeAccessControlListDAO.java
index f25f94acf4..a1355854e3 100644
--- a/source/java/org/alfresco/repo/domain/hibernate/NodeAccessControlListDAO.java
+++ b/source/java/org/alfresco/repo/domain/hibernate/NodeAccessControlListDAO.java
@@ -28,22 +28,23 @@ import java.util.List;
import java.util.Map;
import org.alfresco.repo.domain.AccessControlListDAO;
-import org.alfresco.repo.domain.ChildAssoc;
import org.alfresco.repo.domain.DbAccessControlList;
-import org.alfresco.repo.domain.Node;
import org.alfresco.repo.node.db.NodeDaoService;
import org.alfresco.repo.security.permissions.ACLType;
import org.alfresco.repo.security.permissions.impl.AclChange;
+import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
+import org.alfresco.util.Pair;
+import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
/**
* The Node implementation for getting and setting ACLs.
*
* @author britt
*/
-public class NodeAccessControlListDAO implements AccessControlListDAO
+public class NodeAccessControlListDAO extends HibernateDaoSupport implements AccessControlListDAO
{
/**
* The DAO for Nodes.
@@ -62,41 +63,38 @@ public class NodeAccessControlListDAO implements AccessControlListDAO
fNodeDAOService = nodeDAOService;
}
- /**
- * Get the ACL from a node.
- *
- * @param nodeRef
- * The reference to the node.
- * @return The ACL.
- * @throws InvalidNodeRefException
- */
+ private Pair getNodePairNotNull(NodeRef nodeRef)
+ {
+ Pair nodePair = fNodeDAOService.getNodePair(nodeRef);
+ if (nodePair == null)
+ {
+ throw new InvalidNodeRefException(nodeRef);
+ }
+ return nodePair;
+ }
+
public DbAccessControlList getAccessControlList(NodeRef nodeRef)
{
- Node node = fNodeDAOService.getNode(nodeRef);
- if (node == null)
+ Pair nodePair = getNodePairNotNull(nodeRef);
+ Long aclId = fNodeDAOService.getNodeAccessControlList(nodePair.getFirst());
+ // Now get the entity
+ DbAccessControlList acl;
+ if (aclId == null)
{
- throw new InvalidNodeRefException(nodeRef);
+ return null;
}
- return node.getAccessControlList();
+ else
+ {
+ acl = (DbAccessControlList) getHibernateTemplate().get(DbAccessControlListImpl.class, aclId);
+ }
+ return acl;
}
- /**
- * Set the ACL on a node.
- *
- * @param nodeRef
- * The reference to the node.
- * @param acl
- * The ACL.
- * @throws InvalidNodeRefException
- */
public void setAccessControlList(NodeRef nodeRef, DbAccessControlList acl)
{
- Node node = fNodeDAOService.getNode(nodeRef);
- if (node == null)
- {
- throw new InvalidNodeRefException(nodeRef);
- }
- node.setAccessControlList(acl);
+ Pair nodePair = getNodePairNotNull(nodeRef);
+ Long aclId = (acl == null) ? null : acl.getId();
+ fNodeDAOService.setNodeAccessControlList(nodePair.getFirst(), aclId);
}
public void updateChangedAcls(NodeRef startingPoint, List changes)
@@ -112,24 +110,22 @@ public class NodeAccessControlListDAO implements AccessControlListDAO
public Long getIndirectAcl(NodeRef nodeRef)
{
- return getAccessControlList(nodeRef).getId();
+ DbAccessControlList acl = getAccessControlList(nodeRef);
+ return (acl == null) ? null : acl.getId();
}
public Long getInheritedAcl(NodeRef nodeRef)
{
- Node node = fNodeDAOService.getNode(nodeRef);
- ChildAssoc ca = fNodeDAOService.getPrimaryParentAssoc(node);
- if ((ca != null) && (ca.getParent() != null))
+ Pair nodePair = fNodeDAOService.getNodePair(nodeRef);
+ if (nodePair == null)
{
- DbAccessControlList acl = getAccessControlList(ca.getParent().getNodeRef());
- if (acl != null)
- {
- return acl.getId();
- }
- else
- {
- return null;
- }
+ throw new InvalidNodeRefException(nodeRef);
+ }
+ Pair caPair = fNodeDAOService.getPrimaryParentAssoc(nodePair.getFirst());
+ if ((caPair != null) && (caPair.getSecond().getParentRef() != null))
+ {
+ Long aclId = fNodeDAOService.getNodeAccessControlList(caPair.getFirst());
+ return aclId;
}
else
{
@@ -157,5 +153,8 @@ public class NodeAccessControlListDAO implements AccessControlListDAO
throw new UnsupportedOperationException();
}
-
+ public void setAccessControlList(NodeRef nodeRef, Long aclId)
+ {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/source/java/org/alfresco/repo/domain/hibernate/OldADMPermissionsDaoComponentImpl.java b/source/java/org/alfresco/repo/domain/hibernate/OldADMPermissionsDaoComponentImpl.java
index 751818699d..1455b00522 100644
--- a/source/java/org/alfresco/repo/domain/hibernate/OldADMPermissionsDaoComponentImpl.java
+++ b/source/java/org/alfresco/repo/domain/hibernate/OldADMPermissionsDaoComponentImpl.java
@@ -93,7 +93,7 @@ public class OldADMPermissionsDaoComponentImpl extends AbstractPermissionsDaoCom
if (acl != null)
{
// maintain referencial integrity
- getACLDAO(nodeRef).setAccessControlList(nodeRef, null);
+ getACLDAO(nodeRef).setAccessControlList(nodeRef, (Long) null);
aclDaoComponent.deleteAccessControlList(acl.getId());
}
}
diff --git a/source/java/org/alfresco/repo/domain/hibernate/SessionSizeResourceManager.java b/source/java/org/alfresco/repo/domain/hibernate/SessionSizeResourceManager.java
index c7bf26100c..6829593164 100644
--- a/source/java/org/alfresco/repo/domain/hibernate/SessionSizeResourceManager.java
+++ b/source/java/org/alfresco/repo/domain/hibernate/SessionSizeResourceManager.java
@@ -144,7 +144,7 @@ public class SessionSizeResourceManager extends HibernateDaoSupport implements M
int collectionCount = stats.getCollectionCount();
if ((entityCount + collectionCount) > threshold)
{
- session.flush();
+ DirtySessionMethodInterceptor.flushSession(session, true);
selectivelyClear(session, stats);
// session.clear();
if (logger.isDebugEnabled())
diff --git a/source/java/org/alfresco/repo/domain/hibernate/TransactionImpl.java b/source/java/org/alfresco/repo/domain/hibernate/TransactionImpl.java
index d9ca8b0f78..a0c83f2d6a 100644
--- a/source/java/org/alfresco/repo/domain/hibernate/TransactionImpl.java
+++ b/source/java/org/alfresco/repo/domain/hibernate/TransactionImpl.java
@@ -59,7 +59,7 @@ public class TransactionImpl extends LifecycleAdapter implements Transaction, Se
StringBuilder sb = new StringBuilder(50);
sb.append("Transaction")
.append("[id=").append(id)
- .append(", txnTimeMs=").append(new Date(commitTimeMs))
+ .append(", txnTimeMs=").append(commitTimeMs == null ? "---" : new Date(commitTimeMs))
.append(", changeTxnId=").append(changeTxnId)
.append("]");
return sb.toString();
diff --git a/source/java/org/alfresco/repo/domain/hibernate/UsageDelta.hbm.xml b/source/java/org/alfresco/repo/domain/hibernate/UsageDelta.hbm.xml
index e332309765..4a24851750 100644
--- a/source/java/org/alfresco/repo/domain/hibernate/UsageDelta.hbm.xml
+++ b/source/java/org/alfresco/repo/domain/hibernate/UsageDelta.hbm.xml
@@ -54,7 +54,7 @@
from
org.alfresco.repo.domain.hibernate.UsageDeltaImpl as usage_delta
where
- usage_delta.node = :node
+ usage_delta.node.id = :nodeId
@@ -73,7 +73,7 @@
from
org.alfresco.repo.domain.hibernate.UsageDeltaImpl as usage_delta
where
- usage_delta.node = :node
+ usage_delta.node.id = :nodeId
diff --git a/source/java/org/alfresco/repo/node/AbstractNodeServiceImpl.java b/source/java/org/alfresco/repo/node/AbstractNodeServiceImpl.java
index 69fac54219..86ee356f9d 100644
--- a/source/java/org/alfresco/repo/node/AbstractNodeServiceImpl.java
+++ b/source/java/org/alfresco/repo/node/AbstractNodeServiceImpl.java
@@ -31,6 +31,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.locks.ReentrantLock;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.domain.PropertyValue;
@@ -60,6 +61,8 @@ import org.alfresco.repo.policy.AssociationPolicyDelegate;
import org.alfresco.repo.policy.ClassPolicyDelegate;
import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.repo.search.Indexer;
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.service.cmr.dictionary.ClassDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryException;
@@ -75,7 +78,9 @@ import org.alfresco.service.cmr.repository.datatype.TypeConversionException;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.QNamePattern;
import org.alfresco.service.namespace.RegexQNamePattern;
+import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.GUID;
+import org.alfresco.util.PropertyMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -102,6 +107,7 @@ public abstract class AbstractNodeServiceImpl implements NodeService
/** controls policy delegates */
private PolicyComponent policyComponent;
protected DictionaryService dictionaryService;
+ protected TransactionService transactionService;
/*
* Policy delegates
@@ -147,6 +153,11 @@ public abstract class AbstractNodeServiceImpl implements NodeService
this.dictionaryService = dictionaryService;
}
+ public void setTransactionService(TransactionService transactionService)
+ {
+ this.transactionService = transactionService;
+ }
+
/**
* Checks equality by type and uuid
*/
@@ -564,8 +575,10 @@ public abstract class AbstractNodeServiceImpl implements NodeService
}
else
{
- // remove the property as we don't want to persist it
- preCreationProperties.remove(ContentModel.PROP_NODE_UUID);
+ if (uuid.length() > 50)
+ {
+ throw new IllegalArgumentException("Explicit UUID may not be greater than 50 characters: " + uuid);
+ }
}
// done
return uuid;
@@ -701,21 +714,28 @@ public abstract class AbstractNodeServiceImpl implements NodeService
e);
}
}
+
+ protected Map getDefaultProperties(QName typeQName)
+ {
+ ClassDefinition classDefinition = this.dictionaryService.getClass(typeQName);
+ if (classDefinition == null)
+ {
+ return Collections.emptyMap();
+ }
+ return getDefaultProperties(classDefinition);
+ }
+
/**
* Sets the default property values
*
* @param classDefinition the model type definition for which to get defaults
* @param properties the properties of the node
*/
- protected void addDefaultPropertyValues(ClassDefinition classDefinition, Map properties)
+ protected Map getDefaultProperties(ClassDefinition classDefinition)
{
+ PropertyMap properties = new PropertyMap();
for (Map.Entry entry : classDefinition.getDefaultValues().entrySet())
{
- if (properties.containsKey(entry.getKey()))
- {
- // property is present
- continue;
- }
Serializable value = entry.getValue();
// Check the type of the default property
@@ -747,5 +767,49 @@ public abstract class AbstractNodeServiceImpl implements NodeService
// Set the default value of the property
properties.put(entry.getKey(), value);
}
+ return properties;
+ }
+
+ /**
+ * Override to implement cleanup processes. The default does nothing.
+ *
+ * This method will be called as the system user but without any
+ * additional transactions.
+ */
+ protected List cleanupImpl()
+ {
+ // No operation
+ return Collections.emptyList();
+ }
+
+ /** Prevent multiple executions of the implementation method */
+ private ReentrantLock cleanupLock = new ReentrantLock();
+ public final List cleanup()
+ {
+ boolean locked = cleanupLock.tryLock();
+ if (locked)
+ {
+ try
+ {
+ // Authenticate as system
+ RunAsWork> cleanupWork = new RunAsWork>()
+ {
+ public List doWork() throws Exception
+ {
+ // The current thread got the lock
+ return cleanupImpl();
+ }
+ };
+ return AuthenticationUtil.runAs(cleanupWork, AuthenticationUtil.SYSTEM_USER_NAME);
+ }
+ finally
+ {
+ cleanupLock.unlock();
+ }
+ }
+ else
+ {
+ return Collections.emptyList();
+ }
}
}
diff --git a/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java b/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java
index 1150f7869a..81227b6f05 100644
--- a/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java
+++ b/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java
@@ -248,6 +248,7 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
*
* return (NodeService) applicationContext.getBean("dbNodeService");
*
+ * The NodeService returned must support cascade deletion.
*
* @return Returns the implementation of NodeService
to be
* used for this test. It must have transaction demarcation.
@@ -674,9 +675,9 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
nodeService.removeAspect(sourceNodeRef, ASPECT_WITH_ASSOCIATIONS);
// Check that the associations were removed
- assertEquals("Expected exactly one child",
+ assertEquals("Expected exactly zero child",
0, nodeService.getChildAssocs(sourceNodeRef).size());
- assertEquals("Expected exactly one target",
+ assertEquals("Expected exactly zero target",
0, nodeService.getTargetAssocs(sourceNodeRef, RegexQNamePattern.MATCH_ALL).size());
}
diff --git a/source/java/org/alfresco/repo/node/FullNodeServiceTest.java b/source/java/org/alfresco/repo/node/FullNodeServiceTest.java
index 2258edd0b2..8b7986f358 100644
--- a/source/java/org/alfresco/repo/node/FullNodeServiceTest.java
+++ b/source/java/org/alfresco/repo/node/FullNodeServiceTest.java
@@ -28,6 +28,7 @@ import java.io.Serializable;
import java.util.Locale;
import java.util.Map;
+import org.alfresco.repo.node.db.DbNodeServiceImpl;
import org.alfresco.service.cmr.repository.MLText;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
@@ -45,6 +46,10 @@ public class FullNodeServiceTest extends BaseNodeServiceTest
{
protected NodeService getNodeService()
{
+ // Force cascading
+ DbNodeServiceImpl dbNodeServiceImpl = (DbNodeServiceImpl) applicationContext.getBean("dbNodeServiceImpl");
+ dbNodeServiceImpl.setCascadeInTransaction(true);
+
return (NodeService) applicationContext.getBean("NodeService");
}
diff --git a/source/java/org/alfresco/repo/node/archive/ArchiveAndRestoreTest.java b/source/java/org/alfresco/repo/node/archive/ArchiveAndRestoreTest.java
index a30192e821..46b4b3d7d9 100644
--- a/source/java/org/alfresco/repo/node/archive/ArchiveAndRestoreTest.java
+++ b/source/java/org/alfresco/repo/node/archive/ArchiveAndRestoreTest.java
@@ -38,6 +38,7 @@ import org.alfresco.model.ContentModel;
import org.alfresco.repo.domain.hibernate.SessionSizeResourceManager;
import org.alfresco.repo.node.StoreArchiveMap;
import org.alfresco.repo.node.archive.RestoreNodeReport.RestoreStatus;
+import org.alfresco.repo.node.db.DbNodeServiceImpl;
import org.alfresco.repo.node.integrity.IntegrityChecker;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.service.ServiceRegistry;
@@ -109,6 +110,10 @@ public class ArchiveAndRestoreTest extends TestCase
@Override
public void setUp() throws Exception
{
+ // Force cascading
+ DbNodeServiceImpl dbNodeServiceImpl = (DbNodeServiceImpl) ctx.getBean("dbNodeServiceImpl");
+ dbNodeServiceImpl.setCascadeInTransaction(true);
+
ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean("ServiceRegistry");
nodeArchiveService = (NodeArchiveService) ctx.getBean("nodeArchiveService");
nodeService = serviceRegistry.getNodeService();
@@ -349,8 +354,8 @@ public class ArchiveAndRestoreTest extends TestCase
// check
verifyNodeExistence(b, true);
verifyNodeExistence(bb, false);
- verifyChildAssocExistence(childAssocAtoBB, false);
- verifyChildAssocExistence(childAssocBtoBB, false);
+// verifyChildAssocExistence(childAssocAtoBB, false);
+// verifyChildAssocExistence(childAssocBtoBB, false);
verifyNodeExistence(b_, false);
verifyNodeExistence(bb_, true);
@@ -375,12 +380,12 @@ public class ArchiveAndRestoreTest extends TestCase
// check
verifyNodeExistence(b, false);
verifyNodeExistence(bb, false);
- verifyChildAssocExistence(childAssocAtoBB, false);
- verifyTargetAssocExistence(assocAtoB, false);
- verifyTargetAssocExistence(assocAAtoBB, false);
+// verifyChildAssocExistence(childAssocAtoBB, false);
+// verifyTargetAssocExistence(assocAtoB, false);
+// verifyTargetAssocExistence(assocAAtoBB, false);
verifyNodeExistence(b_, true);
verifyNodeExistence(bb_, true);
- verifyChildAssocExistence(childAssocBtoBB_, true);
+// verifyChildAssocExistence(childAssocBtoBB_, true);
// flush
//AlfrescoTransactionSupport.flush();
@@ -443,10 +448,10 @@ public class ArchiveAndRestoreTest extends TestCase
verifyNodeExistence(bb, true);
verifyChildAssocExistence(childAssocAtoAA, true);
verifyChildAssocExistence(childAssocBtoBB, true);
- verifyChildAssocExistence(childAssocAtoBB, false);
- verifyChildAssocExistence(childAssocBtoAA, false);
- verifyTargetAssocExistence(assocAtoB, false);
- verifyTargetAssocExistence(assocAAtoBB, false);
+// verifyChildAssocExistence(childAssocAtoBB, false);
+// verifyChildAssocExistence(childAssocBtoAA, false);
+// verifyTargetAssocExistence(assocAtoB, false);
+// verifyTargetAssocExistence(assocAAtoBB, false);
verifyNodeExistence(a_, false);
verifyNodeExistence(b_, false);
verifyNodeExistence(aa_, false);
diff --git a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java
index 0be8ddfc8b..31020c5e1b 100644
--- a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java
+++ b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java
@@ -39,26 +39,15 @@ 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.DbAccessControlList;
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.QNameDAO;
-import org.alfresco.repo.domain.QNameEntity;
-import org.alfresco.repo.domain.Store;
-import org.alfresco.repo.domain.hibernate.DMAccessControlListDAO;
-import org.alfresco.repo.domain.hibernate.DMPermissionsDaoComponentImpl;
import org.alfresco.repo.node.AbstractNodeServiceImpl;
import org.alfresco.repo.node.StoreArchiveMap;
+import org.alfresco.repo.node.db.NodeDaoService.NodeRefQueryCallback;
+import org.alfresco.repo.node.index.NodeIndexer;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
-import org.alfresco.repo.security.permissions.ACLType;
-import org.alfresco.repo.security.permissions.AccessControlListProperties;
-import org.alfresco.repo.security.permissions.SimpleAccessControlListProperties;
-import org.alfresco.repo.security.permissions.impl.AclDaoComponent;
-import org.alfresco.repo.security.permissions.impl.PermissionsDaoComponent;
-import org.alfresco.repo.tenant.TenantService;
+import org.alfresco.repo.transaction.RetryingTransactionHelper;
+import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.dictionary.AspectDefinition;
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition;
@@ -78,17 +67,19 @@ 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.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.QNamePattern;
import org.alfresco.service.namespace.RegexQNamePattern;
+import org.alfresco.util.EqualsHelper;
+import org.alfresco.util.GUID;
+import org.alfresco.util.Pair;
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;
/**
@@ -99,32 +90,18 @@ import org.springframework.util.Assert;
public class DbNodeServiceImpl extends AbstractNodeServiceImpl
{
private static Log logger = LogFactory.getLog(DbNodeServiceImpl.class);
-
private static Log loggerPaths = LogFactory.getLog(DbNodeServiceImpl.class.getName() + ".paths");
-
- private QNameDAO qnameDAO;
-
+
private NodeDaoService nodeDaoService;
-
private StoreArchiveMap storeArchiveMap;
-
private NodeService avmNodeService;
-
- private TenantService tenantService;
-
- private AclDaoComponent aclDaoComponent;
-
+ private NodeIndexer nodeIndexer;
+ private boolean cascadeInTransaction;
+
public DbNodeServiceImpl()
{
- storeArchiveMap = new StoreArchiveMap(); // in case it is not set
- }
-
- /**
- * Set the component for creating QName entities.
- */
- public void setQnameDAO(QNameDAO qnameDAO)
- {
- this.qnameDAO = qnameDAO;
+ storeArchiveMap = new StoreArchiveMap(); // in case it is not set
+ cascadeInTransaction = true;
}
public void setNodeDaoService(NodeDaoService nodeDaoService)
@@ -142,89 +119,66 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
this.avmNodeService = avmNodeService;
}
- public void setTenantService(TenantService tenantService)
+ /**
+ * @param nodeIndexer the indexer that will be notified of node additions,
+ * modifications and deletions
+ */
+ public void setNodeIndexer(NodeIndexer nodeIndexer)
{
- this.tenantService = tenantService;
+ this.nodeIndexer = nodeIndexer;
}
- public void setAclDaoComponent(AclDaoComponent aclDaoComponent)
+ /**
+ * Set whether store delete and archive operations must cascade to all children
+ * in the same transaction.
+ *
+ * @param cascadeInTransaction true (default) to cascade during
+ * delete and archive
+ */
+ public void setCascadeInTransaction(boolean cascadeInTransaction)
{
- this.aclDaoComponent = aclDaoComponent;
+ this.cascadeInTransaction = cascadeInTransaction;
}
/**
* Performs a null-safe get of the node
*
- * @param nodeRef
- * the node to retrieve
+ * @param nodeRef the node to retrieve
* @return Returns the node entity (never null)
- * @throws InvalidNodeRefException
- * if the referenced node could not be found
+ * @throws InvalidNodeRefException if the referenced node could not be found
*/
- private Node getNodeNotNull(NodeRef nodeRef) throws InvalidNodeRefException
+ private Pair getNodePairNotNull(NodeRef nodeRef) throws InvalidNodeRefException
{
ParameterCheck.mandatory("nodeRef", nodeRef);
-
- Node unchecked = nodeDaoService.getNode(nodeRef);
+
+ Pair unchecked = nodeDaoService.getNodePair(nodeRef);
if (unchecked == null)
{
throw new InvalidNodeRefException("Node does not exist: " + nodeRef, nodeRef);
}
return unchecked;
}
-
- /**
- * Gets the node status for a live node.
- *
- * @param nodeRef
- * the node reference
- * @return Returns the node status, which will not be null and will have a live node attached.
- * @throws InvalidNodeRefException
- * if the node is deleted or never existed
- */
- public NodeStatus getNodeStatusNotNull(NodeRef nodeRef) throws InvalidNodeRefException
- {
- ParameterCheck.mandatory("nodeRef", nodeRef);
-
- NodeStatus nodeStatus = nodeDaoService.getNodeStatus(nodeRef, false);
- if (nodeStatus == null || nodeStatus.getNode() == null)
- {
- throw new InvalidNodeRefException("Node does not exist: " + nodeRef, nodeRef);
- }
- return nodeStatus;
- }
-
+
public boolean exists(StoreRef storeRef)
{
- Store store = nodeDaoService.getStore(storeRef.getProtocol(), storeRef.getIdentifier());
- boolean exists = (store != null);
- // done
- return exists;
+ return (nodeDaoService.getRootNode(storeRef) != null);
}
-
+
public boolean exists(NodeRef nodeRef)
{
ParameterCheck.mandatory("nodeRef", nodeRef);
-
- Node node = nodeDaoService.getNode(nodeRef);
- boolean exists = (node != null);
+
+ Pair nodePair = nodeDaoService.getNodePair(nodeRef);
+ boolean exists = (nodePair != null);
// done
return exists;
}
-
+
public Status getNodeStatus(NodeRef nodeRef)
{
ParameterCheck.mandatory("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());
- }
+ NodeRef.Status status = nodeDaoService.getNodeRefStatus(nodeRef);
+ return status;
}
/**
@@ -232,433 +186,318 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
*/
public List getStores()
{
- List stores = nodeDaoService.getStores();
- List storeRefs = new ArrayList(stores.size());
- for (Store store : stores)
- {
- StoreRef storeRef = store.getStoreRef();
- try
- {
- if (tenantService.isEnabled())
- {
- String currentUser = AuthenticationUtil.getCurrentUserName();
-
- // MT: return tenant stores only (although for super System return all stores - as used by
- // ConfigurationChecker, IndexRecovery, IndexBackup etc)
- if ((currentUser == null) || (!currentUser.equals(AuthenticationUtil.getSystemUserName())))
- {
- tenantService.checkDomain(storeRef.getIdentifier());
- storeRef = tenantService.getBaseName(storeRef);
- }
- }
-
- storeRefs.add(storeRef);
- }
- catch (RuntimeException re)
- {
- // deliberately ignore - stores in different domain will not be listed
- }
- }
+ // Get the ADM stores
+ List storeRefs = nodeDaoService.getStoreRefs();
// 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();
- NodeRef rootNodeRef = tenantService.getBaseName(rootNode.getNodeRef());
-
- // assign the root aspect - this is expected of all roots, even store roots
- addAspect(rootNodeRef, ContentModel.ASPECT_ROOT, Collections. emptyMap());
-
- // Bind root permission
-
- if (aclDaoComponent != null)
- {
- SimpleAccessControlListProperties properties = DMPermissionsDaoComponentImpl.getDefaultProperties();
- Long id = aclDaoComponent.createAccessControlList(properties);
- DbAccessControlList acl = aclDaoComponent.getDbAccessControlList(id);
- rootNode.setAccessControlList(acl);
- }
-
+ Pair rootNodePair = nodeDaoService.createStore(storeRef);
+ NodeRef rootNodeRef = rootNodePair.getSecond();
+
// invoke policies
invokeOnCreateStore(rootNodeRef);
-
- // done
- if (!store.getStoreRef().equals(storeRef))
- {
- throw new RuntimeException("Incorrect store reference");
- }
-
+
+ // Index
+ ChildAssociationRef assocRef = new ChildAssociationRef(null, null, null, rootNodeRef);
+ nodeIndexer.indexCreateNode(assocRef);
+
+ // Done
return storeRef;
}
-
+
/**
- * @see NodeDaoService#deleteStore(String, String)
+ * @throws UnsupportedOperationException Always
*/
- public void deleteStore(StoreRef storeRef)
+ public void deleteStore(StoreRef storeRef) throws InvalidStoreRefException
{
- String protocol = storeRef.getProtocol();
- String identifier = storeRef.getIdentifier();
-
- // check that the store does exist
- Store store = nodeDaoService.getStore(protocol, identifier);
- if (store == null)
- {
- throw new InvalidStoreRefException("Unable to delete a store that does not exist: " + storeRef, storeRef);
- }
-
- // TODO invoke policies - e.g. tell indexer to delete index
- // invokeBeforeDeleteStore(ContentModel.TYPE_STOREROOT, storeRef);
-
- // (hard) delete store
- nodeDaoService.deleteStore(protocol, identifier);
-
- // done
- return;
+ throw new UnsupportedOperationException();
}
public NodeRef getRootNode(StoreRef storeRef) throws InvalidStoreRefException
{
- Store store = nodeDaoService.getStore(storeRef.getProtocol(), storeRef.getIdentifier());
- if (store == null)
+ Pair rootNodePair = nodeDaoService.getRootNode(storeRef);
+ if (rootNodePair == 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, storeRef);
- }
// done
- return tenantService.getBaseName(node.getNodeRef());
+ return rootNodePair.getSecond();
}
/**
* @see #createNode(NodeRef, QName, QName, QName, Map)
*/
- public ChildAssociationRef createNode(NodeRef parentRef, QName assocTypeQName, QName assocQName, QName nodeTypeQName)
+ 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)
+ * @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)
+ public ChildAssociationRef createNode(
+ NodeRef parentRef,
+ QName assocTypeQName,
+ QName assocQName,
+ QName nodeTypeQName,
+ Map properties)
{
Assert.notNull(parentRef);
Assert.notNull(assocTypeQName);
Assert.notNull(assocQName);
-
+
// Get the parent node
- Node parentNode = getNodeNotNull(parentRef);
+ Pair parentNodePair = getNodePairNotNull(parentRef);
+ StoreRef parentStoreRef = parentRef.getStoreRef();
// 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);
+ {
+ properties = Collections.emptyMap();
}
+ // get/generate an ID for the node
+ String newUuid = generateGuid(properties);
+
+ // Remove any system properties
+ extractIntrinsicProperties(properties);
+
// Invoke policy behaviour
invokeBeforeCreateNode(parentRef, assocTypeQName, assocQName, nodeTypeQName);
-
- // get the store that the parent belongs to
- Store store = nodeDaoService.getStore(parentRef.getStoreRef().getProtocol(), parentRef.getStoreRef().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);
- NodeRef childNodeRef = tenantService.getBaseName(childNode.getNodeRef());
+ Pair childNodePair = nodeDaoService.newNode(parentStoreRef, newUuid, nodeTypeQName);
// We now have enough to declare the child association creation
- invokeBeforeCreateChildAssociation(parentRef, childNodeRef, assocTypeQName, assocQName, true);
-
+ invokeBeforeCreateChildAssociation(parentRef, childNodePair.getSecond(), assocTypeQName, assocQName, true);
+
// Create the association
- ChildAssoc childAssoc = nodeDaoService.newChildAssoc(parentNode, childNode, true, assocTypeQName, assocQName);
+ Pair childAssocPair = nodeDaoService.newChildAssoc(
+ parentNodePair.getFirst(),
+ childNodePair.getFirst(),
+ true,
+ assocTypeQName,
+ assocQName);
+ ChildAssociationRef childAssocRef = childAssocPair.getSecond();
- // 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;
+ // Add defaults
+ addDefaults(childNodePair, nodeTypeQName);
+
+ // set the properties passed in
if (properties.size() > 0)
{
- propertiesAfter = setPropertiesImpl(childNode, properties);
+ Map propertiesConverted = convertProperties(properties);
+ nodeDaoService.addNodeProperties(childNodePair.getFirst(), propertiesConverted);
}
+
+ Map propertiesAfterValues = nodeDaoService.getNodeProperties(childNodePair.getFirst());
// Ensure child uniqueness
- setChildUniqueName(childNode); // ensure uniqueness
- ChildAssociationRef childAssocRef = tenantService.getBaseName(childAssoc.getChildAssocRef());
-
- // permissions behaviour
- if (aclDaoComponent != null)
- {
- DbAccessControlList inherited = parentNode.getAccessControlList();
- if (inherited == null)
- {
- // not fixde up yet or unset
- }
- else
- {
- childNode.setAccessControlList(aclDaoComponent.getDbAccessControlList(aclDaoComponent.getInheritedAccessControlList(inherited.getId())));
- }
- }
-
+ String newName = extractNameProperty(propertiesAfterValues);
+ // Ensure uniqueness. Note that the cm:name may be null, in which case the uniqueness is still
+ setChildNameUnique(childAssocPair, newName, null); // ensure uniqueness
+
// Invoke policy behaviour
invokeOnCreateNode(childAssocRef);
invokeOnCreateChildAssociation(childAssocRef, true);
- if (propertiesAfter != null)
- {
- invokeOnUpdateProperties(childAssocRef.getChildRef(), propertiesBefore, propertiesAfter);
- }
-
+ Map propertiesAfter = convertPropertyValues(propertiesAfterValues);
+ addIntrinsicProperties(childNodePair, propertiesAfter);
+ invokeOnUpdateProperties(
+ childAssocRef.getChildRef(),
+ Collections.emptyMap(),
+ propertiesAfter);
+
+ // Index
+ nodeIndexer.indexCreateNode(childAssocRef);
+
// done
return childAssocRef;
}
/**
- * Add the default aspects to a given node
- *
- * @param nodeTypeDef
+ * Adds all the default aspects and properties required for the given type.
+ * Existing values will not be overridden.
*/
- private void addDefaultAspects(ClassDefinition classDefinition, Node node, Map properties)
+ private void addDefaults(Pair nodePair, QName typeQName)
{
- NodeRef nodeRef = node.getNodeRef();
-
+ addDefaultProperties(nodePair, typeQName);
+ addDefaultAspects(nodePair, typeQName);
+ }
+
+ /**
+ * Add the default aspects to a given node
+ * @return Returns true if any aspects were added
+ */
+ private boolean addDefaultAspects(Pair nodePair, QName typeQName)
+ {
+ ClassDefinition classDefinition = dictionaryService.getClass(typeQName);
+ if (classDefinition == null)
+ {
+ return false;
+ }
+ // Get the existing values
+ Long nodeId = nodePair.getFirst();
+ Map existingPropertyValues = nodeDaoService.getNodeProperties(nodeId);
+ Set existingAspects = nodeDaoService.getNodeAspects(nodeId);
+ return addDefaultAspects(nodePair, existingAspects, existingPropertyValues, typeQName);
+ }
+
+ /**
+ * Add the default aspects to a given node
+ * @return Returns true if any aspects were added
+ */
+ private boolean addDefaultAspects(Pair nodePair, Set existingAspects, Map existingPropertyValues, QName typeQName)
+ {
+ ClassDefinition classDefinition = dictionaryService.getClass(typeQName);
+ if (classDefinition == null)
+ {
+ return false;
+ }
+
+ Long nodeId = nodePair.getFirst();
+ NodeRef nodeRef = nodePair.getSecond();
+
// 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)
+ boolean added = false;
+ for (AspectDefinition typeDefinition : defaultAspectDefs)
{
- QName defaultAspectQName = defaultAspectDef.getName();
- QNameEntity defaultAspectQNameEntity = qnameDAO.getOrCreateQNameEntity(defaultAspectDef.getName());
- invokeBeforeAddAspect(nodeRef, defaultAspectQName);
- nodeAspects.add(defaultAspectQNameEntity.getId());
- addDefaultPropertyValues(defaultAspectDef, properties);
- invokeOnAddAspect(nodeRef, defaultAspectQName);
-
+ QName aspectQName = typeDefinition.getName();
+ boolean existingAspect = existingAspects.contains(aspectQName);
+ // Only add the aspect if it isn't there
+ if (!existingAspect)
+ {
+ invokeBeforeAddAspect(nodeRef, aspectQName);
+ nodeDaoService.addNodeAspects(nodeId, Collections.singleton(aspectQName));
+ added = true;
+ }
+ // Set default properties for the aspect
+ addDefaultProperties(nodePair, aspectQName);
+ if (!existingAspect)
+ {
+ // Fire policy
+ invokeOnAddAspect(nodeRef, aspectQName);
+ }
+
// Now add any default aspects for this aspect
- addDefaultAspects(defaultAspectDef, node, properties);
+ boolean moreAdded = addDefaultAspects(nodePair, aspectQName);
+ added = (added || moreAdded);
}
+ // Done
+ return added;
}
-
+
/**
- * Drops the old primary association and creates a new one
+ * @return Returns true if any properties were added
*/
- public ChildAssociationRef moveNode(NodeRef nodeToMoveRef, NodeRef newParentRef, QName assocTypeQName, QName assocQName) throws InvalidNodeRefException
+ private boolean addDefaultProperties(Pair nodePair, QName typeQName)
{
- 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 = tenantService.getBaseName(oldAssoc.getChildAssocRef());
-
- boolean movingStore = !nodeToMoveRef.getStoreRef().equals(newParentRef.getStoreRef());
-
- // data needed for policy invocation
- QName nodeToMoveTypeQName = nodeToMove.getTypeQName().getQName();
- Set nodeToMoveAspects = nodeToMove.getAspects();
-
- // Invoke policy behaviour
- if (movingStore)
+ ClassDefinition classDefinition = dictionaryService.getClass(typeQName);
+ if (classDefinition == null)
{
- invokeBeforeDeleteNode(nodeToMoveRef);
- invokeBeforeCreateNode(newParentRef, assocTypeQName, assocQName, nodeToMoveTypeQName);
+ return false;
}
- else
- {
- invokeBeforeDeleteChildAssociation(oldAssocRef);
- invokeBeforeCreateChildAssociation(newParentRef, nodeToMoveRef, assocTypeQName, assocQName, false);
- }
-
- // 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 = tenantService.getBaseName(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);
-
- // Fix inherited permissions
-
- if (aclDaoComponent != null)
- {
- if (newAssoc.getChild().getAccessControlList() != null)
- {
- Long targetAcl = newAssoc.getChild().getAccessControlList().getId();
- AccessControlListProperties aclProperties = aclDaoComponent.getAccessControlListProperties(targetAcl);
- Boolean inherits = aclProperties.getInherits();
- if ((inherits != null) && (inherits.booleanValue()))
- {
- if (newAssoc.getParent().getAccessControlList() != null)
- {
- Long parentAcl = newAssoc.getParent().getAccessControlList().getId();
- Long inheritedAcl = aclDaoComponent.getInheritedAccessControlList(parentAcl);
- if (aclProperties.getAclType() == ACLType.DEFINING)
- {
- aclDaoComponent.enableInheritance(targetAcl, parentAcl);
- }
- else if (aclProperties.getAclType() == ACLType.SHARED)
- {
- DMAccessControlListDAO.setFixedAcls(newAssoc.getChildAssocRef().getChildRef(), inheritedAcl, true, this, aclDaoComponent, nodeDaoService);
- }
- }
- else
- {
- if (aclProperties.getAclType() == ACLType.DEFINING)
- {
-
- // there is nothing to inherit from so clear out any inherited aces
- aclDaoComponent.deleteInheritedAccessControlEntries(targetAcl);
- }
- else if (aclProperties.getAclType() == ACLType.SHARED)
- {
- // there is nothing to inherit
- nodeToMove.setAccessControlList(null);
- }
-
- // throw new IllegalStateException("Share bug");
- }
- }
- }
- else
- {
- if (newAssoc.getParent().getAccessControlList() != null)
- {
- Long parentAcl = newAssoc.getParent().getAccessControlList().getId();
- Long inheritedAcl = aclDaoComponent.getInheritedAccessControlList(parentAcl);
- DMAccessControlListDAO.setFixedAcls(newAssoc.getChildAssocRef().getChildRef(), inheritedAcl, true, this, aclDaoComponent, nodeDaoService);
- }
- }
-
- }
-
- // invoke policy behaviour
- if (movingStore)
- {
- Set nodeToMoveAspectQNames = new HashSet(17);
- for (Long qnameEntityId : nodeToMoveAspects)
- {
- QName nodeToMoveAspectQName = qnameDAO.getQNameEntity(qnameEntityId).getQName();
- nodeToMoveAspectQNames.add(nodeToMoveAspectQName);
- }
- // 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, nodeToMoveAspectQNames, true);
- invokeOnCreateNode(newAssoc.getChildAssocRef());
- }
- else
- {
- invokeOnCreateChildAssociation(newAssoc.getChildAssocRef(), false);
- invokeOnDeleteChildAssociation(oldAssoc.getChildAssocRef());
- }
- invokeOnMoveNode(oldAssocRef, newAssocRef);
-
- // update the node status
- nodeDaoService.recordChangeId(nodeToMoveRef);
-
- // done
- return newAssoc.getChildAssocRef();
+ // Get the existing values
+ Long nodeId = nodePair.getFirst();
+ Map existingPropertyValues = nodeDaoService.getNodeProperties(nodeId);
+ return addDefaultProperties(nodePair, existingPropertyValues, typeQName);
}
-
+
+ /**
+ * Adds default properties for the given type to the node. Default values will not be set if there are existing values.
+ */
+ private boolean addDefaultProperties(Pair nodePair, Map existingPropertyValues, QName typeQName)
+ {
+ Long nodeId = nodePair.getFirst();
+ // Get the default properties for this aspect
+ Map defaultProperties = getDefaultProperties(typeQName);
+ Map defaultPropertyValues = this.convertProperties(defaultProperties);
+ // Remove all default values where a value already exists
+ for (Map.Entry entry : existingPropertyValues.entrySet())
+ {
+ QName existingPropertyQName = entry.getKey();
+ PropertyValue existingPropertyValue = entry.getValue();
+ if (existingPropertyValue != null)
+ {
+ defaultPropertyValues.remove(existingPropertyQName);
+ }
+ }
+ // Add the properties to the node - but only if there is anything to set
+ if (defaultPropertyValues.size() > 0)
+ {
+ nodeDaoService.addNodeProperties(nodeId, defaultPropertyValues);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
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)
+ Pair parentNodePair = getNodePairNotNull(childAssocRef.getParentRef());
+ Pair childNodePair = getNodePairNotNull(childAssocRef.getChildRef());
+
+ Long parentNodeId = parentNodePair.getFirst();
+ Long childNodeId = childNodePair.getFirst();
+ QName assocTypeQName = childAssocRef.getTypeQName();
+ QName assocQName = childAssocRef.getQName();
+
+ Pair assocPair = nodeDaoService.getChildAssoc(
+ parentNodeId,
+ childNodeId,
+ assocTypeQName,
+ assocQName);
+ if (assocPair == null)
{
- throw new InvalidChildAssociationRefException("Unable to set child association index: \n" + " assoc: " + childAssocRef + "\n" + " index: " + index, childAssocRef);
+ throw new InvalidChildAssociationRefException("Unable to set child association index: \n" +
+ " assoc: " + childAssocRef + "\n" +
+ " index: " + index,
+ childAssocRef);
}
// set the index
- assoc.setIndex(index);
- // flush
- nodeDaoService.flush();
+ nodeDaoService.updateChildAssoc(assocPair.getFirst(), parentNodeId, childNodeId, assocTypeQName, assocQName, index);
}
public QName getType(NodeRef nodeRef) throws InvalidNodeRefException
{
- Node node = getNodeNotNull(nodeRef);
- return node.getTypeQName().getQName();
+ Pair nodePair = getNodePairNotNull(nodeRef);
+ return nodeDaoService.getNodeType(nodePair.getFirst());
}
-
+
/**
- * @see org.alfresco.service.cmr.repository.NodeService#setType(org.alfresco.service.cmr.repository.NodeRef,
- * org.alfresco.service.namespace.QName)
+ * @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
{
@@ -668,29 +507,32 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
{
throw new InvalidTypeException(typeQName);
}
- Node node = getNodeNotNull(nodeRef);
-
+ Pair nodePair = getNodePairNotNull(nodeRef);
+
// Invoke policies
invokeBeforeUpdateNode(nodeRef);
-
- // Ensure that we have a QName entity to represent the type
- QNameEntity typeQNameEntity = qnameDAO.getOrCreateQNameEntity(typeQName);
- // Get the node and set the new type
- node.setTypeQName(typeQNameEntity);
-
+
+ // Set the type
+ nodeDaoService.updateNode(nodePair.getFirst(), null, null, 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);
-
+ addDefaultAspects(nodePair, typeQName);
+
+ // Index
+ nodeIndexer.indexUpdateNode(nodeRef);
+
// Invoke policies
invokeOnUpdateNode(nodeRef);
}
-
+
/**
* @see Node#getAspects()
*/
- public void addAspect(NodeRef nodeRef, QName aspectTypeQName, Map aspectProperties) throws InvalidNodeRefException, InvalidAspectException
+ public void addAspect(
+ NodeRef nodeRef,
+ QName aspectTypeQName,
+ Map aspectProperties)
+ throws InvalidNodeRefException, InvalidAspectException
{
// check that the aspect is legal
AspectDefinition aspectDef = dictionaryService.getAspect(aspectTypeQName);
@@ -698,379 +540,433 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
{
throw new InvalidAspectException("The aspect is invalid: " + aspectTypeQName, aspectTypeQName);
}
-
- Node node = getNodeNotNull(nodeRef);
-
+
+ // Check the properties
+ if (aspectProperties != null)
+ {
+ // Remove any system properties
+ extractIntrinsicProperties(aspectProperties);
+ }
+ else
+ {
+ // Make a map
+ aspectProperties = Collections.emptyMap();
+ }
+ // Make the properties immutable to be sure that they are not used incorrectly
+ aspectProperties = Collections.unmodifiableMap(aspectProperties);
+
+ Pair nodePair = getNodePairNotNull(nodeRef);
+ Long nodeId = nodePair.getFirst();
+
// Invoke policy behaviours
invokeBeforeUpdateNode(nodeRef);
invokeBeforeAddAspect(nodeRef, aspectTypeQName);
- // attach the properties to the current node properties
- Map nodeProperties = getPropertiesImpl(node);
-
- if (aspectProperties != null)
+ // Add defaults
+ addDefaults(nodePair, aspectTypeQName);
+
+ if (aspectProperties.size() > 0)
{
- nodeProperties.putAll(aspectProperties);
+ Map aspectPropertyValues = convertProperties(aspectProperties);
+ nodeDaoService.addNodeProperties(nodeId, aspectPropertyValues);
}
-
- // Set any default property values that appear on the aspect
- addDefaultPropertyValues(aspectDef, nodeProperties);
-
- // Add any dependent aspect
- addDefaultAspects(aspectDef, node, nodeProperties);
-
- // Set the property values back on the node
- setProperties(nodeRef, nodeProperties);
-
- // Get the persistale QNameEntity for the aspect
- QNameEntity aspectTypeQNameEntity = qnameDAO.getOrCreateQNameEntity(aspectTypeQName);
- // physically attach the aspect to the node
- if (node.getAspects().add(aspectTypeQNameEntity.getId()) == true)
- {
+
+ if (!nodeDaoService.hasNodeAspect(nodeId, aspectTypeQName))
+ {
// Invoke policy behaviours
invokeOnUpdateNode(nodeRef);
invokeOnAddAspect(nodeRef, aspectTypeQName);
-
- // update the node status
- nodeDaoService.recordChangeId(nodeRef);
+ nodeDaoService.addNodeAspects(nodeId, Collections.singleton(aspectTypeQName));
}
+
+ // Index
+ nodeIndexer.indexUpdateNode(nodeRef);
}
- /**
- * @see Node#getAspects()
- */
- public void removeAspect(NodeRef nodeRef, QName aspectTypeQName) throws InvalidNodeRefException, InvalidAspectException
+ public void removeAspect(NodeRef nodeRef, QName aspectTypeQName)
+ throws InvalidNodeRefException, InvalidAspectException
{
- // get the aspect
- AspectDefinition aspectDef = dictionaryService.getAspect(aspectTypeQName);
- if (aspectDef == null)
- {
- throw new InvalidAspectException(aspectTypeQName);
- }
+ /**
+ * Note: Aspect and property removal is resilient to missing dictionary definitions
+ */
// get the node
- Node node = getNodeNotNull(nodeRef);
- Set nodeAspects = node.getAspects();
-
- // Get the persistale QNameEntity for the aspect
- QNameEntity aspectTypeQNameEntity = qnameDAO.getOrCreateQNameEntity(aspectTypeQName);
- if (!nodeAspects.contains(aspectTypeQNameEntity.getId()))
- {
- // The aspect isn't present so just leave it
- return;
- }
-
+ final Pair nodePair = getNodePairNotNull(nodeRef);
+ final Long nodeId = nodePair.getFirst();
+
+ boolean hadAspect = nodeDaoService.hasNodeAspect(nodeId, aspectTypeQName);
+
// Invoke policy behaviours
invokeBeforeUpdateNode(nodeRef);
- invokeBeforeRemoveAspect(nodeRef, aspectTypeQName);
-
- // remove the aspect, if present
- nodeAspects.remove(aspectTypeQNameEntity.getId());
-
- Map nodeProperties = node.getProperties();
- Map propertyDefs = aspectDef.getProperties();
- for (QName propertyQName : propertyDefs.keySet())
+ if (hadAspect)
{
- QNameEntity propertyQNameEntity = qnameDAO.getOrCreateQNameEntity(propertyQName);
- nodeProperties.remove(propertyQNameEntity.getId());
+ invokeBeforeRemoveAspect(nodeRef, aspectTypeQName);
+ nodeDaoService.removeNodeAspects(nodeId, Collections.singleton(aspectTypeQName));
}
-
- // Remove child associations
- Map childAssocDefs = aspectDef.getChildAssociations();
- Collection childAssocs = nodeDaoService.getChildAssocs(node);
- for (ChildAssoc childAssoc : childAssocs)
+
+ AspectDefinition aspectDef = dictionaryService.getAspect(aspectTypeQName);
+ boolean updated = false;
+ if (aspectDef != null)
{
- // Ignore if the association type is not defined by the aspect
- QName childAssocQName = childAssoc.getTypeQName().getQName();
- if (!childAssocDefs.containsKey(childAssocQName))
+ // Remove default properties
+ Map propertyDefs = aspectDef.getProperties();
+ Set propertyToRemoveQNames = propertyDefs.keySet();
+ nodeDaoService.removeNodeProperties(nodeId, propertyToRemoveQNames);
+
+ // Remove child associations
+ // We have to iterate over the associations and remove all those between the parent and child
+ final List> assocsToDelete = new ArrayList>(5);
+ NodeDaoService.ChildAssocRefQueryCallback callback = new NodeDaoService.ChildAssocRefQueryCallback()
{
- continue;
- }
- // The association is of a type that should be removed
- nodeDaoService.deleteChildAssoc(childAssoc, true);
- }
-
- // Remove regular associations
- Map assocDefs = aspectDef.getAssociations();
- List nodeAssocs = nodeDaoService.getTargetNodeAssocs(node);
- for (NodeAssoc nodeAssoc : nodeAssocs)
- {
- // Ignore if the association type is not defined by the aspect
- QName nodeAssocQName = nodeAssoc.getTypeQName().getQName();
- if (!assocDefs.containsKey(nodeAssocQName))
+ public boolean handle(
+ Pair childAssocPair,
+ Pair parentNodePair,
+ Pair childNodePair
+ )
+ {
+ // Add it
+ assocsToDelete.add(childAssocPair);
+ // No recurse
+ return false;
+ }
+ };
+ // Get all the QNames to remove
+ List assocTypeQNamesToRemove = new ArrayList(aspectDef.getChildAssociations().keySet());
+ nodeDaoService.getChildAssocsByTypeQNames(nodeId, assocTypeQNamesToRemove, callback);
+ // Delete all the collected associations
+ for (Pair assocPair : assocsToDelete)
{
- continue;
+ updated = true;
+ Long assocId = assocPair.getFirst();
+ ChildAssociationRef assocRef = assocPair.getSecond();
+ // delete the association instance - it is not primary
+ invokeBeforeDeleteChildAssociation(assocRef);
+ nodeDaoService.deleteChildAssoc(assocId);
+ invokeOnDeleteChildAssociation(assocRef);
+ }
+
+ // Remove regular associations
+ Map nodeAssocDefs = aspectDef.getAssociations();
+ Collection> nodeAssocPairs = nodeDaoService.getNodeAssocsToAndFrom(nodeId);
+ for (Pair nodeAssocPair : nodeAssocPairs)
+ {
+ updated = true;
+ QName nodeAssocTypeQName = nodeAssocPair.getSecond().getTypeQName();
+ // Ignore if the association type is not defined by the aspect
+ if (!nodeAssocDefs.containsKey(nodeAssocTypeQName))
+ {
+ continue;
+ }
+ updated = true;
+ // It has to be removed
+ nodeDaoService.deleteNodeAssoc(nodeAssocPair.getFirst());
}
- // Delete the association
- nodeDaoService.deleteNodeAssoc(nodeAssoc);
}
-
+
// Invoke policy behaviours
- invokeOnUpdateNode(nodeRef);
- invokeOnRemoveAspect(nodeRef, aspectTypeQName);
+ if (updated)
+ {
+ invokeOnUpdateNode(nodeRef);
+ }
+ if (hadAspect)
+ {
+ invokeOnRemoveAspect(nodeRef, aspectTypeQName);
+ }
- // update the node status
- nodeDaoService.recordChangeId(nodeRef);
+ // Index
+ nodeIndexer.indexUpdateNode(nodeRef);
}
/**
* Performs a check on the set of node aspects
- *
- * @see Node#getAspects()
*/
public boolean hasAspect(NodeRef nodeRef, QName aspectQName) throws InvalidNodeRefException, InvalidAspectException
{
- QNameEntity aspectQNameEntity = qnameDAO.getQNameEntity(aspectQName);
- if (aspectQNameEntity == null)
- {
- // There is no persisted, fixed QName like this
- return false;
- }
- Node node = getNodeNotNull(nodeRef);
- Set aspectQNames = node.getAspects();
- boolean hasAspect = aspectQNames.contains(aspectQNameEntity.getId());
- // done
- return hasAspect;
+ Pair nodePair = getNodePairNotNull(nodeRef);
+ return nodeDaoService.hasNodeAspect(nodePair.getFirst(), aspectQName);
}
public Set getAspects(NodeRef nodeRef) throws InvalidNodeRefException
{
- Node node = getNodeNotNull(nodeRef);
- Set aspectQNameEntities = node.getAspects();
- // copy the set to ensure initialization
- Set ret = new HashSet(aspectQNameEntities.size());
- for (Long aspectQNameEntityId : aspectQNameEntities)
- {
- QNameEntity aspectQNameEntity = qnameDAO.getQNameEntity(aspectQNameEntityId);
- ret.add(aspectQNameEntity.getQName());
- }
- // done
- return ret;
+ Pair nodePair = getNodePairNotNull(nodeRef);
+ return nodeDaoService.getNodeAspects(nodePair.getFirst());
}
public void deleteNode(NodeRef nodeRef)
{
- // First get the node to ensure that it exists
- Node node = getNodeNotNull(nodeRef);
+ Pair nodePair = getNodePairNotNull(nodeRef);
+ Long nodeId = nodePair.getFirst();
boolean requiresDelete = false;
-
+
// Invoke policy behaviours
invokeBeforeDeleteNode(nodeRef);
-
+
// get the primary parent-child relationship before it is gone
- ChildAssociationRef childAssocRef = getPrimaryParent(nodeRef);
-
+ Pair childAssocPair = nodeDaoService.getPrimaryParentAssoc(nodeId);
+ ChildAssociationRef childAssocRef = childAssocPair.getSecond();
// get type and aspect QNames as they will be unavailable after the delete
- QName nodeTypeQName = node.getTypeQName().getQName();
- Set nodeAspectQNameEntityIds = node.getAspects();
-
- // Get QNameEntity for subsequent checks
- QNameEntity aspectTempQNameEntity = qnameDAO.getOrCreateQNameEntity(ContentModel.ASPECT_TEMPORARY);
- QNameEntity aspectWorkingCopyQNameEntity = qnameDAO.getOrCreateQNameEntity(ContentModel.ASPECT_WORKING_COPY);
+ QName nodeTypeQName = nodeDaoService.getNodeType(nodeId);
+ Set nodeAspectQNames = nodeDaoService.getNodeAspects(nodeId);
// check if we need to archive the node
StoreRef archiveStoreRef = null;
- if (nodeAspectQNameEntityIds.contains(aspectTempQNameEntity.getId()) || nodeAspectQNameEntityIds.contains(aspectWorkingCopyQNameEntity.getId()))
+ if (nodeAspectQNames.contains(ContentModel.ASPECT_TEMPORARY) ||
+ nodeAspectQNames.contains(ContentModel.ASPECT_WORKING_COPY))
{
- // The node is either temporary or a working copy.
- // It can not be archived.
- requiresDelete = true;
+ // The node is either temporary or a working copy.
+ // It can not be archived.
+ requiresDelete = true;
}
else
{
- StoreRef storeRef = nodeRef.getStoreRef();
-
- // remove tenant domain - to retrieve archive store from map
- archiveStoreRef = storeArchiveMap.getArchiveMap().get(storeRef);
- // get the type and check if we need archiving
- TypeDefinition typeDef = dictionaryService.getType(nodeTypeQName);
- if (typeDef == null || !typeDef.isArchive() || archiveStoreRef == null)
- {
- requiresDelete = true;
- }
+ StoreRef storeRef = nodeRef.getStoreRef();
+ archiveStoreRef = storeArchiveMap.getArchiveMap().get(storeRef);
+ // get the type and check if we need archiving
+ TypeDefinition typeDef = dictionaryService.getType(nodeTypeQName);
+ if (typeDef == null || !typeDef.isArchive() || archiveStoreRef == null)
+ {
+ requiresDelete = true;
+ }
}
-
+
if (requiresDelete)
{
- // perform a normal deletion
- nodeDaoService.deleteNode(node, true);
- // Invoke policy behaviours
- Set nodeToDeleteAspectQNames = new HashSet(17);
- for (Long qnameEntityId : nodeAspectQNameEntityIds)
+ // Cascade as required
+ if (cascadeInTransaction)
{
- QName nodeToDeleteAspectQName = qnameDAO.getQNameEntity(qnameEntityId).getQName();
- nodeToDeleteAspectQNames.add(nodeToDeleteAspectQName);
+ deletePrimaryChildren(nodePair, true);
}
- invokeOnDeleteNode(childAssocRef, nodeTypeQName, nodeToDeleteAspectQNames, false);
+ // perform a normal deletion
+ nodeDaoService.deleteNode(nodeId);
+ // Invoke policy behaviours
+ invokeOnDeleteNode(childAssocRef, nodeTypeQName, nodeAspectQNames, false);
+
+ // Index
+ nodeIndexer.indexDeleteNode(childAssocRef);
}
else
{
- archiveStoreRef = tenantService.getName(archiveStoreRef);
// archive it
archiveNode(nodeRef, archiveStoreRef);
// The archive performs a move, which will fire the appropriate OnDeleteNode
+ invokeOnDeleteNode(childAssocRef, nodeTypeQName, nodeAspectQNames, true);
}
}
-
+
+ private void deletePrimaryChildren(Pair nodePair, boolean cascade)
+ {
+ Long nodeId = nodePair.getFirst();
+ // Get the node's primary children
+ final List> childNodePairs = new ArrayList>(5);
+ NodeDaoService.ChildAssocRefQueryCallback callback = new NodeDaoService.ChildAssocRefQueryCallback()
+ {
+ public boolean handle(
+ Pair childAssocPair,
+ Pair parentNodePair,
+ Pair childNodePair
+ )
+ {
+ // Add it
+ childNodePairs.add(childNodePair);
+ // No recurse
+ return false;
+ }
+ };
+ // Get all the QNames to remove
+ nodeDaoService.getPrimaryChildAssocs(nodeId, callback);
+ // Each child must be deleted
+ for (Pair childNodePair : childNodePairs)
+ {
+ // Cascade, if required
+ if (cascade)
+ {
+ deletePrimaryChildren(childNodePair, true);
+ }
+ // Delete the child
+ nodeDaoService.deleteNode(childNodePair.getFirst());
+// It would appear that policies should be fired here, but they have never been, so
+// in the order to maintain historical consistency we keep it the same.
+// // Fire node policies. This ensures that each node in the hierarchy gets a notification fired.
+// invokeOnDeleteNode(oldParentAssocPair.getSecond(), childNodeTypeQName, childNodeAspectQNames, true);
+// invokeOnCreateNode(newParentAssocPair.getSecond());
+ }
+ }
+
public ChildAssociationRef addChild(NodeRef parentRef, NodeRef childRef, QName assocTypeQName, QName 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);
+ Pair parentNodePair = getNodePairNotNull(parentRef);
+ Long parentNodeId = parentNodePair.getFirst();
+ Pair childNodePair = getNodePairNotNull(childRef);
+ Long childNodeId = childNodePair.getFirst();
// Invoke policy behaviours
invokeBeforeCreateChildAssociation(parentRef, childRef, assocTypeQName, assocQName, false);
-
+
// make the association
- ChildAssoc assoc = nodeDaoService.newChildAssoc(parentNode, childNode, false, assocTypeQName, assocQName);
+ Pair childAssocPair = nodeDaoService.newChildAssoc(parentNodeId, childNodeId, false, assocTypeQName, assocQName);
+ ChildAssociationRef childAssocRef = childAssocPair.getSecond();
// ensure name uniqueness
- setChildUniqueName(childNode);
- ChildAssociationRef assocRef = assoc.getChildAssocRef();
- NodeRef childNodeRef = assocRef.getChildRef();
-
+ setChildNameUnique(childAssocPair, childNodePair);
+ NodeRef childNodeRef = childAssocRef.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, false);
+ invokeOnCreateChildAssociation(childAssocRef, false);
- // update the node status
- nodeDaoService.recordChangeId(childNodeRef);
+ // Index
+ nodeIndexer.indexCreateChildAssociation(childAssocRef);
- return assoc.getChildAssocRef();
+ return childAssocRef;
}
public void removeChild(NodeRef parentRef, NodeRef childRef) throws InvalidNodeRefException
{
- Node parentNode = getNodeNotNull(parentRef);
- Node childNode = getNodeNotNull(childRef);
- Long childNodeId = childNode.getId();
+ final Pair parentNodePair = getNodePairNotNull(parentRef);
+ final Long parentNodeId = parentNodePair.getFirst();
+ final Pair childNodePair = getNodePairNotNull(childRef);
+ final Long childNodeId = childNodePair.getFirst();
+
+ // Get the primary parent association for the child
+ Pair primaryChildAssocPair = nodeDaoService.getPrimaryParentAssoc(childNodeId);
+ // We can shortcut if our parent is also the primary parent
+ if (primaryChildAssocPair != null)
+ {
+ NodeRef primaryParentNodeRef = primaryChildAssocPair.getSecond().getParentRef();
+ if (primaryParentNodeRef.equals(parentRef))
+ {
+ // Shortcut - just delete the child node
+ deleteNode(childRef);
+ return;
+ }
+ }
+
+ // We have to iterate over the associations and remove all those between the parent and child
+ final List> assocsToDelete = new ArrayList>(5);
+ NodeDaoService.ChildAssocRefQueryCallback callback = new NodeDaoService.ChildAssocRefQueryCallback()
+ {
+ public boolean handle(
+ Pair childAssocPair,
+ Pair parentNodePair,
+ Pair childNodePair)
+ {
+ // Ignore if the child is not ours
+ if (!childNodePair.getFirst().equals(childNodeId))
+ {
+ return false;
+ }
+ // Add it
+ assocsToDelete.add(childAssocPair);
+ // No recurse
+ return false;
+ }
+ };
+ nodeDaoService.getChildAssocs(parentNodeId, callback, false);
+
+ // Delete all the collected associations
+ for (Pair assocPair : assocsToDelete)
+ {
+ Long assocId = assocPair.getFirst();
+ ChildAssociationRef assocRef = assocPair.getSecond();
+ // delete the association instance - it is not primary
+ invokeBeforeDeleteChildAssociation(assocRef);
+ nodeDaoService.deleteChildAssoc(assocId);
+ invokeOnDeleteChildAssociation(assocRef);
- // 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());
- }
- else
- {
- // The cascade delete will update the node status, but just a plain assoc deletion will not
- // Update the node status
- nodeDaoService.recordChangeId(childRef);
+ // Index
+ nodeIndexer.indexDeleteChildAssociation(assocRef);
}
- // done
+ // Done
}
-
+
public boolean removeChildAssociation(ChildAssociationRef childAssocRef)
{
- Node parentNode = getNodeNotNull(childAssocRef.getParentRef());
- Node childNode = getNodeNotNull(childAssocRef.getChildRef());
- QName typeQName = childAssocRef.getTypeQName();
- QName qname = childAssocRef.getQName();
+ Long parentNodeId = getNodePairNotNull(childAssocRef.getParentRef()).getFirst();
+ Long childNodeId = getNodePairNotNull(childAssocRef.getChildRef()).getFirst();
+ QName assocTypeQName = childAssocRef.getTypeQName();
+ QName assocQName = childAssocRef.getQName();
// Delete the association
invokeBeforeDeleteChildAssociation(childAssocRef);
- boolean deleted = nodeDaoService.deleteChildAssoc(parentNode, childNode, typeQName, qname);
+ boolean deleted = nodeDaoService.deleteChildAssoc(parentNodeId, childNodeId, assocTypeQName, assocQName);
if (deleted)
{
invokeOnDeleteChildAssociation(childAssocRef);
- // Update the node status
- nodeDaoService.recordChangeId(childNode.getNodeRef());
}
+ // Index
+ nodeIndexer.indexDeleteChildAssociation(childAssocRef);
// Done
return deleted;
}
public boolean removeSeconaryChildAssociation(ChildAssociationRef childAssocRef)
{
- Node parentNode = getNodeNotNull(childAssocRef.getParentRef());
- Node childNode = getNodeNotNull(childAssocRef.getChildRef());
+ Long parentNodeId = getNodePairNotNull(childAssocRef.getParentRef()).getFirst();
+ Long childNodeId = getNodePairNotNull(childAssocRef.getChildRef()).getFirst();
QName typeQName = childAssocRef.getTypeQName();
QName qname = childAssocRef.getQName();
- ChildAssoc assoc = nodeDaoService.getChildAssoc(parentNode, childNode, typeQName, qname);
- if (assoc == null)
+ Pair assocPair = nodeDaoService.getChildAssoc(parentNodeId, childNodeId, typeQName, qname);
+ if (assocPair == null)
{
// No association exists
return false;
}
- if (assoc.getIsPrimary())
+ Long assocId = assocPair.getFirst();
+ ChildAssociationRef assocRef = assocPair.getSecond();
+ if (assocRef.isPrimary())
{
- throw new IllegalArgumentException("removeSeconaryChildAssociation can not be applied to a primary association: \n" + " Child Assoc: " + assoc);
+ throw new IllegalArgumentException(
+ "removeSeconaryChildAssociation can not be applied to a primary association: \n" +
+ " Child Assoc: " + assocRef);
}
// Delete the secondary association
- nodeDaoService.deleteChildAssoc(assoc, false);
+ nodeDaoService.deleteChildAssoc(assocId);
invokeOnDeleteChildAssociation(childAssocRef);
- // Update the node status
- nodeDaoService.recordChangeId(childNode.getNodeRef());
+ // Index
+ nodeIndexer.indexDeleteChildAssociation(childAssocRef);
// Done
return true;
}
/**
- * Remove properties that should not be persisted as general properties. Where necessary, the properties are set on
- * the node.
+ * Remove properties that should not be persisted as general properties. Where necessary, the
+ * properties are set on the node.
*
- * @param node
- * the node to set properties on
- * @param properties
- * properties to change
+ * @param node the node to set properties on
+ * @param properties properties to change
*/
- private void extractIntrinsicProperties(Node node, Map properties)
+ private void extractIntrinsicProperties(Map properties)
{
properties.remove(ContentModel.PROP_STORE_PROTOCOL);
properties.remove(ContentModel.PROP_STORE_IDENTIFIER);
properties.remove(ContentModel.PROP_NODE_UUID);
properties.remove(ContentModel.PROP_NODE_DBID);
}
-
+
/**
- * Adds all properties used by the {@link ContentModel#ASPECT_REFERENCEABLE referencable aspect}.
+ * Adds all properties used by the
+ * {@link ContentModel#ASPECT_REFERENCEABLE referencable aspect}.
*
- * This method can be used to ensure that the values used by the aspect are present as node properties.
+ * This method can be used to ensure that the values used by the aspect
+ * are present as node properties.
*
- * This method also ensures that the {@link ContentModel#PROP_NAME name property} is always present as a property on
- * a node.
+ * This method also ensures that the {@link ContentModel#PROP_NAME name property}
+ * is always present as a property on a node.
*
- * @param node
- * the node with the values
- * @param nodeRef
- * the node reference containing the values required
- * @param properties
- * the node properties
+ * @param node the node with the values
+ * @param nodeRef the node reference containing the values required
+ * @param properties the node properties
*/
- private void addIntrinsicProperties(Node node, Map properties)
+ private void addIntrinsicProperties(Pair nodePair, Map properties)
{
- NodeRef nodeRef = tenantService.getBaseName(node.getNodeRef());
+ Long nodeId = nodePair.getFirst();
+ NodeRef nodeRef = nodePair.getSecond();
properties.put(ContentModel.PROP_STORE_PROTOCOL, nodeRef.getStoreRef().getProtocol());
properties.put(ContentModel.PROP_STORE_IDENTIFIER, nodeRef.getStoreRef().getIdentifier());
properties.put(ContentModel.PROP_NODE_UUID, nodeRef.getId());
- properties.put(ContentModel.PROP_NODE_DBID, node.getId());
+ properties.put(ContentModel.PROP_NODE_DBID, nodeId);
// add the ID as the name, if required
if (properties.get(ContentModel.PROP_NAME) == null)
{
@@ -1078,43 +974,10 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
}
}
- public Map getProperties(NodeRef nodeRef) throws InvalidNodeRefException
- {
- Node node = getNodeNotNull(nodeRef);
- return getPropertiesImpl(node);
- }
-
- private Map getPropertiesImpl(Node node) throws InvalidNodeRefException
- {
- Map propDefs = dictionaryService.getPropertyDefs(node.getTypeQName().getQName());
- Map nodeProperties = node.getProperties();
- Map ret = new HashMap(nodeProperties.size());
- // copy values
- for (Map.Entry entry : nodeProperties.entrySet())
- {
- Long propertyQNameId = entry.getKey();
- QName propertyQName = qnameDAO.getQNameEntity(propertyQNameId).getQName();
- PropertyValue propertyValue = entry.getValue();
- // get the property definition
- PropertyDefinition propertyDef = propDefs.get(propertyQName);
-
- // convert to the correct type
- Serializable value = makeSerializableValue(propertyDef, propertyValue);
- // copy across
- ret.put(propertyQName, value);
- }
- // spoof referencable properties
- addIntrinsicProperties(node, ret);
- // done
- return ret;
- }
-
public Serializable getProperty(NodeRef nodeRef, QName qname) throws InvalidNodeRefException
{
- // get the property from the node
- Node node = getNodeNotNull(nodeRef);
-
- // spoof referencable properties
+ Long nodeId = getNodePairNotNull(nodeRef).getFirst();
+ // Spoof referencable properties
if (qname.equals(ContentModel.PROP_STORE_PROTOCOL))
{
return nodeRef.getStoreRef().getProtocol();
@@ -1127,214 +990,260 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
{
return nodeRef.getId();
}
-
- if (qname.equals(ContentModel.PROP_NODE_DBID))
+ else if (qname.equals(ContentModel.PROP_NODE_DBID))
{
- return node.getId();
+ return nodeId;
}
-
- // Get the QName entity
- QNameEntity qnameEntity = qnameDAO.getQNameEntity(qname);
- if (qnameEntity == null)
+
+ PropertyValue propertyValue = nodeDaoService.getNodeProperty(nodeId, qname);
+
+ // check if we need to provide a spoofed name
+ if (propertyValue == null && qname.equals(ContentModel.PROP_NAME))
{
- // There is no persisted, fixed QName like this
- return null;
+ return nodeRef.getId();
}
- else
+
+ // get the property definition
+ PropertyDefinition propertyDef = dictionaryService.getProperty(qname);
+ // convert to the correct type
+ Serializable value = makeSerializableValue(propertyDef, propertyValue);
+ // done
+ return value;
+ }
+
+ public Map getProperties(NodeRef nodeRef) throws InvalidNodeRefException
+ {
+ Pair nodePair = getNodePairNotNull(nodeRef);
+ return getPropertiesImpl(nodePair);
+ }
+
+ /**
+ * Gets, converts and adds the intrinsic properties to the current node's properties
+ */
+ private Map getPropertiesImpl(Pair nodePair) throws InvalidNodeRefException
+ {
+ Long nodeId = nodePair.getFirst();
+ Map nodeProperties = nodeDaoService.getNodeProperties(nodeId);
+ Map ret = new HashMap(nodeProperties.size());
+ // copy values
+ for (Map.Entry entry: nodeProperties.entrySet())
{
- Map properties = node.getProperties();
- PropertyValue propertyValue = properties.get(qnameEntity.getId());
-
- // check if we need to provide a spoofed name
- if (propertyValue == null && qname.equals(ContentModel.PROP_NAME))
- {
- return nodeRef.getId();
- }
-
- // Convert any NodeRefs using multi-tenant translation
- PropertyDefinition propertyDef = dictionaryService.getProperty(qname);
-
+ 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);
- // done
- return value;
+ // copy across
+ ret.put(propertyQName, value);
}
+ // spoof referencable properties
+ addIntrinsicProperties(nodePair, ret);
+ // done
+ return ret;
}
-
+
/**
- * 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
- extractIntrinsicProperties(node, 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())
- {
- QNameEntity propertyQNameEntity = qnameDAO.getOrCreateQNameEntity(propertyQName);
- PropertyDefinition propertyDef = dictionaryService.getProperty(propertyQName);
- // Get the value to persist
- Serializable value = properties.get(propertyQName);
- // get a persistable value
- PropertyValue propertyValue = makePropertyValue(propertyDef, value);
- nodeProperties.put(propertyQNameEntity.getId(), 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.
+ * 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);
-
+
// get the node
- Node node = getNodeNotNull(nodeRef);
+ Pair nodePair = getNodePairNotNull(nodeRef);
+ Long nodeId = nodePair.getFirst();
+
+ // Ensure that we are not setting intrinsic properties
+ Map properties = Collections.singletonMap(qname, value);
+ extractIntrinsicProperties(properties);
+
+ // Get the properties from before
+ Map propertiesBefore = getPropertiesImpl(nodePair);
- // Invoke policy behaviours
invokeBeforeUpdateNode(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
+ // Update the properties
+ setPropertyImpl(nodeId, qname, value);
+ // Policy callbacks
+ Map propertiesAfter = getPropertiesImpl(nodePair);
invokeOnUpdateNode(nodeRef);
invokeOnUpdateProperties(nodeRef, propertiesBefore, propertiesAfter);
+
+ // Index
+ nodeIndexer.indexUpdateNode(nodeRef);
}
-
+
/**
- * 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
+ * Sets the property, taking special care to handle intrinsic properties and cm:name properly
*/
- private Map setPropertyImpl(Node node, QName qname, Serializable value) throws InvalidNodeRefException
+ private void setPropertyImpl(Long nodeId, QName qname, Serializable value)
{
- NodeRef nodeRef = node.getNodeRef();
-
- Map properties = node.getProperties();
- PropertyDefinition propertyDef = dictionaryService.getProperty(qname);
- // Get the persistable key
- QNameEntity qnameEntity = qnameDAO.getOrCreateQNameEntity(qname);
- // get a persistable value
- PropertyValue propertyValue = makePropertyValue(propertyDef, value);
- properties.put(qnameEntity.getId(), propertyValue);
-
- // update the node status
- nodeDaoService.recordChangeId(nodeRef);
-
- return getPropertiesImpl(node);
+ if (qname.equals(ContentModel.PROP_NODE_UUID))
+ {
+ throw new IllegalArgumentException("The node UUID cannot be changed.");
+ }
+ else
+ {
+ // cm:name special handling
+ if (qname.equals(ContentModel.PROP_NAME))
+ {
+ Pair primaryParentAssocPair = nodeDaoService.getPrimaryParentAssoc(nodeId);
+ if (primaryParentAssocPair != null)
+ {
+ String oldName = extractNameProperty(nodeDaoService.getNodeProperties(nodeId));
+ String newName = DefaultTypeConverter.INSTANCE.convert(String.class, value);
+ setChildNameUnique(primaryParentAssocPair, newName, oldName);
+ }
+ }
+ // Set the property
+ PropertyDefinition propertyDef = dictionaryService.getProperty(qname);
+ // get a persistable value
+ PropertyValue propertyValue = makePropertyValue(propertyDef, value);
+ nodeDaoService.addNodeProperty(nodeId, qname, propertyValue);
+ }
}
+
+ /**
+ * 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
+ {
+ Pair nodePair = getNodePairNotNull(nodeRef);
+ Long nodeId = nodePair.getFirst();
+
+ extractIntrinsicProperties(properties);
+ // Invoke policy behaviours
+ Map propertiesBefore = getPropertiesImpl(nodePair);
+ invokeBeforeUpdateNode(nodeRef);
+
+ // Do the set properties
+ setPropertiesImpl(nodeId, properties);
+
+ // Invoke policy behaviours
+ Map propertiesAfter = getPropertiesImpl(nodePair);
+ invokeOnUpdateNode(nodeRef);
+ invokeOnUpdateProperties(nodeRef, propertiesBefore, propertiesAfter);
+
+ // Index
+ nodeIndexer.indexUpdateNode(nodeRef);
+ }
+
+ private void setPropertiesImpl(Long nodeId, Map properties)
+ {
+ // Get the cm:name and uuid for special handling
+ if (properties.containsKey(ContentModel.PROP_NAME))
+ {
+ Serializable name = properties.get(ContentModel.PROP_NAME);
+ setPropertyImpl(nodeId, ContentModel.PROP_NAME, name);
+ }
+ if (properties.containsKey(ContentModel.PROP_NODE_UUID))
+ {
+ throw new IllegalArgumentException("The node UUID cannot be set");
+ }
+ // Now remove special properties
+ extractIntrinsicProperties(properties);
+ // convert the map
+ Map propertyValues = convertProperties(properties);
+ // Update the node
+ nodeDaoService.setNodeProperties(nodeId, propertyValues);
+ }
+
public void removeProperty(NodeRef nodeRef, QName qname) throws InvalidNodeRefException
{
- if (qname.equals(ContentModel.PROP_NAME))
- {
- throw new UnsupportedOperationException("The property " + qname + " may not be removed individually");
- }
-
// Get the node
- Node node = getNodeNotNull(nodeRef);
-
+ Pair nodePair = getNodePairNotNull(nodeRef);
+ Long nodeId = nodePair.getFirst();
+
// Invoke policy behaviours
invokeBeforeUpdateNode(nodeRef);
-
- // Get the persistable QNameEntity
- QNameEntity qnameEntity = qnameDAO.getOrCreateQNameEntity(qname);
+
// Get the values before
- Map propertiesBefore = getPropertiesImpl(node);
- // Remove the property
- Map properties = node.getProperties();
- properties.remove(qnameEntity.getId());
- // Get the values afterwards
- Map propertiesAfter = getPropertiesImpl(node);
+ Map propertiesBefore = getPropertiesImpl(nodePair);
+
+ // cm:name special handling
+ if (qname.equals(ContentModel.PROP_NAME))
+ {
+ Pair primaryParentAssocPair = nodeDaoService.getPrimaryParentAssoc(nodeId);
+ String oldName = extractNameProperty(nodeDaoService.getNodeProperties(nodeId));
+ String newName = null;
+ setChildNameUnique(primaryParentAssocPair, newName, oldName);
+ }
+ // Remove
+ nodeDaoService.removeNodeProperties(nodeId, Collections.singleton(qname));
+
// Invoke policy behaviours
+ Map propertiesAfter = getPropertiesImpl(nodePair);
invokeOnUpdateNode(nodeRef);
invokeOnUpdateProperties(nodeRef, propertiesBefore, propertiesAfter);
+
+ // Index
+ nodeIndexer.indexUpdateNode(nodeRef);
}
- /**
- * Transforms {@link Node#getParentAssocs()} to a new collection
- */
+ private Map convertProperties(Map properties) throws InvalidNodeRefException
+ {
+ Map convertedProperties = new HashMap(17);
+
+ // 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);
+ convertedProperties.put(propertyQName, propertyValue);
+ }
+
+ // Return the converted properties
+ return convertedProperties;
+ }
+
+ private Map convertPropertyValues(Map propertyValues) throws InvalidNodeRefException
+ {
+ Map convertedProperties = new HashMap(17);
+
+ // check the property type and copy the values across
+ for (Map.Entry entry : propertyValues.entrySet())
+ {
+ QName propertyQName = entry.getKey();
+ PropertyValue propertyValue = entry.getValue();
+ PropertyDefinition propertyDef = dictionaryService.getProperty(propertyQName);
+ Serializable property = makeSerializableValue(propertyDef, propertyValue);
+ convertedProperties.put(propertyQName, property);
+ }
+
+ // Return the converted properties
+ return convertedProperties;
+ }
+
public Collection getParents(NodeRef nodeRef) throws InvalidNodeRefException
{
- Node node = getNodeNotNull(nodeRef);
- // get the assocs pointing to it
- Collection parentAssocs = nodeDaoService.getParentAssocs(node);
+ // Get the node
+ Pair nodePair = getNodePairNotNull(nodeRef);
+ Long nodeId = nodePair.getFirst();
+
+ // Get the assocs pointing to it
+ Collection> parentAssocPairs = nodeDaoService.getParentAssocs(nodeId);
// list of results
- Collection results = new ArrayList(parentAssocs.size());
- for (ChildAssoc assoc : parentAssocs)
+ Collection results = new ArrayList(parentAssocPairs.size());
+ for (Pair assocPair : parentAssocPairs)
{
- // get the parent
- results.add(tenantService.getBaseName(assoc.getParent().getNodeRef()));
+ NodeRef parentNodeRef = assocPair.getSecond().getParentRef();
+ results.add(parentNodeRef);
}
// done
return results;
@@ -1345,28 +1254,25 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
*/
public List getParentAssocs(NodeRef nodeRef, QNamePattern typeQNamePattern, QNamePattern qnamePattern)
{
- Node node = getNodeNotNull(nodeRef);
- // get the assocs pointing to it
- Collection parentAssocs = nodeDaoService.getParentAssocs(node);
- // shortcut if there are no assocs
- if (parentAssocs.size() == 0)
- {
- return Collections.emptyList();
- }
+ // Get the node
+ Pair nodePair = getNodePairNotNull(nodeRef);
+ Long nodeId = nodePair.getFirst();
+
+ // Get the assocs pointing to it
+ Collection> parentAssocPairs = nodeDaoService.getParentAssocs(nodeId);
// list of results
- List results = new ArrayList(parentAssocs.size());
- for (ChildAssoc assoc : parentAssocs)
+ List results = new ArrayList(parentAssocPairs.size());
+ for (Pair assocPair : parentAssocPairs)
{
- QName assocTypeQName = assoc.getTypeQName().getQName();
- QName assocQName = assoc.getQname();
- // does the qname match the pattern?
+ ChildAssociationRef assocRef = assocPair.getSecond();
+ QName assocTypeQName = assocRef.getTypeQName();
+ QName assocQName = assocRef.getQName();
if (!qnamePattern.isMatch(assocQName) || !typeQNamePattern.isMatch(assocTypeQName))
{
- // no match - ignore
+ // No match
continue;
}
- ChildAssociationRef childAssocRef = tenantService.getBaseName(assoc.getChildAssocRef());
- results.add(childAssocRef);
+ results.add(assocRef);
}
// done
return results;
@@ -1375,40 +1281,78 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
/**
* Filters out any associations if their qname is not a match to the given pattern.
*/
- public List getChildAssocs(NodeRef nodeRef, QNamePattern typeQNamePattern, QNamePattern qnamePattern)
+ public List getChildAssocs(NodeRef nodeRef, final QNamePattern typeQNamePattern, final QNamePattern qnamePattern)
{
- Node node = getNodeNotNull(nodeRef);
+ // Get the node
+ Pair nodePair = getNodePairNotNull(nodeRef);
+ Long nodeId = nodePair.getFirst();
- Collection childAssocRefs = null;
+ final List results = new ArrayList(100);
+
// if the type is the wildcard type, and the qname is not a search, then use a shortcut query
if (typeQNamePattern.equals(RegexQNamePattern.MATCH_ALL) && qnamePattern instanceof QName)
{
- // get all child associations with the specific qualified name
- childAssocRefs = nodeDaoService.getChildAssocRefs(node, (QName) qnamePattern);
+ NodeDaoService.ChildAssocRefQueryCallback callback = new NodeDaoService.ChildAssocRefQueryCallback()
+ {
+ public boolean handle(
+ Pair childAssocPair,
+ Pair parentNodePair,
+ Pair