mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
Merged DEV/CHECK_EXISTS to HEAD
svn merge svn://www.alfresco.org:3691/alfresco/BRANCHES/DEV/CHECK_EXISTS@3442 svn://www.alfresco.org:3691/alfresco/BRANCHES/DEV/CHECK_EXISTS@3590 . TODO: Fix bug raising incorrect exception during UI paste of same-named file Note: - Added a new method to NodeService to get child by name - Added a new method to FileFolderService to perform fast, direct lookups based on name git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@3591 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -67,15 +67,6 @@
|
|||||||
timeToLiveSeconds="0"
|
timeToLiveSeconds="0"
|
||||||
overflowToDisk="false"
|
overflowToDisk="false"
|
||||||
/>
|
/>
|
||||||
<!-- approx 20MB memory required -->
|
|
||||||
<cache
|
|
||||||
name="org.alfresco.repo.domain.hibernate.NodeImpl.childAssocs"
|
|
||||||
maxElementsInMemory="10000"
|
|
||||||
eternal="true"
|
|
||||||
timeToIdleSeconds="0"
|
|
||||||
timeToLiveSeconds="0"
|
|
||||||
overflowToDisk="false"
|
|
||||||
/>
|
|
||||||
<!-- approx 10MB memory required -->
|
<!-- approx 10MB memory required -->
|
||||||
<cache
|
<cache
|
||||||
name="org.alfresco.repo.domain.hibernate.NodeImpl.parentAssocs"
|
name="org.alfresco.repo.domain.hibernate.NodeImpl.parentAssocs"
|
||||||
@@ -94,24 +85,6 @@
|
|||||||
timeToLiveSeconds="0"
|
timeToLiveSeconds="0"
|
||||||
overflowToDisk="false"
|
overflowToDisk="false"
|
||||||
/>
|
/>
|
||||||
<!-- approx 10MB memory required -->
|
|
||||||
<cache
|
|
||||||
name="org.alfresco.repo.domain.hibernate.NodeImpl.sourceNodeAssocs"
|
|
||||||
maxElementsInMemory="10000"
|
|
||||||
eternal="true"
|
|
||||||
timeToIdleSeconds="0"
|
|
||||||
timeToLiveSeconds="0"
|
|
||||||
overflowToDisk="false"
|
|
||||||
/>
|
|
||||||
<!-- approx 10MB memory required -->
|
|
||||||
<cache
|
|
||||||
name="org.alfresco.repo.domain.hibernate.NodeImpl.targetNodeAssocs"
|
|
||||||
maxElementsInMemory="10000"
|
|
||||||
eternal="true"
|
|
||||||
timeToIdleSeconds="0"
|
|
||||||
timeToLiveSeconds="0"
|
|
||||||
overflowToDisk="false"
|
|
||||||
/>
|
|
||||||
<!-- general use node associations are not common -->
|
<!-- general use node associations are not common -->
|
||||||
<cache
|
<cache
|
||||||
name="org.alfresco.repo.domain.hibernate.NodeAssocImpl"
|
name="org.alfresco.repo.domain.hibernate.NodeAssocImpl"
|
||||||
|
@@ -160,9 +160,6 @@
|
|||||||
<prop key="org.alfresco.repo.domain.hibernate.NodeImpl.properties">${cache.strategy}</prop>
|
<prop key="org.alfresco.repo.domain.hibernate.NodeImpl.properties">${cache.strategy}</prop>
|
||||||
<prop key="org.alfresco.repo.domain.hibernate.NodeImpl.aspects">${cache.strategy}</prop>
|
<prop key="org.alfresco.repo.domain.hibernate.NodeImpl.aspects">${cache.strategy}</prop>
|
||||||
<prop key="org.alfresco.repo.domain.hibernate.NodeImpl.parentAssocs">${cache.strategy}</prop>
|
<prop key="org.alfresco.repo.domain.hibernate.NodeImpl.parentAssocs">${cache.strategy}</prop>
|
||||||
<prop key="org.alfresco.repo.domain.hibernate.NodeImpl.childAssocs">${cache.strategy}</prop>
|
|
||||||
<prop key="org.alfresco.repo.domain.hibernate.NodeImpl.sourceNodeAssocs">${cache.strategy}</prop>
|
|
||||||
<prop key="org.alfresco.repo.domain.hibernate.NodeImpl.targetNodeAssocs">${cache.strategy}</prop>
|
|
||||||
|
|
||||||
<prop key="org.alfresco.repo.domain.hibernate.DbAccessControlListImpl.entries">${cache.strategy}</prop>
|
<prop key="org.alfresco.repo.domain.hibernate.DbAccessControlListImpl.entries">${cache.strategy}</prop>
|
||||||
<prop key="org.alfresco.repo.domain.hibernate.DbAuthorityImpl.externalKeys">${cache.strategy}</prop>
|
<prop key="org.alfresco.repo.domain.hibernate.DbAuthorityImpl.externalKeys">${cache.strategy}</prop>
|
||||||
@@ -209,7 +206,7 @@
|
|||||||
<ref bean="nodeDaoServiceImpl" />
|
<ref bean="nodeDaoServiceImpl" />
|
||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
<bean id="persmissionDaoServiceTxnRegistration" class="org.alfresco.repo.transaction.TransactionalDaoInterceptor" >
|
<bean id="persmissionsDaoServiceTxnRegistration" class="org.alfresco.repo.transaction.TransactionalDaoInterceptor" >
|
||||||
<property name="daoService">
|
<property name="daoService">
|
||||||
<ref bean="permissionsDaoComponent" />
|
<ref bean="permissionsDaoComponent" />
|
||||||
</property>
|
</property>
|
||||||
|
@@ -88,3 +88,11 @@ patch.uifacetsAspectRemovalPatch.updated=Successfully removed the uifacets aspec
|
|||||||
|
|
||||||
patch.guestPersonPermission2.description=Change Guest Person permission to visible by all users as 'Consumer'.
|
patch.guestPersonPermission2.description=Change Guest Person permission to visible by all users as 'Consumer'.
|
||||||
patch.guestPersonPermission2.result=Updated Guest Person permission to visible by all users as 'Consumer'.
|
patch.guestPersonPermission2.result=Updated Guest Person permission to visible by all users as 'Consumer'.
|
||||||
|
|
||||||
|
patch.schemaUpgradeScript.description=Ensures that the database upgrade script has been run.
|
||||||
|
patch.schemaUpgradeScript.err.not_executed=The schema upgrade script, ''{0}'', has not been run against this database.
|
||||||
|
|
||||||
|
patch.uniqueChildName.description=Checks and renames duplicate children.
|
||||||
|
patch.uniqueChildName.copyOf=({0})
|
||||||
|
patch.uniqueChildName.result=Checked {0} associations and fixed {1} duplicates. See file {2} for details.
|
||||||
|
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
# System-related messages
|
# System-related messages
|
||||||
|
|
||||||
system.err.property_not_set=Property ''{0}'' has not been set: {1}
|
system.err.property_not_set=Property ''{0}'' has not been set: {1}
|
||||||
|
system.err.duplicate_name=Duplicate child name not allowed: {0}
|
@@ -343,7 +343,6 @@
|
|||||||
<ref bean="searchService"/>
|
<ref bean="searchService"/>
|
||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="patch.scriptsFolder" class="org.alfresco.repo.admin.patch.impl.ScriptsFolderPatch" parent="basePatch">
|
<bean id="patch.scriptsFolder" class="org.alfresco.repo.admin.patch.impl.ScriptsFolderPatch" parent="basePatch">
|
||||||
<property name="id"><value>patch.scriptsFolder</value></property>
|
<property name="id"><value>patch.scriptsFolder</value></property>
|
||||||
<property name="description"><value>patch.scriptsFolder.description</value></property>
|
<property name="description"><value>patch.scriptsFolder.description</value></property>
|
||||||
@@ -362,7 +361,7 @@
|
|||||||
<ref bean="importerComponent" />
|
<ref bean="importerComponent" />
|
||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="patch.topLevelGroupParentChildAssociationTypePatch" class="org.alfresco.repo.admin.patch.impl.TopLevelGroupParentChildAssociationTypePatch" parent="basePatch">
|
<bean id="patch.topLevelGroupParentChildAssociationTypePatch" class="org.alfresco.repo.admin.patch.impl.TopLevelGroupParentChildAssociationTypePatch" parent="basePatch">
|
||||||
<property name="id"><value>patch.topLevelGroupParentChildAssociationTypePatch</value></property>
|
<property name="id"><value>patch.topLevelGroupParentChildAssociationTypePatch</value></property>
|
||||||
<property name="description"><value>patch.topLevelGroupParentChildAssociationTypePatch.description</value></property>
|
<property name="description"><value>patch.topLevelGroupParentChildAssociationTypePatch.description</value></property>
|
||||||
@@ -440,4 +439,38 @@
|
|||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<bean id="patch.schemaUpdateScript-V1.4-1" class="org.alfresco.repo.admin.patch.impl.SchemaUpgradeScriptPatch" parent="basePatch">
|
||||||
|
<property name="id"><value>patch.schemaUpdateScript-V1.4-1</value></property>
|
||||||
|
<property name="description"><value>patch.patch.schemaUpgradeScriptPatch.description</value></property>
|
||||||
|
<property name="fixesFromSchema"><value>0</value></property>
|
||||||
|
<property name="fixesToSchema"><value>19</value></property>
|
||||||
|
<property name="targetSchema"><value>20</value></property>
|
||||||
|
<property name="scriptName">
|
||||||
|
<value>AlfrescoSchemaUpdate-1.4-1-xxx.sql</value>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
<bean id="patch.uniqueChildName" class="org.alfresco.repo.admin.patch.impl.UniqueChildNamePatch" parent="basePatch" >
|
||||||
|
<property name="id"><value>patch.uniqueChildName</value></property>
|
||||||
|
<property name="description"><value>patch.uniqueChildName.description</value></property>
|
||||||
|
<property name="fixesFromSchema"><value>0</value></property>
|
||||||
|
<property name="fixesToSchema"><value>19</value></property>
|
||||||
|
<property name="targetSchema"><value>20</value></property>
|
||||||
|
<!-- helper beans -->
|
||||||
|
<property name="sessionFactory">
|
||||||
|
<ref bean="sessionFactory" />
|
||||||
|
</property>
|
||||||
|
<property name="dictionaryService">
|
||||||
|
<ref bean="dictionaryService" />
|
||||||
|
</property>
|
||||||
|
<property name="nodeDaoService">
|
||||||
|
<ref bean="nodeDaoService" />
|
||||||
|
</property>
|
||||||
|
<!-- dependent on upgrade script 1.4-1 having being run -->
|
||||||
|
<property name="dependsOn" >
|
||||||
|
<list>
|
||||||
|
<ref bean="patch.schemaUpdateScript-V1.4-1" />
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
</beans>
|
</beans>
|
||||||
|
@@ -373,6 +373,7 @@
|
|||||||
org.alfresco.service.cmr.model.FileFolderService.listFiles=ACL_NODE.0.sys:base.ReadChildren,AFTER_ACL_NODE.sys:base.Read
|
org.alfresco.service.cmr.model.FileFolderService.listFiles=ACL_NODE.0.sys:base.ReadChildren,AFTER_ACL_NODE.sys:base.Read
|
||||||
org.alfresco.service.cmr.model.FileFolderService.listFolders=ACL_NODE.0.sys:base.ReadChildren,AFTER_ACL_NODE.sys:base.Read
|
org.alfresco.service.cmr.model.FileFolderService.listFolders=ACL_NODE.0.sys:base.ReadChildren,AFTER_ACL_NODE.sys:base.Read
|
||||||
org.alfresco.service.cmr.model.FileFolderService.search=ACL_NODE.0.sys:base.ReadChildren,AFTER_ACL_NODE.sys:base.Read
|
org.alfresco.service.cmr.model.FileFolderService.search=ACL_NODE.0.sys:base.ReadChildren,AFTER_ACL_NODE.sys:base.Read
|
||||||
|
org.alfresco.service.cmr.model.FileFolderService.searchSimple=ACL_NODE.0.sys:base.ReadChildren,AFTER_ACL_NODE.sys:base.Read
|
||||||
org.alfresco.service.cmr.model.FileFolderService.rename=ACL_PARENT.0.sys:base.CreateChildren,AFTER_ACL_NODE.sys:base.WriteProperties
|
org.alfresco.service.cmr.model.FileFolderService.rename=ACL_PARENT.0.sys:base.CreateChildren,AFTER_ACL_NODE.sys:base.WriteProperties
|
||||||
org.alfresco.service.cmr.model.FileFolderService.move=ACL_NODE.0.sys:base.DeleteNode,ACL_NODE.1.sys:base.CreateChildren
|
org.alfresco.service.cmr.model.FileFolderService.move=ACL_NODE.0.sys:base.DeleteNode,ACL_NODE.1.sys:base.CreateChildren
|
||||||
org.alfresco.service.cmr.model.FileFolderService.copy=ACL_NODE.0.sys:base.Read,ACL_NODE.1.sys:base.CreateChildren
|
org.alfresco.service.cmr.model.FileFolderService.copy=ACL_NODE.0.sys:base.Read,ACL_NODE.1.sys:base.CreateChildren
|
||||||
|
@@ -19,4 +19,4 @@ version.build=@build-number@
|
|||||||
|
|
||||||
# Schema number
|
# Schema number
|
||||||
|
|
||||||
version.schema=19
|
version.schema=20
|
||||||
|
@@ -137,7 +137,7 @@ public class FTPSrvSession extends SrvSession implements Runnable
|
|||||||
|
|
||||||
// Flag to control whether data transfers use a seperate thread
|
// Flag to control whether data transfers use a seperate thread
|
||||||
|
|
||||||
private static boolean UseThreadedDataTransfer = true;
|
private static boolean UseThreadedDataTransfer = false;
|
||||||
|
|
||||||
// Session socket
|
// Session socket
|
||||||
|
|
||||||
|
@@ -31,6 +31,7 @@ import org.alfresco.filesys.server.filesys.FileAttribute;
|
|||||||
import org.alfresco.filesys.server.filesys.FileExistsException;
|
import org.alfresco.filesys.server.filesys.FileExistsException;
|
||||||
import org.alfresco.filesys.server.filesys.FileInfo;
|
import org.alfresco.filesys.server.filesys.FileInfo;
|
||||||
import org.alfresco.filesys.server.filesys.FileName;
|
import org.alfresco.filesys.server.filesys.FileName;
|
||||||
|
import org.alfresco.filesys.util.WildCard;
|
||||||
import org.alfresco.model.ContentModel;
|
import org.alfresco.model.ContentModel;
|
||||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||||
import org.alfresco.service.cmr.model.FileFolderService;
|
import org.alfresco.service.cmr.model.FileFolderService;
|
||||||
@@ -402,11 +403,7 @@ public class CifsHelper
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs an XPath query to get the first-level descendents matching the given path
|
* Searches for the node or nodes that match the path element for the given parent node
|
||||||
*
|
|
||||||
* @param pathRootNodeRef
|
|
||||||
* @param pathElement
|
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
private List<NodeRef> getDirectDescendents(NodeRef pathRootNodeRef, String pathElement)
|
private List<NodeRef> getDirectDescendents(NodeRef pathRootNodeRef, String pathElement)
|
||||||
{
|
{
|
||||||
@@ -416,18 +413,36 @@ public class CifsHelper
|
|||||||
" Path Root: " + pathRootNodeRef + "\n" +
|
" Path Root: " + pathRootNodeRef + "\n" +
|
||||||
" Path Element: " + pathElement);
|
" Path Element: " + pathElement);
|
||||||
}
|
}
|
||||||
// escape for the Lucene syntax search
|
List<NodeRef> results = null;
|
||||||
String escapedPathElement = SearchLanguageConversion.convertCifsToLucene(pathElement);
|
// if this contains no wildcards, then we can fasttrack it
|
||||||
// do the lookup
|
if (!WildCard.containsWildcards(pathElement))
|
||||||
List<org.alfresco.service.cmr.model.FileInfo> childInfos = fileFolderService.search(
|
|
||||||
pathRootNodeRef,
|
|
||||||
escapedPathElement,
|
|
||||||
false);
|
|
||||||
// convert to noderefs
|
|
||||||
List<NodeRef> results = new ArrayList<NodeRef>(childInfos.size());
|
|
||||||
for (org.alfresco.service.cmr.model.FileInfo info : childInfos)
|
|
||||||
{
|
{
|
||||||
results.add(info.getNodeRef());
|
// a specific name is required
|
||||||
|
NodeRef foundNodeRef = fileFolderService.searchSimple(pathRootNodeRef, pathElement);
|
||||||
|
if (foundNodeRef == null)
|
||||||
|
{
|
||||||
|
results = Collections.emptyList();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
results = Collections.singletonList(foundNodeRef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// escape for the Lucene syntax search
|
||||||
|
String escapedPathElement = SearchLanguageConversion.convertCifsToLucene(pathElement);
|
||||||
|
// do the lookup
|
||||||
|
List<org.alfresco.service.cmr.model.FileInfo> childInfos = fileFolderService.search(
|
||||||
|
pathRootNodeRef,
|
||||||
|
escapedPathElement,
|
||||||
|
false);
|
||||||
|
// convert to noderefs
|
||||||
|
results = new ArrayList<NodeRef>(childInfos.size());
|
||||||
|
for (org.alfresco.service.cmr.model.FileInfo info : childInfos)
|
||||||
|
{
|
||||||
|
results.add(info.getNodeRef());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// done
|
// done
|
||||||
return results;
|
return results;
|
||||||
|
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005 Alfresco, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Mozilla Public License version 1.1
|
||||||
|
* with a permitted attribution clause. You may obtain a
|
||||||
|
* copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.alfresco.org/legal/license.txt
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||||
|
* either express or implied. See the License for the specific
|
||||||
|
* language governing permissions and limitations under the
|
||||||
|
* License.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.admin.patch.impl;
|
||||||
|
|
||||||
|
import org.alfresco.repo.admin.patch.AbstractPatch;
|
||||||
|
import org.alfresco.service.cmr.admin.PatchException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This patch ensures that an upgrade script has been executed. Upgrade scripts
|
||||||
|
* should create an entry for the patch with the required ID and execution status
|
||||||
|
* so that the code in this class is never called. If called, an exception message
|
||||||
|
* is always generated.
|
||||||
|
*
|
||||||
|
* @author Derek Hulley
|
||||||
|
*/
|
||||||
|
public class SchemaUpgradeScriptPatch extends AbstractPatch
|
||||||
|
{
|
||||||
|
private static final String MSG_NOT_EXECUTED = "patch.schemaUpgradeScript.err.not_executed";
|
||||||
|
|
||||||
|
private String scriptName;
|
||||||
|
|
||||||
|
public SchemaUpgradeScriptPatch()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the name of the upgrade script to execute.
|
||||||
|
*
|
||||||
|
* @param scriptName the script filename
|
||||||
|
*/
|
||||||
|
public void setScriptName(String scriptName)
|
||||||
|
{
|
||||||
|
this.scriptName = scriptName;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void checkProperties()
|
||||||
|
{
|
||||||
|
super.checkProperties();
|
||||||
|
checkPropertyNotNull(scriptName, "scriptName");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see #MSG_NOT_EXECUTED
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected String applyInternal() throws Exception
|
||||||
|
{
|
||||||
|
throw new PatchException(MSG_NOT_EXECUTED, scriptName);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,268 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005 Alfresco, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Mozilla Public License version 1.1
|
||||||
|
* with a permitted attribution clause. You may obtain a
|
||||||
|
* copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.alfresco.org/legal/license.txt
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||||
|
* either express or implied. See the License for the specific
|
||||||
|
* language governing permissions and limitations under the
|
||||||
|
* License.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.admin.patch.impl;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.alfresco.i18n.I18NUtil;
|
||||||
|
import org.alfresco.model.ContentModel;
|
||||||
|
import org.alfresco.repo.admin.patch.AbstractPatch;
|
||||||
|
import org.alfresco.repo.domain.ChildAssoc;
|
||||||
|
import org.alfresco.repo.domain.Node;
|
||||||
|
import org.alfresco.repo.node.db.NodeDaoService;
|
||||||
|
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
|
||||||
|
import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition;
|
||||||
|
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||||
|
import org.alfresco.service.cmr.repository.DuplicateChildNodeNameException;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
import org.alfresco.service.cmr.repository.Path;
|
||||||
|
import org.alfresco.service.namespace.QName;
|
||||||
|
import org.hibernate.Query;
|
||||||
|
import org.hibernate.Session;
|
||||||
|
import org.hibernate.SessionFactory;
|
||||||
|
import org.springframework.orm.hibernate3.HibernateCallback;
|
||||||
|
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that all child node names are unique for the associations that require it.
|
||||||
|
*
|
||||||
|
* @author Derek Hulley
|
||||||
|
*/
|
||||||
|
public class UniqueChildNamePatch extends AbstractPatch
|
||||||
|
{
|
||||||
|
private static final String MSG_SUCCESS = "patch.uniqueChildName.result";
|
||||||
|
private static final String MSG_COPY_OF = "patch.uniqueChildName.copyOf";
|
||||||
|
/** the number of associations to process at a time */
|
||||||
|
private static final int MAX_RESULTS = 1000;
|
||||||
|
|
||||||
|
private SessionFactory sessionFactory;
|
||||||
|
private DictionaryService dictionaryService;
|
||||||
|
private NodeDaoService nodeDaoService;
|
||||||
|
|
||||||
|
public UniqueChildNamePatch()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSessionFactory(SessionFactory sessionFactory)
|
||||||
|
{
|
||||||
|
this.sessionFactory = sessionFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param dictionaryService The service used to sort out the associations
|
||||||
|
* that require duplicate checks
|
||||||
|
*/
|
||||||
|
public void setDictionaryService(DictionaryService dictionaryService)
|
||||||
|
{
|
||||||
|
this.dictionaryService = dictionaryService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param nodeDaoService The service that generates the CRC values
|
||||||
|
*/
|
||||||
|
public void setNodeDaoService(NodeDaoService nodeDaoService)
|
||||||
|
{
|
||||||
|
this.nodeDaoService = nodeDaoService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void checkProperties()
|
||||||
|
{
|
||||||
|
super.checkProperties();
|
||||||
|
checkPropertyNotNull(sessionFactory, "sessionFactory");
|
||||||
|
checkPropertyNotNull(dictionaryService, "dictionaryService");
|
||||||
|
checkPropertyNotNull(nodeDaoService, "nodeDaoService");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String applyInternal() throws Exception
|
||||||
|
{
|
||||||
|
// initialise the helper
|
||||||
|
HibernateHelper helper = new HibernateHelper();
|
||||||
|
helper.setSessionFactory(sessionFactory);
|
||||||
|
|
||||||
|
String msg = helper.assignCrc();
|
||||||
|
|
||||||
|
// done
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class HibernateHelper extends HibernateDaoSupport
|
||||||
|
{
|
||||||
|
private File logFile;
|
||||||
|
private FileChannel channel;
|
||||||
|
|
||||||
|
private HibernateHelper() throws IOException
|
||||||
|
{
|
||||||
|
logFile = new File("./UniqueChildNamePatch.log");
|
||||||
|
// open the file for appending
|
||||||
|
RandomAccessFile outputFile = new RandomAccessFile(logFile, "rw");
|
||||||
|
channel = outputFile.getChannel();
|
||||||
|
// move to the end of the file
|
||||||
|
channel.position(channel.size());
|
||||||
|
// add a newline and it's ready
|
||||||
|
writeLine("").writeLine("");
|
||||||
|
writeLine("UniqueChildNamePatch executing on " + new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
private HibernateHelper write(Object obj) throws IOException
|
||||||
|
{
|
||||||
|
channel.write(ByteBuffer.wrap(obj.toString().getBytes()));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
private HibernateHelper writeLine(Object obj) throws IOException
|
||||||
|
{
|
||||||
|
write(obj);
|
||||||
|
write("\n");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String assignCrc() throws Exception
|
||||||
|
{
|
||||||
|
// get the association types to check
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
List<QName> assocTypeQNames = getUsedAssocQNames();
|
||||||
|
|
||||||
|
int fixed = 0;
|
||||||
|
int processed = 0;
|
||||||
|
// check loop through all associations, looking for duplicates
|
||||||
|
for (QName assocTypeQName : assocTypeQNames)
|
||||||
|
{
|
||||||
|
AssociationDefinition assocDef = dictionaryService.getAssociation(assocTypeQName);
|
||||||
|
if (!(assocDef instanceof ChildAssociationDefinition))
|
||||||
|
{
|
||||||
|
String msg = "WARNING: Non-child association used to link a child node: " + assocTypeQName;
|
||||||
|
writeLine(msg);
|
||||||
|
logger.warn(msg);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ChildAssociationDefinition childAssocDef = (ChildAssociationDefinition) assocDef;
|
||||||
|
if (childAssocDef.getDuplicateChildNamesAllowed())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
write("Checking for name duplicates on association type ").writeLine(assocTypeQName);
|
||||||
|
|
||||||
|
// get all child associations until there are no more results
|
||||||
|
long lastAssocId = Long.MIN_VALUE;
|
||||||
|
int lastResultCount = 1;
|
||||||
|
while(lastResultCount > 0)
|
||||||
|
{
|
||||||
|
writeLine(String.format("...Processed %7d associations with %3d duplicates found...", processed, fixed));
|
||||||
|
|
||||||
|
List<Object[]> results = getAssociations(assocTypeQName, lastAssocId) ;
|
||||||
|
lastResultCount = results.size();
|
||||||
|
for (Object[] objects : results)
|
||||||
|
{
|
||||||
|
ChildAssoc childAssoc = (ChildAssoc) objects[0];
|
||||||
|
Node childNode = (Node) objects[1];
|
||||||
|
NodeRef childNodeRef = childNode.getNodeRef();
|
||||||
|
|
||||||
|
// get the current name
|
||||||
|
String childName = (String) nodeService.getProperty(childNodeRef, ContentModel.PROP_NAME);
|
||||||
|
|
||||||
|
lastAssocId = childAssoc.getId();
|
||||||
|
String usedChildName = childName;
|
||||||
|
processed++;
|
||||||
|
boolean duplicate = false;
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// push the name back to the node
|
||||||
|
nodeService.setProperty(childNodeRef, ContentModel.PROP_NAME, usedChildName);
|
||||||
|
break; // no issues - no duplicate
|
||||||
|
}
|
||||||
|
catch (DuplicateChildNodeNameException e)
|
||||||
|
{
|
||||||
|
// there was a duplicate, so adjust the name and change the node property
|
||||||
|
duplicate = true;
|
||||||
|
// assign a new name
|
||||||
|
usedChildName = childName + I18NUtil.getMessage(MSG_COPY_OF, processed);
|
||||||
|
// try again
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if duplicated, report it
|
||||||
|
if (duplicate)
|
||||||
|
{
|
||||||
|
fixed++;
|
||||||
|
// get the node path
|
||||||
|
NodeRef parentNodeRef = childAssoc.getParent().getNodeRef();
|
||||||
|
Path path = nodeService.getPath(parentNodeRef);
|
||||||
|
writeLine(" Changed duplicated child name:");
|
||||||
|
writeLine(" Parent: " + parentNodeRef);
|
||||||
|
writeLine(" Parent path: " + path);
|
||||||
|
writeLine(" Duplicate name: " + childName);
|
||||||
|
writeLine(" Replaced with: " + usedChildName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// build the result message
|
||||||
|
String msg = I18NUtil.getMessage(MSG_SUCCESS, processed, fixed, logFile);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private List<QName> getUsedAssocQNames()
|
||||||
|
{
|
||||||
|
HibernateCallback callback = new HibernateCallback()
|
||||||
|
{
|
||||||
|
public Object doInHibernate(Session session)
|
||||||
|
{
|
||||||
|
Query query = session
|
||||||
|
.createQuery(
|
||||||
|
"select distinct assoc.typeQName " +
|
||||||
|
"from org.alfresco.repo.domain.hibernate.ChildAssocImpl as assoc");
|
||||||
|
return query.list();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
List<QName> results = (List<QName>) getHibernateTemplate().execute(callback);
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns a list of <tt>ChildAssoc</tt> and <tt>String</tt> instances
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private List<Object[]> getAssociations(final QName assocTypeQName, final long lastAssocId)
|
||||||
|
{
|
||||||
|
HibernateCallback callback = new HibernateCallback()
|
||||||
|
{
|
||||||
|
public Object doInHibernate(Session session)
|
||||||
|
{
|
||||||
|
Query query = session
|
||||||
|
.getNamedQuery("node.patch.GetAssocsAndChildNames")
|
||||||
|
.setLong("lastAssocId", lastAssocId)
|
||||||
|
.setParameter("assocTypeQName", assocTypeQName)
|
||||||
|
.setMaxResults(MAX_RESULTS);
|
||||||
|
return query.list();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
List<Object[]> results = (List<Object[]>) getHibernateTemplate().execute(callback);
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -22,6 +22,9 @@ import java.io.FileOutputStream;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.FileChannel;
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.zip.CRC32;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
@@ -111,4 +114,30 @@ public class FileIOTest extends TestCase
|
|||||||
countRead = channelRead.read(bufferRead);
|
countRead = channelRead.read(bufferRead);
|
||||||
assertEquals("Expected full read", 26, countRead);
|
assertEquals("Expected full read", 26, countRead);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testCrcPerformance() throws Exception
|
||||||
|
{
|
||||||
|
long before = System.nanoTime();
|
||||||
|
int count = 1000000;
|
||||||
|
Set<Long> results = new HashSet<Long>(count);
|
||||||
|
boolean negatives = false;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
CRC32 crc = new CRC32();
|
||||||
|
crc.update(Integer.toString(i).getBytes());
|
||||||
|
long value = crc.getValue();
|
||||||
|
if (value < 0)
|
||||||
|
{
|
||||||
|
negatives = true;
|
||||||
|
}
|
||||||
|
if (!results.add(value))
|
||||||
|
{
|
||||||
|
System.out.println("Duplicate on " + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
long after = System.nanoTime();
|
||||||
|
long delta = after - before;
|
||||||
|
double aveNs = (double)delta / (double)count;
|
||||||
|
System.out.println(String.format("CRC32: %10.2f ns per item. Negatives=" + negatives, aveNs));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -66,6 +66,28 @@ public interface ChildAssoc extends Comparable<ChildAssoc>
|
|||||||
*/
|
*/
|
||||||
public void setTypeQName(QName assocTypeQName);
|
public void setTypeQName(QName assocTypeQName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns the child node name. This may be truncated, in which case it
|
||||||
|
* will end with <b>...</b>
|
||||||
|
*/
|
||||||
|
public String getChildNodeName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param childNodeName the name of the child node, which may be truncated and
|
||||||
|
* terminated with <b>...</b> in order to not exceed 50 characters.
|
||||||
|
*/
|
||||||
|
public void setChildNodeName(String childNodeName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns the crc value for the child node name.
|
||||||
|
*/
|
||||||
|
public long getChildNodeNameCrc();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param crc the crc value
|
||||||
|
*/
|
||||||
|
public void setChildNodeNameCrc(long crc);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Returns the qualified name of this association
|
* @return Returns the qualified name of this association
|
||||||
*/
|
*/
|
||||||
|
@@ -56,24 +56,8 @@ public interface Node
|
|||||||
|
|
||||||
public void setTypeQName(QName typeQName);
|
public void setTypeQName(QName typeQName);
|
||||||
|
|
||||||
// public NodeStatus getStatus();
|
|
||||||
//
|
|
||||||
// public void setStatus(NodeStatus status);
|
|
||||||
//
|
|
||||||
public Set<QName> getAspects();
|
public Set<QName> getAspects();
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Returns all the regular associations for which this node is a target
|
|
||||||
*/
|
|
||||||
public Collection<NodeAssoc> getSourceNodeAssocs();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Returns all the regular associations for which this node is a source
|
|
||||||
*/
|
|
||||||
public Collection<NodeAssoc> getTargetNodeAssocs();
|
|
||||||
|
|
||||||
public Collection<ChildAssoc> getChildAssocs();
|
|
||||||
|
|
||||||
public Collection<ChildAssoc> getParentAssocs();
|
public Collection<ChildAssoc> getParentAssocs();
|
||||||
|
|
||||||
public Map<QName, PropertyValue> getProperties();
|
public Map<QName, PropertyValue> getProperties();
|
||||||
|
@@ -42,12 +42,6 @@ public interface NodeAssoc
|
|||||||
*/
|
*/
|
||||||
public void buildAssociation(Node sourceNode, Node targetNode);
|
public void buildAssociation(Node sourceNode, Node targetNode);
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs the necessary work on the {@link #getSource()() source} and
|
|
||||||
* {@link #getTarget()() target} nodes to maintain the inverse association sets
|
|
||||||
*/
|
|
||||||
public void removeAssociation();
|
|
||||||
|
|
||||||
public AssociationRef getNodeAssocRef();
|
public AssociationRef getNodeAssocRef();
|
||||||
|
|
||||||
public Node getSource();
|
public Node getSource();
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
<class
|
<class
|
||||||
name="org.alfresco.repo.domain.hibernate.AppliedPatchImpl"
|
name="org.alfresco.repo.domain.hibernate.AppliedPatchImpl"
|
||||||
proxy="org.alfresco.repo.domain.AppliedPatch"
|
proxy="org.alfresco.repo.domain.AppliedPatch"
|
||||||
table="applied_patch"
|
table="alf_applied_patch"
|
||||||
dynamic-update="false"
|
dynamic-update="false"
|
||||||
dynamic-insert="false"
|
dynamic-insert="false"
|
||||||
select-before-update="false"
|
select-before-update="false"
|
||||||
|
@@ -38,6 +38,8 @@ public class ChildAssocImpl implements ChildAssoc, Serializable
|
|||||||
private Node parent;
|
private Node parent;
|
||||||
private Node child;
|
private Node child;
|
||||||
private QName typeQName;
|
private QName typeQName;
|
||||||
|
private String childNodeName;
|
||||||
|
private long childNodeNameCrc;
|
||||||
private QName qName;
|
private QName qName;
|
||||||
private boolean isPrimary;
|
private boolean isPrimary;
|
||||||
private int index;
|
private int index;
|
||||||
@@ -60,15 +62,11 @@ public class ChildAssocImpl implements ChildAssoc, Serializable
|
|||||||
// add the forward associations
|
// add the forward associations
|
||||||
this.setParent(parentNode);
|
this.setParent(parentNode);
|
||||||
this.setChild(childNode);
|
this.setChild(childNode);
|
||||||
// add the inverse associations
|
|
||||||
parentNode.getChildAssocs().add(this);
|
|
||||||
childNode.getParentAssocs().add(this);
|
childNode.getParentAssocs().add(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeAssociation()
|
public void removeAssociation()
|
||||||
{
|
{
|
||||||
// maintain inverse assoc from parent node to this instance
|
|
||||||
this.getParent().getChildAssocs().remove(this);
|
|
||||||
// maintain inverse assoc from child node to this instance
|
// maintain inverse assoc from child node to this instance
|
||||||
this.getChild().getParentAssocs().remove(this);
|
this.getChild().getParentAssocs().remove(this);
|
||||||
}
|
}
|
||||||
@@ -128,7 +126,8 @@ public class ChildAssocImpl implements ChildAssoc, Serializable
|
|||||||
sb.append("ChildAssoc")
|
sb.append("ChildAssoc")
|
||||||
.append("[ parent=").append(parent)
|
.append("[ parent=").append(parent)
|
||||||
.append(", child=").append(child)
|
.append(", child=").append(child)
|
||||||
.append(", name=").append(getQname())
|
.append(", child name crc=").append(childNodeNameCrc)
|
||||||
|
.append(", assoc name=").append(getQname())
|
||||||
.append(", isPrimary=").append(isPrimary)
|
.append(", isPrimary=").append(isPrimary)
|
||||||
.append("]");
|
.append("]");
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
@@ -281,6 +280,26 @@ public class ChildAssocImpl implements ChildAssoc, Serializable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getChildNodeName()
|
||||||
|
{
|
||||||
|
return childNodeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChildNodeName(String childNodeName)
|
||||||
|
{
|
||||||
|
this.childNodeName = childNodeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getChildNodeNameCrc()
|
||||||
|
{
|
||||||
|
return childNodeNameCrc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChildNodeNameCrc(long crc)
|
||||||
|
{
|
||||||
|
this.childNodeNameCrc = crc;
|
||||||
|
}
|
||||||
|
|
||||||
public QName getQname()
|
public QName getQname()
|
||||||
{
|
{
|
||||||
return qName;
|
return qName;
|
||||||
|
@@ -20,7 +20,6 @@ import java.io.Serializable;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@@ -28,9 +27,7 @@ import javax.transaction.UserTransaction;
|
|||||||
|
|
||||||
import org.alfresco.model.ContentModel;
|
import org.alfresco.model.ContentModel;
|
||||||
import org.alfresco.repo.domain.ChildAssoc;
|
import org.alfresco.repo.domain.ChildAssoc;
|
||||||
import org.alfresco.repo.domain.DbAccessControlList;
|
|
||||||
import org.alfresco.repo.domain.Node;
|
import org.alfresco.repo.domain.Node;
|
||||||
import org.alfresco.repo.domain.NodeAssoc;
|
|
||||||
import org.alfresco.repo.domain.NodeKey;
|
import org.alfresco.repo.domain.NodeKey;
|
||||||
import org.alfresco.repo.domain.NodeStatus;
|
import org.alfresco.repo.domain.NodeStatus;
|
||||||
import org.alfresco.repo.domain.PropertyValue;
|
import org.alfresco.repo.domain.PropertyValue;
|
||||||
@@ -215,52 +212,6 @@ public class HibernateNodeTest extends BaseSpringTest
|
|||||||
assertEquals("Not all aspects persisted", 4, aspects.size());
|
assertEquals("Not all aspects persisted", 4, aspects.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNodeAssoc() throws Exception
|
|
||||||
{
|
|
||||||
// make a source node
|
|
||||||
Node sourceNode = new NodeImpl();
|
|
||||||
sourceNode.setStore(store);
|
|
||||||
sourceNode.setUuid(GUID.generate());
|
|
||||||
sourceNode.setTypeQName(ContentModel.TYPE_CMOBJECT);
|
|
||||||
Serializable realNodeId = getSession().save(sourceNode);
|
|
||||||
|
|
||||||
// make a container node
|
|
||||||
Node targetNode = new NodeImpl();
|
|
||||||
targetNode.setStore(store);
|
|
||||||
targetNode.setStore(store);
|
|
||||||
targetNode.setUuid(GUID.generate());
|
|
||||||
targetNode.setTypeQName(ContentModel.TYPE_CONTAINER);
|
|
||||||
Serializable containerNodeId = getSession().save(targetNode);
|
|
||||||
|
|
||||||
// create an association between them
|
|
||||||
NodeAssoc assoc = new NodeAssocImpl();
|
|
||||||
assoc.setTypeQName(QName.createQName("next"));
|
|
||||||
assoc.buildAssociation(sourceNode, targetNode);
|
|
||||||
getSession().save(assoc);
|
|
||||||
|
|
||||||
// make another association between the same two nodes
|
|
||||||
assoc = new NodeAssocImpl();
|
|
||||||
assoc.setTypeQName(QName.createQName("helper"));
|
|
||||||
assoc.buildAssociation(sourceNode, targetNode);
|
|
||||||
getSession().save(assoc);
|
|
||||||
|
|
||||||
// flush and clear the session
|
|
||||||
getSession().flush();
|
|
||||||
getSession().clear();
|
|
||||||
|
|
||||||
// reload the source
|
|
||||||
sourceNode = (Node) getSession().get(NodeImpl.class, realNodeId);
|
|
||||||
assertNotNull("Source node not found", sourceNode);
|
|
||||||
// check that the associations are present
|
|
||||||
assertEquals("Expected exactly 2 target assocs", 2, sourceNode.getTargetNodeAssocs().size());
|
|
||||||
|
|
||||||
// reload the target
|
|
||||||
targetNode = (Node) getSession().get(NodeImpl.class, containerNodeId);
|
|
||||||
assertNotNull("Target node not found", targetNode);
|
|
||||||
// check that the associations are present
|
|
||||||
assertEquals("Expected exactly 2 source assocs", 2, targetNode.getSourceNodeAssocs().size());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testChildAssoc() throws Exception
|
public void testChildAssoc() throws Exception
|
||||||
{
|
{
|
||||||
// make a content node
|
// make a content node
|
||||||
@@ -287,7 +238,7 @@ public class HibernateNodeTest extends BaseSpringTest
|
|||||||
// make another association between the same two parent and child nodes
|
// make another association between the same two parent and child nodes
|
||||||
ChildAssoc assoc2 = new ChildAssocImpl();
|
ChildAssoc assoc2 = new ChildAssocImpl();
|
||||||
assoc2.setIsPrimary(true);
|
assoc2.setIsPrimary(true);
|
||||||
assoc2.setTypeQName(QName.createQName(null, "type1"));
|
assoc2.setTypeQName(QName.createQName(null, "type2"));
|
||||||
assoc2.setQname(QName.createQName(null, "number2"));
|
assoc2.setQname(QName.createQName(null, "number2"));
|
||||||
assoc2.buildAssociation(containerNode, contentNode);
|
assoc2.buildAssociation(containerNode, contentNode);
|
||||||
getSession().save(assoc2);
|
getSession().save(assoc2);
|
||||||
@@ -295,21 +246,9 @@ public class HibernateNodeTest extends BaseSpringTest
|
|||||||
assertFalse("Hashcode incorrent", assoc2.hashCode() == 0);
|
assertFalse("Hashcode incorrent", assoc2.hashCode() == 0);
|
||||||
assertNotSame("Assoc equals failure", assoc1, assoc2);
|
assertNotSame("Assoc equals failure", assoc1, assoc2);
|
||||||
|
|
||||||
// flushAndClear();
|
|
||||||
|
|
||||||
// reload the container
|
// reload the container
|
||||||
containerNode = (Node) getSession().get(NodeImpl.class, containerNodeId);
|
containerNode = (Node) getSession().get(NodeImpl.class, containerNodeId);
|
||||||
assertNotNull("Node not found", containerNode);
|
assertNotNull("Node not found", containerNode);
|
||||||
// check
|
|
||||||
assertEquals("Expected exactly 2 children", 2, containerNode.getChildAssocs().size());
|
|
||||||
for (Iterator iterator = containerNode.getChildAssocs().iterator(); iterator.hasNext(); /**/)
|
|
||||||
{
|
|
||||||
ChildAssoc assoc = (ChildAssoc) iterator.next();
|
|
||||||
// the node id must be known
|
|
||||||
assertNotNull("Node not populated on assoc", assoc.getChild());
|
|
||||||
assertEquals("Node key on child assoc is incorrect",
|
|
||||||
contentNodeId, assoc.getChild().getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
// check that we can traverse the association from the child
|
// check that we can traverse the association from the child
|
||||||
Collection<ChildAssoc> parentAssocs = contentNode.getParentAssocs();
|
Collection<ChildAssoc> parentAssocs = contentNode.getParentAssocs();
|
||||||
@@ -362,7 +301,6 @@ public class HibernateNodeTest extends BaseSpringTest
|
|||||||
Map<QName, PropertyValue> checkProperties = checkNode.getProperties();
|
Map<QName, PropertyValue> checkProperties = checkNode.getProperties();
|
||||||
assertTrue("Propery map retrieved was not the same instance", checkProperties == properties);
|
assertTrue("Propery map retrieved was not the same instance", checkProperties == properties);
|
||||||
assertTrue("Property not found", checkProperties.containsKey(ContentModel.PROP_NAME));
|
assertTrue("Property not found", checkProperties.containsKey(ContentModel.PROP_NAME));
|
||||||
// assertTrue("Property value instance retrieved not the same", checkProperties)
|
|
||||||
|
|
||||||
flushAndClear();
|
flushAndClear();
|
||||||
// commit the transaction
|
// commit the transaction
|
||||||
@@ -380,8 +318,6 @@ public class HibernateNodeTest extends BaseSpringTest
|
|||||||
assertNotNull(checkNode);
|
assertNotNull(checkNode);
|
||||||
checkAspects = checkNode.getAspects();
|
checkAspects = checkNode.getAspects();
|
||||||
|
|
||||||
// assertTrue("Node retrieved was not same instance", checkNode == node);
|
|
||||||
|
|
||||||
txn.commit();
|
txn.commit();
|
||||||
}
|
}
|
||||||
catch (Throwable e)
|
catch (Throwable e)
|
||||||
@@ -465,37 +401,10 @@ public class HibernateNodeTest extends BaseSpringTest
|
|||||||
// now read the structure back in from the container down
|
// now read the structure back in from the container down
|
||||||
containerNodeStatus = (NodeStatus) getSession().get(NodeStatusImpl.class, containerNodeKey);
|
containerNodeStatus = (NodeStatus) getSession().get(NodeStatusImpl.class, containerNodeKey);
|
||||||
containerNode = containerNodeStatus.getNode();
|
containerNode = containerNodeStatus.getNode();
|
||||||
Collection<ChildAssoc> assocs = containerNode.getChildAssocs();
|
|
||||||
for (ChildAssoc assoc : assocs)
|
|
||||||
{
|
|
||||||
Node childNode = assoc.getChild();
|
|
||||||
Store store = childNode.getStore();
|
|
||||||
childNode.getAspects().size();
|
|
||||||
childNode.getProperties().size();
|
|
||||||
childNode.getParentAssocs().size();
|
|
||||||
childNode.getChildAssocs().size();
|
|
||||||
childNode.getSourceNodeAssocs().size();
|
|
||||||
childNode.getTargetNodeAssocs().size();
|
|
||||||
DbAccessControlList acl = childNode.getAccessControlList();
|
|
||||||
if (acl != null)
|
|
||||||
{
|
|
||||||
acl.getEntries().size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear out again
|
// clear out again
|
||||||
getSession().clear();
|
getSession().clear();
|
||||||
|
|
||||||
// now remove a property from each child
|
|
||||||
containerNodeStatus = (NodeStatus) getSession().get(NodeStatusImpl.class, containerNodeKey);
|
|
||||||
containerNode = containerNodeStatus.getNode();
|
|
||||||
assocs = containerNode.getChildAssocs();
|
|
||||||
for (ChildAssoc assoc : assocs)
|
|
||||||
{
|
|
||||||
Node childNode = assoc.getChild();
|
|
||||||
PropertyValue removed = childNode.getProperties().remove(ContentModel.PROP_ARCHIVED_BY);
|
|
||||||
assertNotNull("Property was not present", removed);
|
|
||||||
}
|
|
||||||
// expect that just the specific property gets removed in the delete statement
|
// expect that just the specific property gets removed in the delete statement
|
||||||
getSession().flush();
|
getSession().flush();
|
||||||
getSession().clear();
|
getSession().clear();
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
<class
|
<class
|
||||||
name="org.alfresco.repo.domain.hibernate.NodeImpl"
|
name="org.alfresco.repo.domain.hibernate.NodeImpl"
|
||||||
proxy="org.alfresco.repo.domain.Node"
|
proxy="org.alfresco.repo.domain.Node"
|
||||||
table="node"
|
table="alf_node"
|
||||||
dynamic-update="false"
|
dynamic-update="false"
|
||||||
dynamic-insert="false"
|
dynamic-insert="false"
|
||||||
select-before-update="false"
|
select-before-update="false"
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
<!-- forward assoc to properties -->
|
<!-- forward assoc to properties -->
|
||||||
<map
|
<map
|
||||||
name="properties"
|
name="properties"
|
||||||
table="node_properties"
|
table="alf_node_properties"
|
||||||
lazy="true"
|
lazy="true"
|
||||||
fetch="select"
|
fetch="select"
|
||||||
sort="unsorted"
|
sort="unsorted"
|
||||||
@@ -75,7 +75,7 @@
|
|||||||
<!-- forward assoc to aspects -->
|
<!-- forward assoc to aspects -->
|
||||||
<set
|
<set
|
||||||
name="aspects"
|
name="aspects"
|
||||||
table="node_aspects"
|
table="alf_node_aspects"
|
||||||
lazy="false"
|
lazy="false"
|
||||||
fetch="join"
|
fetch="join"
|
||||||
inverse="false"
|
inverse="false"
|
||||||
@@ -96,45 +96,12 @@
|
|||||||
<key column="child_node_id" />
|
<key column="child_node_id" />
|
||||||
<one-to-many class="org.alfresco.repo.domain.hibernate.ChildAssocImpl" />
|
<one-to-many class="org.alfresco.repo.domain.hibernate.ChildAssocImpl" />
|
||||||
</set>
|
</set>
|
||||||
<!-- inverse assoc to child childassocs -->
|
|
||||||
<set
|
|
||||||
name="childAssocs"
|
|
||||||
inverse="true"
|
|
||||||
lazy="true"
|
|
||||||
fetch="select"
|
|
||||||
cascade="none"
|
|
||||||
optimistic-lock="true" >
|
|
||||||
<key column="parent_node_id" />
|
|
||||||
<one-to-many class="org.alfresco.repo.domain.hibernate.ChildAssocImpl" />
|
|
||||||
</set>
|
|
||||||
<!-- inverse assoc to source nodeassocs -->
|
|
||||||
<set
|
|
||||||
name="sourceNodeAssocs"
|
|
||||||
inverse="true"
|
|
||||||
lazy="true"
|
|
||||||
fetch="select"
|
|
||||||
cascade="none"
|
|
||||||
optimistic-lock="true" >
|
|
||||||
<key column="target_node_id" />
|
|
||||||
<one-to-many class="org.alfresco.repo.domain.hibernate.NodeAssocImpl" />
|
|
||||||
</set>
|
|
||||||
<!-- inverse assoc to target nodeassocs -->
|
|
||||||
<set
|
|
||||||
name="targetNodeAssocs"
|
|
||||||
inverse="true"
|
|
||||||
lazy="true"
|
|
||||||
fetch="select"
|
|
||||||
cascade="none"
|
|
||||||
optimistic-lock="true" >
|
|
||||||
<key column="source_node_id" />
|
|
||||||
<one-to-many class="org.alfresco.repo.domain.hibernate.NodeAssocImpl" />
|
|
||||||
</set>
|
|
||||||
</class>
|
</class>
|
||||||
|
|
||||||
<class
|
<class
|
||||||
name="org.alfresco.repo.domain.hibernate.NodeStatusImpl"
|
name="org.alfresco.repo.domain.hibernate.NodeStatusImpl"
|
||||||
proxy="org.alfresco.repo.domain.NodeStatus"
|
proxy="org.alfresco.repo.domain.NodeStatus"
|
||||||
table="node_status"
|
table="alf_node_status"
|
||||||
dynamic-update="false"
|
dynamic-update="false"
|
||||||
dynamic-insert="false"
|
dynamic-insert="false"
|
||||||
select-before-update="false"
|
select-before-update="false"
|
||||||
@@ -155,7 +122,7 @@
|
|||||||
fetch="join"
|
fetch="join"
|
||||||
unique="false"
|
unique="false"
|
||||||
not-null="false" />
|
not-null="false" />
|
||||||
<property name="changeTxnId" column="change_txn_id" type="string" length="56" not-null="true" />
|
<property name="changeTxnId" column="change_txn_id" type="string" length="56" not-null="true" index="CHANGE_TXN_ID"/>
|
||||||
</class>
|
</class>
|
||||||
|
|
||||||
<class
|
<class
|
||||||
@@ -165,32 +132,37 @@
|
|||||||
dynamic-update="false"
|
dynamic-update="false"
|
||||||
lazy="true"
|
lazy="true"
|
||||||
optimistic-lock="version"
|
optimistic-lock="version"
|
||||||
table="child_assoc" >
|
table="alf_child_assoc" >
|
||||||
<!-- auto-generated ID -->
|
<!-- auto-generated ID -->
|
||||||
<id name="id" column="id" type="long" >
|
<id name="id" column="id" type="long" >
|
||||||
<generator class="native" />
|
<generator class="native" />
|
||||||
</id>
|
</id>
|
||||||
<!-- forward assoc to parent node -->
|
<natural-id mutable="true">
|
||||||
<many-to-one
|
<!-- forward assoc to parent node -->
|
||||||
name="parent"
|
<many-to-one
|
||||||
class="org.alfresco.repo.domain.hibernate.NodeImpl"
|
name="parent"
|
||||||
lazy="proxy"
|
class="org.alfresco.repo.domain.hibernate.NodeImpl"
|
||||||
fetch="select"
|
lazy="proxy"
|
||||||
optimistic-lock="true"
|
fetch="select"
|
||||||
not-null="true" >
|
optimistic-lock="true"
|
||||||
<column name="parent_node_id" />
|
not-null="true"
|
||||||
</many-to-one>
|
unique-key="UIDX_CHILD_NAME" >
|
||||||
<!-- forward assoc to child node -->
|
<column name="parent_node_id" />
|
||||||
<many-to-one
|
</many-to-one>
|
||||||
name="child"
|
<!-- forward assoc to child node -->
|
||||||
lazy="proxy"
|
<many-to-one
|
||||||
fetch="select"
|
name="child"
|
||||||
class="org.alfresco.repo.domain.hibernate.NodeImpl"
|
lazy="proxy"
|
||||||
optimistic-lock="true"
|
fetch="select"
|
||||||
not-null="true" >
|
class="org.alfresco.repo.domain.hibernate.NodeImpl"
|
||||||
<column name="child_node_id" />
|
optimistic-lock="true"
|
||||||
</many-to-one>
|
not-null="true" >
|
||||||
<property name="typeQName" column="type_qname" type="QName" length="255" not-null="true" />
|
<column name="child_node_id" />
|
||||||
|
</many-to-one>
|
||||||
|
<property name="typeQName" column="type_qname" type="QName" length="255" not-null="true" unique-key="UIDX_CHILD_NAME" />
|
||||||
|
</natural-id>
|
||||||
|
<property name="childNodeName" column="child_node_name" type="string" length="50" not-null="true" unique-key="UIDX_CHILD_NAME" />
|
||||||
|
<property name="childNodeNameCrc" column="child_node_name_crc" type="long" not-null="true" unique-key="UIDX_CHILD_NAME" />
|
||||||
<property name="qname" column="qname" type="QName" length="255" not-null="true" />
|
<property name="qname" column="qname" type="QName" length="255" not-null="true" />
|
||||||
<property name="isPrimary" column="is_primary" />
|
<property name="isPrimary" column="is_primary" />
|
||||||
<property name="index" column="assoc_index" />
|
<property name="index" column="assoc_index" />
|
||||||
@@ -199,30 +171,32 @@
|
|||||||
<class
|
<class
|
||||||
name="org.alfresco.repo.domain.hibernate.NodeAssocImpl"
|
name="org.alfresco.repo.domain.hibernate.NodeAssocImpl"
|
||||||
proxy="org.alfresco.repo.domain.NodeAssoc"
|
proxy="org.alfresco.repo.domain.NodeAssoc"
|
||||||
table="node_assoc" >
|
table="alf_node_assoc" >
|
||||||
<!-- auto-generated ID -->
|
<!-- auto-generated ID -->
|
||||||
<id name="id" column="id" type="long" >
|
<id name="id" column="id" type="long" >
|
||||||
<generator class="native" />
|
<generator class="native" />
|
||||||
</id>
|
</id>
|
||||||
<!-- forward assoc to source node -->
|
<natural-id mutable="true">
|
||||||
<many-to-one
|
<!-- forward assoc to source node -->
|
||||||
name="source"
|
<many-to-one
|
||||||
class="org.alfresco.repo.domain.hibernate.NodeImpl"
|
name="source"
|
||||||
lazy="false"
|
class="org.alfresco.repo.domain.hibernate.NodeImpl"
|
||||||
fetch="join"
|
lazy="false"
|
||||||
not-null="true" >
|
fetch="join"
|
||||||
<column name="source_node_id" />
|
not-null="true" >
|
||||||
</many-to-one>
|
<column name="source_node_id" />
|
||||||
<!-- forward assoc to target node -->
|
</many-to-one>
|
||||||
<many-to-one
|
<!-- forward assoc to target node -->
|
||||||
name="target"
|
<many-to-one
|
||||||
class="org.alfresco.repo.domain.hibernate.NodeImpl"
|
name="target"
|
||||||
lazy="false"
|
class="org.alfresco.repo.domain.hibernate.NodeImpl"
|
||||||
fetch="join"
|
lazy="false"
|
||||||
not-null="true" >
|
fetch="join"
|
||||||
<column name="target_node_id" />
|
not-null="true" >
|
||||||
</many-to-one>
|
<column name="target_node_id" />
|
||||||
<property name="typeQName" column="type_qname" type="QName" length="255" not-null="true" />
|
</many-to-one>
|
||||||
|
<property name="typeQName" column="type_qname" type="QName" length="255" not-null="true" />
|
||||||
|
</natural-id>
|
||||||
</class>
|
</class>
|
||||||
|
|
||||||
<query name="store.GetAllStores">
|
<query name="store.GetAllStores">
|
||||||
@@ -232,39 +206,107 @@
|
|||||||
org.alfresco.repo.domain.hibernate.StoreImpl as store
|
org.alfresco.repo.domain.hibernate.StoreImpl as store
|
||||||
</query>
|
</query>
|
||||||
|
|
||||||
|
<query name="node.updateChildAssocName">
|
||||||
|
update
|
||||||
|
org.alfresco.repo.domain.hibernate.ChildAssocImpl assoc
|
||||||
|
set
|
||||||
|
assoc.childNodeName = :newName,
|
||||||
|
assoc.childNodeNameCrc = :newNameCrc
|
||||||
|
where
|
||||||
|
assoc.id = :childAssocId
|
||||||
|
</query>
|
||||||
|
|
||||||
|
<query name="node.GetChildAssocs">
|
||||||
|
select
|
||||||
|
assoc
|
||||||
|
from
|
||||||
|
org.alfresco.repo.domain.hibernate.ChildAssocImpl as assoc
|
||||||
|
where
|
||||||
|
assoc.parent.id = :parentId
|
||||||
|
order by
|
||||||
|
assoc.index,
|
||||||
|
assoc.id
|
||||||
|
</query>
|
||||||
|
|
||||||
|
<query name="node.GetChildAssocByTypeAndName">
|
||||||
|
select
|
||||||
|
assoc
|
||||||
|
from
|
||||||
|
org.alfresco.repo.domain.hibernate.ChildAssocImpl as assoc
|
||||||
|
where
|
||||||
|
assoc.parent.id = :parentId and
|
||||||
|
assoc.typeQName = :typeQName and
|
||||||
|
assoc.childNodeName = :childNodeName and
|
||||||
|
assoc.childNodeNameCrc = :childNodeNameCrc
|
||||||
|
order by
|
||||||
|
assoc.index,
|
||||||
|
assoc.id
|
||||||
|
</query>
|
||||||
|
|
||||||
|
<query name="node.GetChildAssocRefs">
|
||||||
|
select
|
||||||
|
assoc.typeQName,
|
||||||
|
assoc.qname,
|
||||||
|
assoc.isPrimary,
|
||||||
|
assoc.index,
|
||||||
|
child.id,
|
||||||
|
child.store.key.protocol,
|
||||||
|
child.store.key.identifier,
|
||||||
|
child.uuid as parentUuid
|
||||||
|
from
|
||||||
|
org.alfresco.repo.domain.hibernate.ChildAssocImpl as assoc
|
||||||
|
join assoc.parent as parent
|
||||||
|
join assoc.child as child
|
||||||
|
where
|
||||||
|
assoc.parent.id = :parentId
|
||||||
|
order by
|
||||||
|
assoc.index,
|
||||||
|
assoc.id
|
||||||
|
</query>
|
||||||
|
|
||||||
<query name="node.GetNodeAssoc">
|
<query name="node.GetNodeAssoc">
|
||||||
select
|
select
|
||||||
assoc
|
assoc
|
||||||
from
|
from
|
||||||
org.alfresco.repo.domain.hibernate.NodeAssocImpl as assoc
|
org.alfresco.repo.domain.hibernate.NodeAssocImpl as assoc
|
||||||
where
|
where
|
||||||
assoc.source = :sourceNode and
|
assoc.source.id = :sourceId and
|
||||||
assoc.target = :targetNode and
|
assoc.target.id = :targetId and
|
||||||
assoc.typeQName = :assocTypeQName
|
assoc.typeQName = :assocTypeQName
|
||||||
</query>
|
</query>
|
||||||
|
|
||||||
<query name="node.GetNodeAssocTargets">
|
<query name="node.GetNodeAssocsToAndFrom">
|
||||||
select
|
select
|
||||||
target
|
assoc
|
||||||
from
|
from
|
||||||
org.alfresco.repo.domain.hibernate.NodeAssocImpl as assoc
|
org.alfresco.repo.domain.hibernate.NodeAssocImpl as assoc
|
||||||
join assoc.target as target
|
|
||||||
where
|
where
|
||||||
assoc.source = :sourceNode and
|
assoc.source.id = :nodeId or
|
||||||
assoc.typeQName = :assocTypeQName
|
assoc.target.id = :nodeId
|
||||||
</query>
|
</query>
|
||||||
|
|
||||||
<query name="node.GetNodeAssocSources">
|
<query name="node.GetTargetAssocs">
|
||||||
select
|
select
|
||||||
source
|
assoc
|
||||||
from
|
from
|
||||||
org.alfresco.repo.domain.hibernate.NodeAssocImpl as assoc
|
org.alfresco.repo.domain.hibernate.NodeAssocImpl as assoc
|
||||||
join assoc.source as source
|
join assoc.source as source
|
||||||
|
join assoc.target as target
|
||||||
where
|
where
|
||||||
assoc.target = :targetNode and
|
assoc.source.id = :sourceId
|
||||||
assoc.typeQName = :assocTypeQName
|
|
||||||
</query>
|
</query>
|
||||||
|
|
||||||
|
<query name="node.GetSourceAssocs">
|
||||||
|
select
|
||||||
|
assoc
|
||||||
|
from
|
||||||
|
org.alfresco.repo.domain.hibernate.NodeAssocImpl as assoc
|
||||||
|
join assoc.source as source
|
||||||
|
join assoc.target as target
|
||||||
|
where
|
||||||
|
assoc.target.id = :targetId
|
||||||
|
</query>
|
||||||
|
|
||||||
<query name="node.GetNextChangeTxnIds">
|
<query name="node.GetNextChangeTxnIds">
|
||||||
select distinct
|
select distinct
|
||||||
status.changeTxnId
|
status.changeTxnId
|
||||||
@@ -333,4 +375,20 @@
|
|||||||
node.properties.multiValued = false
|
node.properties.multiValued = false
|
||||||
</query>
|
</query>
|
||||||
|
|
||||||
|
<query name="node.patch.GetAssocsAndChildNames">
|
||||||
|
<![CDATA[
|
||||||
|
select
|
||||||
|
assoc,
|
||||||
|
child
|
||||||
|
from
|
||||||
|
org.alfresco.repo.domain.hibernate.ChildAssocImpl as assoc
|
||||||
|
join assoc.child as child
|
||||||
|
where
|
||||||
|
assoc.id > :lastAssocId and
|
||||||
|
assoc.typeQName = :assocTypeQName
|
||||||
|
order by
|
||||||
|
assoc.id
|
||||||
|
]]>
|
||||||
|
</query>
|
||||||
|
|
||||||
</hibernate-mapping>
|
</hibernate-mapping>
|
||||||
|
@@ -57,17 +57,6 @@ public class NodeAssocImpl implements NodeAssoc, Serializable
|
|||||||
// add the forward associations
|
// add the forward associations
|
||||||
this.setTarget(targetNode);
|
this.setTarget(targetNode);
|
||||||
this.setSource(sourceNode);
|
this.setSource(sourceNode);
|
||||||
// add the inverse associations
|
|
||||||
sourceNode.getTargetNodeAssocs().add(this);
|
|
||||||
targetNode.getSourceNodeAssocs().add(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeAssociation()
|
|
||||||
{
|
|
||||||
// maintain inverse assoc from source node to this instance
|
|
||||||
this.getSource().getTargetNodeAssocs().remove(this);
|
|
||||||
// maintain inverse assoc from target node to this instance
|
|
||||||
this.getTarget().getSourceNodeAssocs().remove(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public AssociationRef getNodeAssocRef()
|
public AssociationRef getNodeAssocRef()
|
||||||
|
@@ -29,7 +29,6 @@ import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
|
|||||||
import org.alfresco.repo.domain.ChildAssoc;
|
import org.alfresco.repo.domain.ChildAssoc;
|
||||||
import org.alfresco.repo.domain.DbAccessControlList;
|
import org.alfresco.repo.domain.DbAccessControlList;
|
||||||
import org.alfresco.repo.domain.Node;
|
import org.alfresco.repo.domain.Node;
|
||||||
import org.alfresco.repo.domain.NodeAssoc;
|
|
||||||
import org.alfresco.repo.domain.PropertyValue;
|
import org.alfresco.repo.domain.PropertyValue;
|
||||||
import org.alfresco.repo.domain.Store;
|
import org.alfresco.repo.domain.Store;
|
||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
@@ -52,12 +51,8 @@ public class NodeImpl extends LifecycleAdapter implements Node, Serializable
|
|||||||
private Store store;
|
private Store store;
|
||||||
private String uuid;
|
private String uuid;
|
||||||
private QName typeQName;
|
private QName typeQName;
|
||||||
// private NodeStatus status;
|
|
||||||
private Set<QName> aspects;
|
private Set<QName> aspects;
|
||||||
private Collection<NodeAssoc> sourceNodeAssocs;
|
|
||||||
private Collection<NodeAssoc> targetNodeAssocs;
|
|
||||||
private Collection<ChildAssoc> parentAssocs;
|
private Collection<ChildAssoc> parentAssocs;
|
||||||
private Collection<ChildAssoc> childAssocs;
|
|
||||||
private Map<QName, PropertyValue> properties;
|
private Map<QName, PropertyValue> properties;
|
||||||
private DbAccessControlList accessControlList;
|
private DbAccessControlList accessControlList;
|
||||||
|
|
||||||
@@ -68,10 +63,7 @@ public class NodeImpl extends LifecycleAdapter implements Node, Serializable
|
|||||||
public NodeImpl()
|
public NodeImpl()
|
||||||
{
|
{
|
||||||
aspects = new HashSet<QName>(5);
|
aspects = new HashSet<QName>(5);
|
||||||
sourceNodeAssocs = new HashSet<NodeAssoc>(5);
|
|
||||||
targetNodeAssocs = new HashSet<NodeAssoc>(5);
|
|
||||||
parentAssocs = new HashSet<ChildAssoc>(5);
|
parentAssocs = new HashSet<ChildAssoc>(5);
|
||||||
childAssocs = new HashSet<ChildAssoc>(11);
|
|
||||||
properties = new HashMap<QName, PropertyValue>(5);
|
properties = new HashMap<QName, PropertyValue>(5);
|
||||||
|
|
||||||
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
||||||
@@ -227,16 +219,6 @@ public class NodeImpl extends LifecycleAdapter implements Node, Serializable
|
|||||||
this.typeQName = typeQName;
|
this.typeQName = typeQName;
|
||||||
}
|
}
|
||||||
|
|
||||||
// public NodeStatus getStatus()
|
|
||||||
// {
|
|
||||||
// return status;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public void setStatus(NodeStatus status)
|
|
||||||
// {
|
|
||||||
// this.status = status;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
public Set<QName> getAspects()
|
public Set<QName> getAspects()
|
||||||
{
|
{
|
||||||
return aspects;
|
return aspects;
|
||||||
@@ -251,34 +233,6 @@ public class NodeImpl extends LifecycleAdapter implements Node, Serializable
|
|||||||
this.aspects = aspects;
|
this.aspects = aspects;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<NodeAssoc> getSourceNodeAssocs()
|
|
||||||
{
|
|
||||||
return sourceNodeAssocs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For Hibernate use
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private void setSourceNodeAssocs(Collection<NodeAssoc> sourceNodeAssocs)
|
|
||||||
{
|
|
||||||
this.sourceNodeAssocs = sourceNodeAssocs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<NodeAssoc> getTargetNodeAssocs()
|
|
||||||
{
|
|
||||||
return targetNodeAssocs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For Hibernate use
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private void setTargetNodeAssocs(Collection<NodeAssoc> targetNodeAssocs)
|
|
||||||
{
|
|
||||||
this.targetNodeAssocs = targetNodeAssocs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<ChildAssoc> getParentAssocs()
|
public Collection<ChildAssoc> getParentAssocs()
|
||||||
{
|
{
|
||||||
return parentAssocs;
|
return parentAssocs;
|
||||||
@@ -293,20 +247,6 @@ public class NodeImpl extends LifecycleAdapter implements Node, Serializable
|
|||||||
this.parentAssocs = parentAssocs;
|
this.parentAssocs = parentAssocs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<ChildAssoc> getChildAssocs()
|
|
||||||
{
|
|
||||||
return childAssocs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For Hibernate use
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private void setChildAssocs(Collection<ChildAssoc> childAssocs)
|
|
||||||
{
|
|
||||||
this.childAssocs = childAssocs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<QName, PropertyValue> getProperties()
|
public Map<QName, PropertyValue> getProperties()
|
||||||
{
|
{
|
||||||
return properties;
|
return properties;
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
<class
|
<class
|
||||||
name="org.alfresco.repo.domain.hibernate.DbAccessControlListImpl"
|
name="org.alfresco.repo.domain.hibernate.DbAccessControlListImpl"
|
||||||
proxy="org.alfresco.repo.domain.DbAccessControlList"
|
proxy="org.alfresco.repo.domain.DbAccessControlList"
|
||||||
table="access_control_list"
|
table="alf_access_control_list"
|
||||||
dynamic-insert="false"
|
dynamic-insert="false"
|
||||||
dynamic-update="false"
|
dynamic-update="false"
|
||||||
select-before-update="false"
|
select-before-update="false"
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
<class
|
<class
|
||||||
name="org.alfresco.repo.domain.hibernate.DbAccessControlEntryImpl"
|
name="org.alfresco.repo.domain.hibernate.DbAccessControlEntryImpl"
|
||||||
proxy="org.alfresco.repo.domain.DbAccessControlEntry"
|
proxy="org.alfresco.repo.domain.DbAccessControlEntry"
|
||||||
table="access_control_entry"
|
table="alf_access_control_entry"
|
||||||
dynamic-insert="false"
|
dynamic-insert="false"
|
||||||
dynamic-update="false"
|
dynamic-update="false"
|
||||||
select-before-update="false"
|
select-before-update="false"
|
||||||
@@ -81,7 +81,7 @@
|
|||||||
<class
|
<class
|
||||||
name="org.alfresco.repo.domain.hibernate.DbPermissionImpl"
|
name="org.alfresco.repo.domain.hibernate.DbPermissionImpl"
|
||||||
proxy="org.alfresco.repo.domain.DbPermission"
|
proxy="org.alfresco.repo.domain.DbPermission"
|
||||||
table="permission"
|
table="alf_permission"
|
||||||
dynamic-insert="false"
|
dynamic-insert="false"
|
||||||
dynamic-update="false"
|
dynamic-update="false"
|
||||||
select-before-update="false"
|
select-before-update="false"
|
||||||
@@ -102,7 +102,7 @@
|
|||||||
<class
|
<class
|
||||||
name="org.alfresco.repo.domain.hibernate.DbAuthorityImpl"
|
name="org.alfresco.repo.domain.hibernate.DbAuthorityImpl"
|
||||||
proxy="org.alfresco.repo.domain.DbAuthority"
|
proxy="org.alfresco.repo.domain.DbAuthority"
|
||||||
table="authority"
|
table="alf_authority"
|
||||||
dynamic-insert="false"
|
dynamic-insert="false"
|
||||||
dynamic-update="false"
|
dynamic-update="false"
|
||||||
select-before-update="false"
|
select-before-update="false"
|
||||||
@@ -113,7 +113,7 @@
|
|||||||
|
|
||||||
<set
|
<set
|
||||||
name="externalKeys"
|
name="externalKeys"
|
||||||
table="auth_ext_keys"
|
table="alf_auth_ext_keys"
|
||||||
lazy="true"
|
lazy="true"
|
||||||
sort="unsorted"
|
sort="unsorted"
|
||||||
fetch="select"
|
fetch="select"
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
<class
|
<class
|
||||||
name="org.alfresco.repo.domain.hibernate.StoreImpl"
|
name="org.alfresco.repo.domain.hibernate.StoreImpl"
|
||||||
proxy="org.alfresco.repo.domain.Store"
|
proxy="org.alfresco.repo.domain.Store"
|
||||||
table="store"
|
table="alf_store"
|
||||||
dynamic-update="false"
|
dynamic-update="false"
|
||||||
dynamic-insert="false"
|
dynamic-insert="false"
|
||||||
select-before-update="false"
|
select-before-update="false"
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
<class
|
<class
|
||||||
name="org.alfresco.repo.domain.hibernate.VersionCountImpl"
|
name="org.alfresco.repo.domain.hibernate.VersionCountImpl"
|
||||||
proxy="org.alfresco.repo.domain.VersionCount"
|
proxy="org.alfresco.repo.domain.VersionCount"
|
||||||
table="version_count"
|
table="alf_version_count"
|
||||||
dynamic-update="false"
|
dynamic-update="false"
|
||||||
dynamic-insert="false"
|
dynamic-insert="false"
|
||||||
select-before-update="false"
|
select-before-update="false"
|
||||||
|
@@ -331,16 +331,16 @@ public class FileFolderPerformanceTester extends TestCase
|
|||||||
// 100,
|
// 100,
|
||||||
// new double[] {0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90});
|
// new double[] {0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90});
|
||||||
// }
|
// }
|
||||||
public void test_1_shuffled_10_400() throws Exception
|
// public void test_1_shuffled_10_400() throws Exception
|
||||||
{
|
// {
|
||||||
buildStructure(
|
// buildStructure(
|
||||||
rootFolderRef,
|
// rootFolderRef,
|
||||||
1,
|
// 1,
|
||||||
true,
|
// true,
|
||||||
10,
|
// 10,
|
||||||
400,
|
// 400,
|
||||||
new double[] {0.05, 0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90});
|
// new double[] {0.05, 0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90});
|
||||||
}
|
// }
|
||||||
public void test_4_shuffled_10_100() throws Exception
|
public void test_4_shuffled_10_100() throws Exception
|
||||||
{
|
{
|
||||||
buildStructure(
|
buildStructure(
|
||||||
@@ -351,4 +351,14 @@ public class FileFolderPerformanceTester extends TestCase
|
|||||||
100,
|
100,
|
||||||
new double[] {0.05, 0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90});
|
new double[] {0.05, 0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90});
|
||||||
}
|
}
|
||||||
|
// public void test_1_ordered_1_50000() throws Exception
|
||||||
|
// {
|
||||||
|
// buildStructure(
|
||||||
|
// rootFolderRef,
|
||||||
|
// 1,
|
||||||
|
// false,
|
||||||
|
// 1,
|
||||||
|
// 50000,
|
||||||
|
// new double[] {0.01, 0.02, 0.03, 0.04, 0.05, 0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90});
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
@@ -39,6 +39,7 @@ import org.alfresco.service.cmr.repository.ContentReader;
|
|||||||
import org.alfresco.service.cmr.repository.ContentService;
|
import org.alfresco.service.cmr.repository.ContentService;
|
||||||
import org.alfresco.service.cmr.repository.ContentWriter;
|
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||||
import org.alfresco.service.cmr.repository.CopyService;
|
import org.alfresco.service.cmr.repository.CopyService;
|
||||||
|
import org.alfresco.service.cmr.repository.DuplicateChildNodeNameException;
|
||||||
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
|
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
|
||||||
import org.alfresco.service.cmr.repository.MimetypeService;
|
import org.alfresco.service.cmr.repository.MimetypeService;
|
||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
@@ -205,27 +206,6 @@ public class FileFolderServiceImpl implements FileFolderService
|
|||||||
return fileInfo;
|
return fileInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensure that a file or folder with the given name does not already exist
|
|
||||||
*
|
|
||||||
* @throws FileExistsException if a same-named file or folder already exists
|
|
||||||
*/
|
|
||||||
private void checkExists(NodeRef parentFolderRef, String name)
|
|
||||||
throws FileExistsException
|
|
||||||
{
|
|
||||||
// the name is never a wildcard, so we can perform an exact search
|
|
||||||
List<NodeRef> nodeRefs = searchInternal(parentFolderRef, name, true, true, false);
|
|
||||||
if (nodeRefs.size() == 0)
|
|
||||||
{
|
|
||||||
// no match
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// we found a match, so raise the exception
|
|
||||||
NodeRef duplicateNodeRef = nodeRefs.get(0);
|
|
||||||
FileInfo duplicateFileInfo = toFileInfo(duplicateNodeRef);
|
|
||||||
throw new FileExistsException(duplicateFileInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception when the type is not a valid File or Folder type
|
* Exception when the type is not a valid File or Folder type
|
||||||
*
|
*
|
||||||
@@ -321,6 +301,20 @@ public class FileFolderServiceImpl implements FileFolderService
|
|||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public NodeRef searchSimple(NodeRef contextNodeRef, String name)
|
||||||
|
{
|
||||||
|
NodeRef childNodeRef = nodeService.getChildByName(contextNodeRef, ContentModel.ASSOC_CONTAINS, name);
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug(
|
||||||
|
"Simple name search results: \n" +
|
||||||
|
" parent: " + contextNodeRef + "\n" +
|
||||||
|
" name: " + name + "\n" +
|
||||||
|
" result: " + childNodeRef);
|
||||||
|
}
|
||||||
|
return childNodeRef;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see #search(NodeRef, String, boolean, boolean, boolean)
|
* @see #search(NodeRef, String, boolean, boolean, boolean)
|
||||||
@@ -378,7 +372,7 @@ public class FileFolderServiceImpl implements FileFolderService
|
|||||||
* file info objects. This allows {@link #checkExists(NodeRef, String)} to
|
* file info objects. This allows {@link #checkExists(NodeRef, String)} to
|
||||||
* bypass the retrieval of node properties.
|
* bypass the retrieval of node properties.
|
||||||
*/
|
*/
|
||||||
public List<NodeRef> searchInternal(
|
private List<NodeRef> searchInternal(
|
||||||
NodeRef contextNodeRef,
|
NodeRef contextNodeRef,
|
||||||
String namePattern,
|
String namePattern,
|
||||||
boolean fileSearch,
|
boolean fileSearch,
|
||||||
@@ -547,7 +541,6 @@ public class FileFolderServiceImpl implements FileFolderService
|
|||||||
}
|
}
|
||||||
|
|
||||||
// there is nothing to do if both the name and parent folder haven't changed
|
// there is nothing to do if both the name and parent folder haven't changed
|
||||||
boolean checkExists = true;
|
|
||||||
if (targetParentRef.equals(assocRef.getParentRef()))
|
if (targetParentRef.equals(assocRef.getParentRef()))
|
||||||
{
|
{
|
||||||
if (newName.equals(beforeFileInfo.getName()))
|
if (newName.equals(beforeFileInfo.getName()))
|
||||||
@@ -563,16 +556,9 @@ public class FileFolderServiceImpl implements FileFolderService
|
|||||||
}
|
}
|
||||||
else if (newName.equalsIgnoreCase(beforeFileInfo.getName()))
|
else if (newName.equalsIgnoreCase(beforeFileInfo.getName()))
|
||||||
{
|
{
|
||||||
checkExists = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for existing file or folder
|
|
||||||
if (checkExists)
|
|
||||||
{
|
|
||||||
checkExists(targetParentRef, newName);
|
|
||||||
}
|
|
||||||
|
|
||||||
QName qname = QName.createQName(
|
QName qname = QName.createQName(
|
||||||
NamespaceService.CONTENT_MODEL_1_0_URI,
|
NamespaceService.CONTENT_MODEL_1_0_URI,
|
||||||
QName.createValidLocalName(newName));
|
QName.createValidLocalName(newName));
|
||||||
@@ -613,8 +599,15 @@ public class FileFolderServiceImpl implements FileFolderService
|
|||||||
String currentName = (String)nodeService.getProperty(targetNodeRef, ContentModel.PROP_NAME);
|
String currentName = (String)nodeService.getProperty(targetNodeRef, ContentModel.PROP_NAME);
|
||||||
if (currentName.equals(newName) == false)
|
if (currentName.equals(newName) == false)
|
||||||
{
|
{
|
||||||
// changed the name property
|
try
|
||||||
nodeService.setProperty(targetNodeRef, ContentModel.PROP_NAME, newName);
|
{
|
||||||
|
// changed the name property
|
||||||
|
nodeService.setProperty(targetNodeRef, ContentModel.PROP_NAME, newName);
|
||||||
|
}
|
||||||
|
catch (DuplicateChildNodeNameException e)
|
||||||
|
{
|
||||||
|
throw new FileExistsException(targetParentRef, newName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the details after the operation
|
// get the details after the operation
|
||||||
@@ -658,9 +651,6 @@ public class FileFolderServiceImpl implements FileFolderService
|
|||||||
throw new AlfrescoRuntimeException("The type is not supported by this service: " + typeQName);
|
throw new AlfrescoRuntimeException("The type is not supported by this service: " + typeQName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for existing file or folder
|
|
||||||
checkExists(parentNodeRef, name);
|
|
||||||
|
|
||||||
// set up initial properties
|
// set up initial properties
|
||||||
Map<QName, Serializable> properties = new HashMap<QName, Serializable>(11);
|
Map<QName, Serializable> properties = new HashMap<QName, Serializable>(11);
|
||||||
properties.put(ContentModel.PROP_NAME, (Serializable) name);
|
properties.put(ContentModel.PROP_NAME, (Serializable) name);
|
||||||
@@ -676,12 +666,21 @@ public class FileFolderServiceImpl implements FileFolderService
|
|||||||
QName qname = QName.createQName(
|
QName qname = QName.createQName(
|
||||||
NamespaceService.CONTENT_MODEL_1_0_URI,
|
NamespaceService.CONTENT_MODEL_1_0_URI,
|
||||||
QName.createValidLocalName(name));
|
QName.createValidLocalName(name));
|
||||||
ChildAssociationRef assocRef = nodeService.createNode(
|
ChildAssociationRef assocRef = null;
|
||||||
parentNodeRef,
|
try
|
||||||
ContentModel.ASSOC_CONTAINS,
|
{
|
||||||
qname,
|
assocRef = nodeService.createNode(
|
||||||
typeQName,
|
parentNodeRef,
|
||||||
properties);
|
ContentModel.ASSOC_CONTAINS,
|
||||||
|
qname,
|
||||||
|
typeQName,
|
||||||
|
properties);
|
||||||
|
}
|
||||||
|
catch (DuplicateChildNodeNameException e)
|
||||||
|
{
|
||||||
|
throw new FileExistsException(parentNodeRef, name);
|
||||||
|
}
|
||||||
|
|
||||||
NodeRef nodeRef = assocRef.getChildRef();
|
NodeRef nodeRef = assocRef.getChildRef();
|
||||||
FileInfo fileInfo = toFileInfo(nodeRef);
|
FileInfo fileInfo = toFileInfo(nodeRef);
|
||||||
// done
|
// done
|
||||||
@@ -716,31 +715,25 @@ public class FileFolderServiceImpl implements FileFolderService
|
|||||||
|
|
||||||
NodeRef currentParentRef = parentNodeRef;
|
NodeRef currentParentRef = parentNodeRef;
|
||||||
// just loop and create if necessary
|
// just loop and create if necessary
|
||||||
FileInfo lastFileInfo = null;
|
|
||||||
for (String pathElement : pathElements)
|
for (String pathElement : pathElements)
|
||||||
{
|
{
|
||||||
try
|
// does it exist?
|
||||||
|
NodeRef nodeRef = searchSimple(currentParentRef, pathElement);
|
||||||
|
if (nodeRef == null)
|
||||||
{
|
{
|
||||||
// not present - make it
|
// not present - make it
|
||||||
FileInfo createdFileInfo = create(currentParentRef, pathElement, folderTypeQName);
|
FileInfo createdFileInfo = create(currentParentRef, pathElement, folderTypeQName);
|
||||||
currentParentRef = createdFileInfo.getNodeRef();
|
currentParentRef = createdFileInfo.getNodeRef();
|
||||||
lastFileInfo = createdFileInfo;
|
|
||||||
}
|
}
|
||||||
catch (FileExistsException e)
|
else
|
||||||
{
|
{
|
||||||
// it exists - just get it
|
// it exists
|
||||||
List<FileInfo> fileInfos = search(currentParentRef, pathElement, false, true, false);
|
currentParentRef = nodeRef;
|
||||||
if (fileInfos.size() == 0)
|
|
||||||
{
|
|
||||||
// ? It must have been removed
|
|
||||||
throw new AlfrescoRuntimeException("Path element has just been removed: " + pathElement);
|
|
||||||
}
|
|
||||||
currentParentRef = fileInfos.get(0).getNodeRef();
|
|
||||||
lastFileInfo = fileInfos.get(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// done
|
// done
|
||||||
return lastFileInfo;
|
FileInfo fileInfo = toFileInfo(currentParentRef);
|
||||||
|
return fileInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FileInfo> getNamePath(NodeRef rootNodeRef, NodeRef nodeRef) throws FileNotFoundException
|
public List<FileInfo> getNamePath(NodeRef rootNodeRef, NodeRef nodeRef) throws FileNotFoundException
|
||||||
|
@@ -66,7 +66,8 @@ public class FileFolderServiceImplTest extends TestCase
|
|||||||
private static final String NAME_L1_FILE_A = "L1- File A";
|
private static final String NAME_L1_FILE_A = "L1- File A";
|
||||||
private static final String NAME_L1_FILE_B = "L1- File B";
|
private static final String NAME_L1_FILE_B = "L1- File B";
|
||||||
private static final String NAME_L1_FILE_C = "L1- File C (%_)";
|
private static final String NAME_L1_FILE_C = "L1- File C (%_)";
|
||||||
private static final String NAME_DUPLICATE = "DUPLICATE";
|
private static final String NAME_CHECK_FILE = "CHECK_FILE";
|
||||||
|
private static final String NAME_CHECK_FOLDER = "CHECK_FOLDER";
|
||||||
|
|
||||||
private static final ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
|
private static final ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
|
||||||
|
|
||||||
@@ -258,11 +259,11 @@ public class FileFolderServiceImplTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public void testGetByName() throws Exception
|
public void testGetByName() throws Exception
|
||||||
{
|
{
|
||||||
FileInfo fileInfo = getByName(NAME_DUPLICATE, true);
|
FileInfo fileInfo = getByName(NAME_CHECK_FOLDER, true);
|
||||||
assertNotNull(fileInfo);
|
assertNotNull(fileInfo);
|
||||||
assertTrue(fileInfo.isFolder());
|
assertTrue(fileInfo.isFolder());
|
||||||
|
|
||||||
fileInfo = getByName(NAME_DUPLICATE, false);
|
fileInfo = getByName(NAME_CHECK_FILE, false);
|
||||||
assertNotNull(fileInfo);
|
assertNotNull(fileInfo);
|
||||||
assertFalse(fileInfo.isFolder());
|
assertFalse(fileInfo.isFolder());
|
||||||
}
|
}
|
||||||
@@ -308,6 +309,10 @@ public class FileFolderServiceImplTest extends TestCase
|
|||||||
// make sure that it is an immediate child of the root
|
// make sure that it is an immediate child of the root
|
||||||
List<FileInfo> checkFileInfos = fileFolderService.search(workingRootNodeRef, NAME_L1_FOLDER_A, false);
|
List<FileInfo> checkFileInfos = fileFolderService.search(workingRootNodeRef, NAME_L1_FOLDER_A, false);
|
||||||
assertEquals("Folder not moved to root", 1, checkFileInfos.size());
|
assertEquals("Folder not moved to root", 1, checkFileInfos.size());
|
||||||
|
// rename properly
|
||||||
|
FileInfo checkFileInfo = fileFolderService.move(folderToMoveRef, null, "new name");
|
||||||
|
checkFileInfos = fileFolderService.search(workingRootNodeRef, checkFileInfo.getName(), false);
|
||||||
|
assertEquals("Folder not renamed in root", 1, checkFileInfos.size());
|
||||||
// attempt illegal rename (existing)
|
// attempt illegal rename (existing)
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -318,10 +323,6 @@ public class FileFolderServiceImplTest extends TestCase
|
|||||||
{
|
{
|
||||||
// expected
|
// expected
|
||||||
}
|
}
|
||||||
// rename properly
|
|
||||||
FileInfo checkFileInfo = fileFolderService.move(folderToMoveRef, null, "new name");
|
|
||||||
checkFileInfos = fileFolderService.search(workingRootNodeRef, checkFileInfo.getName(), false);
|
|
||||||
assertEquals("Folder not renamed in root", 1, checkFileInfos.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testCopy() throws Exception
|
public void testCopy() throws Exception
|
||||||
@@ -495,6 +496,22 @@ public class FileFolderServiceImplTest extends TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testSearchSimple() throws Exception
|
||||||
|
{
|
||||||
|
FileInfo folderInfo = getByName(NAME_L0_FOLDER_A, true);
|
||||||
|
assertNotNull(folderInfo);
|
||||||
|
NodeRef folderNodeRef = folderInfo.getNodeRef();
|
||||||
|
// search for a file that is not there
|
||||||
|
NodeRef phantomNodeRef = fileFolderService.searchSimple(folderNodeRef, "aaaaaaa");
|
||||||
|
assertNull("Found non-existent node by name", phantomNodeRef);
|
||||||
|
// search for a file that is there
|
||||||
|
NodeRef fileNodeRef = fileFolderService.searchSimple(folderNodeRef, NAME_L1_FILE_A);
|
||||||
|
assertNotNull("Didn't find file", fileNodeRef);
|
||||||
|
// double check
|
||||||
|
FileInfo checkInfo = getByName(NAME_L1_FILE_A, false);
|
||||||
|
assertEquals("Incorrect node found", checkInfo.getNodeRef(), fileNodeRef);
|
||||||
|
}
|
||||||
|
|
||||||
public void testResolveNamePath() throws Exception
|
public void testResolveNamePath() throws Exception
|
||||||
{
|
{
|
||||||
FileInfo fileInfo = getByName(NAME_L1_FILE_A, false);
|
FileInfo fileInfo = getByName(NAME_L1_FILE_A, false);
|
||||||
@@ -546,4 +563,20 @@ public class FileFolderServiceImplTest extends TestCase
|
|||||||
String checkContent = reader.getContentString();
|
String checkContent = reader.getContentString();
|
||||||
assertEquals("Content mismatch", content, checkContent);
|
assertEquals("Content mismatch", content, checkContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testLongFileNames() throws Exception
|
||||||
|
{
|
||||||
|
String fileName =
|
||||||
|
"12345678901234567890123456789012345678901234567890" +
|
||||||
|
"12345678901234567890123456789012345678901234567890" +
|
||||||
|
"12345678901234567890123456789012345678901234567890" +
|
||||||
|
"12345678901234567890123456789012345678901234567890" +
|
||||||
|
"12345678901234567890123456789012345678901234567890" +
|
||||||
|
"12345678901234567890123456789012345678901234567890";
|
||||||
|
FileInfo fileInfo = fileFolderService.create(workingRootNodeRef, fileName, ContentModel.TYPE_CONTENT);
|
||||||
|
// see if we can get it again
|
||||||
|
NodeRef fileNodeRef = fileFolderService.searchSimple(workingRootNodeRef, fileName);
|
||||||
|
assertNotNull("Long filename not found", fileNodeRef);
|
||||||
|
assertEquals(fileInfo.getNodeRef(), fileNodeRef);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -32,6 +32,7 @@ import org.alfresco.repo.content.MimetypeMap;
|
|||||||
import org.alfresco.repo.dictionary.DictionaryComponent;
|
import org.alfresco.repo.dictionary.DictionaryComponent;
|
||||||
import org.alfresco.repo.dictionary.DictionaryDAO;
|
import org.alfresco.repo.dictionary.DictionaryDAO;
|
||||||
import org.alfresco.repo.dictionary.M2Model;
|
import org.alfresco.repo.dictionary.M2Model;
|
||||||
|
import org.alfresco.repo.domain.hibernate.ChildAssocImpl;
|
||||||
import org.alfresco.repo.domain.hibernate.NodeImpl;
|
import org.alfresco.repo.domain.hibernate.NodeImpl;
|
||||||
import org.alfresco.repo.node.db.NodeDaoService;
|
import org.alfresco.repo.node.db.NodeDaoService;
|
||||||
import org.alfresco.repo.policy.JavaBehaviour;
|
import org.alfresco.repo.policy.JavaBehaviour;
|
||||||
@@ -47,6 +48,7 @@ import org.alfresco.service.cmr.repository.AssociationRef;
|
|||||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||||
import org.alfresco.service.cmr.repository.ContentData;
|
import org.alfresco.service.cmr.repository.ContentData;
|
||||||
import org.alfresco.service.cmr.repository.CyclicChildRelationshipException;
|
import org.alfresco.service.cmr.repository.CyclicChildRelationshipException;
|
||||||
|
import org.alfresco.service.cmr.repository.DuplicateChildNodeNameException;
|
||||||
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
|
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
|
||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
import org.alfresco.service.cmr.repository.NodeService;
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
@@ -107,6 +109,7 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
|
|||||||
public static final QName PROP_QNAME_PROP1 = QName.createQName(NAMESPACE, "prop1");
|
public static final QName PROP_QNAME_PROP1 = QName.createQName(NAMESPACE, "prop1");
|
||||||
public static final QName PROP_QNAME_PROP2 = QName.createQName(NAMESPACE, "prop2");
|
public static final QName PROP_QNAME_PROP2 = QName.createQName(NAMESPACE, "prop2");
|
||||||
public static final QName ASSOC_TYPE_QNAME_TEST_CHILDREN = ContentModel.ASSOC_CHILDREN;
|
public static final QName ASSOC_TYPE_QNAME_TEST_CHILDREN = ContentModel.ASSOC_CHILDREN;
|
||||||
|
public static final QName ASSOC_TYPE_QNAME_TEST_CONTAINS = ContentModel.ASSOC_CONTAINS;
|
||||||
public static final QName ASSOC_TYPE_QNAME_TEST_NEXT = QName.createQName(NAMESPACE, "next");
|
public static final QName ASSOC_TYPE_QNAME_TEST_NEXT = QName.createQName(NAMESPACE, "next");
|
||||||
|
|
||||||
public static final QName TYPE_QNAME_TEST_MULTIPLE_TESTER = QName.createQName(NAMESPACE, "multiple-tester");
|
public static final QName TYPE_QNAME_TEST_MULTIPLE_TESTER = QName.createQName(NAMESPACE, "multiple-tester");
|
||||||
@@ -730,9 +733,10 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
|
|||||||
private int countChildrenOfNode(NodeRef nodeRef)
|
private int countChildrenOfNode(NodeRef nodeRef)
|
||||||
{
|
{
|
||||||
String query =
|
String query =
|
||||||
"select node.childAssocs" +
|
"select childAssoc" +
|
||||||
" from " +
|
" from " +
|
||||||
NodeImpl.class.getName() + " node" +
|
ChildAssocImpl.class.getName() + " childAssoc" +
|
||||||
|
" join childAssoc.parent node" +
|
||||||
" where node.uuid = ? and node.store.key.protocol = ? and node.store.key.identifier = ?";
|
" where node.uuid = ? and node.store.key.protocol = ? and node.store.key.identifier = ?";
|
||||||
Session session = getSession();
|
Session session = getSession();
|
||||||
List results = session.createQuery(query)
|
List results = session.createQuery(query)
|
||||||
@@ -761,17 +765,24 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
|
|||||||
|
|
||||||
public void testAddChild() throws Exception
|
public void testAddChild() throws Exception
|
||||||
{
|
{
|
||||||
NodeRef childNodeRef = nodeService.createNode(
|
NodeRef childNodeRef1 = nodeService.createNode(
|
||||||
rootNodeRef,
|
rootNodeRef,
|
||||||
ASSOC_TYPE_QNAME_TEST_CHILDREN,
|
ASSOC_TYPE_QNAME_TEST_CHILDREN,
|
||||||
QName.createQName("pathA"),
|
QName.createQName("C1"),
|
||||||
ContentModel.TYPE_CONTAINER).getChildRef();
|
ContentModel.TYPE_CONTAINER).getChildRef();
|
||||||
int countBefore = countChildrenOfNode(rootNodeRef);
|
int count = countChildrenOfNode(rootNodeRef);
|
||||||
assertEquals("Root children count incorrect", 1, countBefore);
|
assertEquals("Root children count incorrect", 1, count);
|
||||||
|
NodeRef childNodeRef2 = nodeService.createNode(
|
||||||
|
childNodeRef1,
|
||||||
|
ASSOC_TYPE_QNAME_TEST_CHILDREN,
|
||||||
|
QName.createQName("C2"),
|
||||||
|
ContentModel.TYPE_CONTAINER).getChildRef();
|
||||||
|
count = countChildrenOfNode(rootNodeRef);
|
||||||
|
assertEquals("Root children count incorrect", 1, count);
|
||||||
// associate the two nodes
|
// associate the two nodes
|
||||||
nodeService.addChild(
|
nodeService.addChild(
|
||||||
rootNodeRef,
|
rootNodeRef,
|
||||||
childNodeRef,
|
childNodeRef2,
|
||||||
ASSOC_TYPE_QNAME_TEST_CHILDREN,
|
ASSOC_TYPE_QNAME_TEST_CHILDREN,
|
||||||
QName.createQName("pathB"));
|
QName.createQName("pathB"));
|
||||||
// there should now be 2 child assocs on the root
|
// there should now be 2 child assocs on the root
|
||||||
@@ -782,7 +793,7 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
nodeService.addChild(
|
nodeService.addChild(
|
||||||
childNodeRef,
|
childNodeRef1,
|
||||||
rootNodeRef,
|
rootNodeRef,
|
||||||
ASSOC_TYPE_QNAME_TEST_CHILDREN,
|
ASSOC_TYPE_QNAME_TEST_CHILDREN,
|
||||||
QName.createQName("backToRoot"));
|
QName.createQName("backToRoot"));
|
||||||
@@ -807,10 +818,6 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
|
|||||||
QName.createQName("pathA"),
|
QName.createQName("pathA"),
|
||||||
ContentModel.TYPE_CONTAINER);
|
ContentModel.TYPE_CONTAINER);
|
||||||
NodeRef childARef = pathARef.getChildRef();
|
NodeRef childARef = pathARef.getChildRef();
|
||||||
ChildAssociationRef pathBRef = nodeService.addChild(
|
|
||||||
parentRef, childARef, ASSOC_TYPE_QNAME_TEST_CHILDREN, QName.createQName("pathB"));
|
|
||||||
ChildAssociationRef pathCRef = nodeService.addChild(
|
|
||||||
parentRef, childARef, ASSOC_TYPE_QNAME_TEST_CHILDREN, QName.createQName("pathC"));
|
|
||||||
AssociationRef pathDRef = nodeService.createAssociation(
|
AssociationRef pathDRef = nodeService.createAssociation(
|
||||||
parentRef, childARef, ASSOC_TYPE_QNAME_TEST_NEXT);
|
parentRef, childARef, ASSOC_TYPE_QNAME_TEST_NEXT);
|
||||||
// remove the child - this must cascade
|
// remove the child - this must cascade
|
||||||
@@ -826,24 +833,6 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
|
|||||||
0, nodeService.getTargetAssocs(parentRef, RegexQNamePattern.MATCH_ALL).size());
|
0, nodeService.getTargetAssocs(parentRef, RegexQNamePattern.MATCH_ALL).size());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAddAndRemoveChild() throws Exception
|
|
||||||
{
|
|
||||||
ChildAssociationRef pathARef = nodeService.createNode(
|
|
||||||
rootNodeRef,
|
|
||||||
ASSOC_TYPE_QNAME_TEST_CHILDREN,
|
|
||||||
QName.createQName("pathA"),
|
|
||||||
ContentModel.TYPE_CONTAINER);
|
|
||||||
NodeRef childRef = pathARef.getChildRef();
|
|
||||||
// make a duplication, but non-primary, child associaton
|
|
||||||
nodeService.addChild(
|
|
||||||
rootNodeRef,
|
|
||||||
pathARef.getChildRef(),
|
|
||||||
pathARef.getTypeQName(),
|
|
||||||
pathARef.getQName());
|
|
||||||
// now remove the association - it will cascade to the child
|
|
||||||
nodeService.removeChild(rootNodeRef, childRef);
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum TestEnum
|
public enum TestEnum
|
||||||
{
|
{
|
||||||
TEST_ONE,
|
TEST_ONE,
|
||||||
@@ -1521,4 +1510,137 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
|
|||||||
" total nodes: " + totalNodes + "\n" +
|
" total nodes: " + totalNodes + "\n" +
|
||||||
" total assocs: " + totalAssocs);
|
" total assocs: " + totalAssocs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that the duplicate child name is detected and thrown correctly
|
||||||
|
*/
|
||||||
|
public void testDuplicateCatch() throws Exception
|
||||||
|
{
|
||||||
|
NodeRef parentRef = nodeService.createNode(
|
||||||
|
rootNodeRef,
|
||||||
|
ASSOC_TYPE_QNAME_TEST_CHILDREN,
|
||||||
|
QName.createQName("parent_child"),
|
||||||
|
ContentModel.TYPE_CONTAINER).getChildRef();
|
||||||
|
ChildAssociationRef pathARef = nodeService.createNode(
|
||||||
|
parentRef,
|
||||||
|
ASSOC_TYPE_QNAME_TEST_CONTAINS,
|
||||||
|
QName.createQName("pathA"),
|
||||||
|
ContentModel.TYPE_CONTENT);
|
||||||
|
// no issue with this
|
||||||
|
ChildAssociationRef pathBRef = nodeService.createNode(
|
||||||
|
parentRef,
|
||||||
|
ASSOC_TYPE_QNAME_TEST_CONTAINS,
|
||||||
|
QName.createQName("pathB"),
|
||||||
|
ContentModel.TYPE_CONTENT);
|
||||||
|
AlfrescoTransactionSupport.flush();
|
||||||
|
// there should be no issue with a duplicate association names any more
|
||||||
|
ChildAssociationRef pathBDuplicateRef = nodeService.createNode(
|
||||||
|
parentRef,
|
||||||
|
ASSOC_TYPE_QNAME_TEST_CONTAINS,
|
||||||
|
QName.createQName("pathB"),
|
||||||
|
ContentModel.TYPE_CONTENT);
|
||||||
|
AlfrescoTransactionSupport.flush();
|
||||||
|
// Now create nodes with duplicate cm:name properties
|
||||||
|
Map<QName, Serializable> props = new HashMap<QName, Serializable>(5);
|
||||||
|
props.put(ContentModel.PROP_NAME, "ABC");
|
||||||
|
ChildAssociationRef pathAbcRef = nodeService.createNode(
|
||||||
|
parentRef,
|
||||||
|
ASSOC_TYPE_QNAME_TEST_CONTAINS,
|
||||||
|
QName.createQName("ABC"),
|
||||||
|
ContentModel.TYPE_CONTENT,
|
||||||
|
props);
|
||||||
|
AlfrescoTransactionSupport.flush();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// now check that the duplicate is detected with attention to being case-insensitive
|
||||||
|
props.put(ContentModel.PROP_NAME, "abc");
|
||||||
|
ChildAssociationRef pathAbcDuplicateRef = nodeService.createNode(
|
||||||
|
parentRef,
|
||||||
|
ASSOC_TYPE_QNAME_TEST_CONTAINS,
|
||||||
|
QName.createQName("ABC-duplicate"),
|
||||||
|
ContentModel.TYPE_CONTENT,
|
||||||
|
props);
|
||||||
|
fail("Failed to throw duplicate child name exception");
|
||||||
|
}
|
||||||
|
catch (DuplicateChildNodeNameException e)
|
||||||
|
{
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that the unique constraint doesn't break delete and create within the same
|
||||||
|
* transaction.
|
||||||
|
*/
|
||||||
|
public void testDeleteAndAddSameName() throws Exception
|
||||||
|
{
|
||||||
|
NodeRef parentRef = nodeService.createNode(
|
||||||
|
rootNodeRef,
|
||||||
|
ASSOC_TYPE_QNAME_TEST_CHILDREN,
|
||||||
|
QName.createQName("parent_child"),
|
||||||
|
ContentModel.TYPE_CONTAINER).getChildRef();
|
||||||
|
// create node ABC
|
||||||
|
Map<QName, Serializable> props = new HashMap<QName, Serializable>(5);
|
||||||
|
props.put(ContentModel.PROP_NAME, "ABC");
|
||||||
|
ChildAssociationRef pathAbcRef = nodeService.createNode(
|
||||||
|
parentRef,
|
||||||
|
ASSOC_TYPE_QNAME_TEST_CONTAINS,
|
||||||
|
QName.createQName("ABC"),
|
||||||
|
ContentModel.TYPE_CONTENT,
|
||||||
|
props);
|
||||||
|
NodeRef abcRef = pathAbcRef.getChildRef();
|
||||||
|
AlfrescoTransactionSupport.flush();
|
||||||
|
// delete ABC
|
||||||
|
nodeService.deleteNode(abcRef);
|
||||||
|
// create it again
|
||||||
|
pathAbcRef = nodeService.createNode(
|
||||||
|
parentRef,
|
||||||
|
ASSOC_TYPE_QNAME_TEST_CONTAINS,
|
||||||
|
QName.createQName("ABC"),
|
||||||
|
ContentModel.TYPE_CONTENT,
|
||||||
|
props);
|
||||||
|
// there should not be any failure when doing this in the same transaction
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGetByName() throws Exception
|
||||||
|
{
|
||||||
|
NodeRef parentRef = nodeService.createNode(
|
||||||
|
rootNodeRef,
|
||||||
|
ASSOC_TYPE_QNAME_TEST_CHILDREN,
|
||||||
|
QName.createQName("parent_child"),
|
||||||
|
ContentModel.TYPE_CONTAINER).getChildRef();
|
||||||
|
// create node ABC
|
||||||
|
Map<QName, Serializable> props = new HashMap<QName, Serializable>(5);
|
||||||
|
props.put(ContentModel.PROP_NAME, "ABC");
|
||||||
|
ChildAssociationRef pathAbcRef = nodeService.createNode(
|
||||||
|
parentRef,
|
||||||
|
ASSOC_TYPE_QNAME_TEST_CONTAINS,
|
||||||
|
QName.createQName("ABC"),
|
||||||
|
ContentModel.TYPE_CONTENT,
|
||||||
|
props);
|
||||||
|
NodeRef abcRef = pathAbcRef.getChildRef();
|
||||||
|
// create node DEF
|
||||||
|
props.put(ContentModel.PROP_NAME, "DEF");
|
||||||
|
ChildAssociationRef pathDefRef = nodeService.createNode(
|
||||||
|
abcRef,
|
||||||
|
ASSOC_TYPE_QNAME_TEST_CONTAINS,
|
||||||
|
QName.createQName("DEF"),
|
||||||
|
ContentModel.TYPE_CONTENT,
|
||||||
|
props);
|
||||||
|
NodeRef defRef = pathDefRef.getChildRef();
|
||||||
|
|
||||||
|
// now browse down using the node service
|
||||||
|
NodeRef checkParentRef = nodeService.getChildByName(rootNodeRef, ASSOC_TYPE_QNAME_TEST_CHILDREN, parentRef.getId());
|
||||||
|
assertNotNull("First level, non-named node not found", checkParentRef);
|
||||||
|
assertEquals(parentRef, checkParentRef);
|
||||||
|
NodeRef checkAbcRef = nodeService.getChildByName(checkParentRef, ASSOC_TYPE_QNAME_TEST_CONTAINS, "abc");
|
||||||
|
assertNotNull("Second level, named node 'ABC' not found", checkAbcRef);
|
||||||
|
assertEquals(abcRef, checkAbcRef);
|
||||||
|
NodeRef checkDefRef = nodeService.getChildByName(checkAbcRef, ASSOC_TYPE_QNAME_TEST_CONTAINS, "def");
|
||||||
|
assertNotNull("Third level, named node 'DEF' not found", checkDefRef);
|
||||||
|
assertEquals(defRef, checkDefRef);
|
||||||
|
// check that we get null where not present
|
||||||
|
NodeRef checkHijRef = nodeService.getChildByName(checkAbcRef, ASSOC_TYPE_QNAME_TEST_CONTAINS, "hij");
|
||||||
|
assertNull("Third level, named node 'HIJ' should not have been found", checkHijRef);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -23,6 +23,7 @@ import java.util.Collections;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -41,6 +42,8 @@ import org.alfresco.repo.node.StoreArchiveMap;
|
|||||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||||
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
||||||
import org.alfresco.service.cmr.dictionary.AspectDefinition;
|
import org.alfresco.service.cmr.dictionary.AspectDefinition;
|
||||||
|
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
|
||||||
|
import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition;
|
||||||
import org.alfresco.service.cmr.dictionary.ClassDefinition;
|
import org.alfresco.service.cmr.dictionary.ClassDefinition;
|
||||||
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
||||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||||
@@ -63,8 +66,10 @@ import org.alfresco.service.cmr.repository.NodeRef.Status;
|
|||||||
import org.alfresco.service.namespace.NamespaceService;
|
import org.alfresco.service.namespace.NamespaceService;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
import org.alfresco.service.namespace.QNamePattern;
|
import org.alfresco.service.namespace.QNamePattern;
|
||||||
|
import org.alfresco.util.ParameterCheck;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.springframework.dao.DataIntegrityViolationException;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -278,29 +283,35 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
String newId = generateGuid(properties);
|
String newId = generateGuid(properties);
|
||||||
|
|
||||||
// create the node instance
|
// create the node instance
|
||||||
Node node = nodeDaoService.newNode(store, newId, nodeTypeQName);
|
Node childNode = nodeDaoService.newNode(store, newId, nodeTypeQName);
|
||||||
|
|
||||||
// get the parent node
|
// get the parent node
|
||||||
Node parentNode = getNodeNotNull(parentRef);
|
Node parentNode = getNodeNotNull(parentRef);
|
||||||
|
|
||||||
// create the association - invoke policy behaviour
|
|
||||||
ChildAssoc childAssoc = nodeDaoService.newChildAssoc(parentNode, node, true, assocTypeQName, assocQName);
|
|
||||||
ChildAssociationRef childAssocRef = childAssoc.getChildAssocRef();
|
|
||||||
|
|
||||||
// Set the default property values
|
// Set the default property values
|
||||||
addDefaultPropertyValues(nodeTypeDef, properties);
|
addDefaultPropertyValues(nodeTypeDef, properties);
|
||||||
|
|
||||||
// Add the default aspects to the node
|
// Add the default aspects to the node
|
||||||
addDefaultAspects(nodeTypeDef, node, childAssocRef.getChildRef(), properties);
|
addDefaultAspects(nodeTypeDef, childNode, properties);
|
||||||
|
|
||||||
// set the properties - it is a new node so only set properties if there are any
|
// set the properties - it is a new node so only set properties if there are any
|
||||||
Map<QName, Serializable> propertiesBefore = getProperties(childAssocRef.getChildRef());
|
Map<QName, Serializable> propertiesBefore = getPropertiesImpl(childNode);
|
||||||
Map<QName, Serializable> propertiesAfter = null;
|
Map<QName, Serializable> propertiesAfter = null;
|
||||||
if (properties.size() > 0)
|
if (properties.size() > 0)
|
||||||
{
|
{
|
||||||
propertiesAfter = setPropertiesImpl(childAssocRef.getChildRef(), properties);
|
propertiesAfter = setPropertiesImpl(childNode, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create the association
|
||||||
|
ChildAssoc childAssoc = nodeDaoService.newChildAssoc(
|
||||||
|
parentNode,
|
||||||
|
childNode,
|
||||||
|
true,
|
||||||
|
assocTypeQName,
|
||||||
|
assocQName);
|
||||||
|
setChildUniqueName(childNode); // ensure uniqueness
|
||||||
|
ChildAssociationRef childAssocRef = childAssoc.getChildAssocRef();
|
||||||
|
|
||||||
// Invoke policy behaviour
|
// Invoke policy behaviour
|
||||||
invokeOnCreateNode(childAssocRef);
|
invokeOnCreateNode(childAssocRef);
|
||||||
invokeOnUpdateNode(parentRef);
|
invokeOnUpdateNode(parentRef);
|
||||||
@@ -318,8 +329,10 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
*
|
*
|
||||||
* @param nodeTypeDef
|
* @param nodeTypeDef
|
||||||
*/
|
*/
|
||||||
private void addDefaultAspects(ClassDefinition classDefinition, Node node, NodeRef nodeRef, Map<QName, Serializable> properties)
|
private void addDefaultAspects(ClassDefinition classDefinition, Node node, Map<QName, Serializable> properties)
|
||||||
{
|
{
|
||||||
|
NodeRef nodeRef = node.getNodeRef();
|
||||||
|
|
||||||
// get the mandatory aspects for the node type
|
// get the mandatory aspects for the node type
|
||||||
List<AspectDefinition> defaultAspectDefs = classDefinition.getDefaultAspects();
|
List<AspectDefinition> defaultAspectDefs = classDefinition.getDefaultAspects();
|
||||||
|
|
||||||
@@ -333,7 +346,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
invokeOnAddAspect(nodeRef, defaultAspectDef.getName());
|
invokeOnAddAspect(nodeRef, defaultAspectDef.getName());
|
||||||
|
|
||||||
// Now add any default aspects for this aspect
|
// Now add any default aspects for this aspect
|
||||||
addDefaultAspects(defaultAspectDef, node, nodeRef, properties);
|
addDefaultAspects(defaultAspectDef, node, properties);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -432,8 +445,15 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
// remove the child assoc from the old parent
|
// remove the child assoc from the old parent
|
||||||
// don't cascade as we will still need the node afterwards
|
// don't cascade as we will still need the node afterwards
|
||||||
nodeDaoService.deleteChildAssoc(oldAssoc, false);
|
nodeDaoService.deleteChildAssoc(oldAssoc, false);
|
||||||
|
|
||||||
// create a new assoc
|
// create a new assoc
|
||||||
ChildAssoc newAssoc = nodeDaoService.newChildAssoc(newParentNode, nodeToMove, true, assocTypeQName, assocQName);
|
ChildAssoc newAssoc = nodeDaoService.newChildAssoc(
|
||||||
|
newParentNode,
|
||||||
|
nodeToMove,
|
||||||
|
true,
|
||||||
|
assocTypeQName,
|
||||||
|
assocQName);
|
||||||
|
setChildUniqueName(nodeToMove); // ensure uniqueness
|
||||||
ChildAssociationRef newAssocRef = newAssoc.getChildAssocRef();
|
ChildAssociationRef newAssocRef = newAssoc.getChildAssocRef();
|
||||||
|
|
||||||
// If the node is moving stores, then drag the node hierarchy with it
|
// If the node is moving stores, then drag the node hierarchy with it
|
||||||
@@ -521,8 +541,8 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
node.setTypeQName(typeQName);
|
node.setTypeQName(typeQName);
|
||||||
|
|
||||||
// Add the default aspects to the node (update the properties with any new default values)
|
// Add the default aspects to the node (update the properties with any new default values)
|
||||||
Map<QName, Serializable> properties = this.getProperties(nodeRef);
|
Map<QName, Serializable> properties = this.getPropertiesImpl(node);
|
||||||
addDefaultAspects(nodeTypeDef, node, nodeRef, properties);
|
addDefaultAspects(nodeTypeDef, node, properties);
|
||||||
this.setProperties(nodeRef, properties);
|
this.setProperties(nodeRef, properties);
|
||||||
|
|
||||||
// Invoke policies
|
// Invoke policies
|
||||||
@@ -552,7 +572,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
Node node = getNodeNotNull(nodeRef);
|
Node node = getNodeNotNull(nodeRef);
|
||||||
|
|
||||||
// attach the properties to the current node properties
|
// attach the properties to the current node properties
|
||||||
Map<QName, Serializable> nodeProperties = getProperties(nodeRef);
|
Map<QName, Serializable> nodeProperties = getPropertiesImpl(node);
|
||||||
|
|
||||||
if (aspectProperties != null)
|
if (aspectProperties != null)
|
||||||
{
|
{
|
||||||
@@ -563,7 +583,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
addDefaultPropertyValues(aspectDef, nodeProperties);
|
addDefaultPropertyValues(aspectDef, nodeProperties);
|
||||||
|
|
||||||
// Add any dependant aspect
|
// Add any dependant aspect
|
||||||
addDefaultAspects(aspectDef, node, nodeRef, nodeProperties);
|
addDefaultAspects(aspectDef, node, nodeProperties);
|
||||||
|
|
||||||
// Set the property values back on the node
|
// Set the property values back on the node
|
||||||
setProperties(nodeRef, nodeProperties);
|
setProperties(nodeRef, nodeProperties);
|
||||||
@@ -705,21 +725,19 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
invokeBeforeUpdateNode(parentRef);
|
invokeBeforeUpdateNode(parentRef);
|
||||||
invokeBeforeCreateChildAssociation(parentRef, childRef, assocTypeQName, assocQName);
|
invokeBeforeCreateChildAssociation(parentRef, childRef, assocTypeQName, assocQName);
|
||||||
|
|
||||||
// check that both nodes belong to the same store
|
|
||||||
if (!parentRef.getStoreRef().equals(childRef.getStoreRef()))
|
|
||||||
{
|
|
||||||
throw new InvalidNodeRefException("Parent and child nodes must belong to the same store: \n" +
|
|
||||||
" parent: " + parentRef + "\n" +
|
|
||||||
" child: " + childRef,
|
|
||||||
childRef);
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the parent node and ensure that it is a container node
|
// get the parent node and ensure that it is a container node
|
||||||
Node parentNode = getNodeNotNull(parentRef);
|
Node parentNode = getNodeNotNull(parentRef);
|
||||||
// get the child node
|
// get the child node
|
||||||
Node childNode = getNodeNotNull(childRef);
|
Node childNode = getNodeNotNull(childRef);
|
||||||
// make the association
|
// make the association
|
||||||
ChildAssoc assoc = nodeDaoService.newChildAssoc(parentNode, childNode, false, assocTypeQName, assocQName);
|
ChildAssoc assoc = nodeDaoService.newChildAssoc(
|
||||||
|
parentNode,
|
||||||
|
childNode,
|
||||||
|
false,
|
||||||
|
assocTypeQName,
|
||||||
|
assocQName);
|
||||||
|
// ensure name uniqueness
|
||||||
|
setChildUniqueName(childNode);
|
||||||
ChildAssociationRef assocRef = assoc.getChildAssocRef();
|
ChildAssociationRef assocRef = assoc.getChildAssocRef();
|
||||||
NodeRef childNodeRef = assocRef.getChildRef();
|
NodeRef childNodeRef = assocRef.getChildRef();
|
||||||
|
|
||||||
@@ -742,7 +760,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
|
|
||||||
// get all the child assocs
|
// get all the child assocs
|
||||||
ChildAssociationRef primaryAssocRef = null;
|
ChildAssociationRef primaryAssocRef = null;
|
||||||
Collection<ChildAssoc> assocs = parentNode.getChildAssocs();
|
Collection<ChildAssoc> assocs = nodeDaoService.getChildAssocs(parentNode);
|
||||||
assocs = new HashSet<ChildAssoc>(assocs); // copy set as we will be modifying it
|
assocs = new HashSet<ChildAssoc>(assocs); // copy set as we will be modifying it
|
||||||
for (ChildAssoc assoc : assocs)
|
for (ChildAssoc assoc : assocs)
|
||||||
{
|
{
|
||||||
@@ -780,6 +798,13 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
public Map<QName, Serializable> getProperties(NodeRef nodeRef) throws InvalidNodeRefException
|
public Map<QName, Serializable> getProperties(NodeRef nodeRef) throws InvalidNodeRefException
|
||||||
{
|
{
|
||||||
Node node = getNodeNotNull(nodeRef);
|
Node node = getNodeNotNull(nodeRef);
|
||||||
|
return getPropertiesImpl(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<QName, Serializable> getPropertiesImpl(Node node) throws InvalidNodeRefException
|
||||||
|
{
|
||||||
|
NodeRef nodeRef = node.getNodeRef();
|
||||||
|
|
||||||
Map<QName, PropertyValue> nodeProperties = node.getProperties();
|
Map<QName, PropertyValue> nodeProperties = node.getProperties();
|
||||||
Map<QName, Serializable> ret = new HashMap<QName, Serializable>(nodeProperties.size());
|
Map<QName, Serializable> ret = new HashMap<QName, Serializable>(nodeProperties.size());
|
||||||
// copy values
|
// copy values
|
||||||
@@ -849,12 +874,16 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
*/
|
*/
|
||||||
public void setProperties(NodeRef nodeRef, Map<QName, Serializable> properties) throws InvalidNodeRefException
|
public void setProperties(NodeRef nodeRef, Map<QName, Serializable> properties) throws InvalidNodeRefException
|
||||||
{
|
{
|
||||||
// Invoke policy behaviours
|
Node node = getNodeNotNull(nodeRef);
|
||||||
|
|
||||||
|
// Invoke policy behaviours
|
||||||
invokeBeforeUpdateNode(nodeRef);
|
invokeBeforeUpdateNode(nodeRef);
|
||||||
|
|
||||||
// Do the set properties
|
// Do the set properties
|
||||||
Map<QName, Serializable> propertiesBefore = getProperties(nodeRef);
|
Map<QName, Serializable> propertiesBefore = getPropertiesImpl(node);
|
||||||
Map<QName, Serializable> propertiesAfter = setPropertiesImpl(nodeRef, properties);
|
Map<QName, Serializable> propertiesAfter = setPropertiesImpl(node, properties);
|
||||||
|
|
||||||
|
setChildUniqueName(node); // ensure uniqueness
|
||||||
|
|
||||||
// Invoke policy behaviours
|
// Invoke policy behaviours
|
||||||
invokeOnUpdateNode(nodeRef);
|
invokeOnUpdateNode(nodeRef);
|
||||||
@@ -865,24 +894,18 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
* Does the work of setting the property values. Returns a map containing the state of the properties after the set
|
* Does the work of setting the property values. Returns a map containing the state of the properties after the set
|
||||||
* operation is complete.
|
* operation is complete.
|
||||||
*
|
*
|
||||||
* @param nodeRef the node reference
|
* @param node the node
|
||||||
* @param properties the map of property values
|
* @param properties the map of property values
|
||||||
* @return the map of property values after the set operation is complete
|
* @return the map of property values after the set operation is complete
|
||||||
* @throws InvalidNodeRefException
|
* @throws InvalidNodeRefException
|
||||||
*/
|
*/
|
||||||
private Map<QName, Serializable> setPropertiesImpl(NodeRef nodeRef, Map<QName, Serializable> properties) throws InvalidNodeRefException
|
private Map<QName, Serializable> setPropertiesImpl(Node node, Map<QName, Serializable> properties) throws InvalidNodeRefException
|
||||||
{
|
{
|
||||||
if (properties == null)
|
ParameterCheck.mandatory("properties", properties);
|
||||||
{
|
|
||||||
throw new IllegalArgumentException("Properties may not be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove referencable properties
|
// remove referencable properties
|
||||||
removeReferencableProperties(properties);
|
removeReferencableProperties(properties);
|
||||||
|
|
||||||
// find the node
|
|
||||||
Node node = getNodeNotNull(nodeRef);
|
|
||||||
|
|
||||||
// copy properties onto node
|
// copy properties onto node
|
||||||
Map<QName, PropertyValue> nodeProperties = node.getProperties();
|
Map<QName, PropertyValue> nodeProperties = node.getProperties();
|
||||||
nodeProperties.clear();
|
nodeProperties.clear();
|
||||||
@@ -898,6 +921,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
// update the node status
|
// update the node status
|
||||||
|
NodeRef nodeRef = node.getNodeRef();
|
||||||
nodeDaoService.recordChangeId(nodeRef);
|
nodeDaoService.recordChangeId(nodeRef);
|
||||||
|
|
||||||
// Return the properties after
|
// Return the properties after
|
||||||
@@ -917,10 +941,18 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
// Invoke policy behaviours
|
// Invoke policy behaviours
|
||||||
invokeBeforeUpdateNode(nodeRef);
|
invokeBeforeUpdateNode(nodeRef);
|
||||||
|
|
||||||
// Do the set operation
|
// get the node
|
||||||
Map<QName, Serializable> propertiesBefore = getProperties(nodeRef);
|
Node node = getNodeNotNull(nodeRef);
|
||||||
Map<QName, Serializable> propertiesAfter = setPropertyImpl(nodeRef, qname, value);
|
|
||||||
|
|
||||||
|
// Do the set operation
|
||||||
|
Map<QName, Serializable> propertiesBefore = getPropertiesImpl(node);
|
||||||
|
Map<QName, Serializable> propertiesAfter = setPropertyImpl(node, qname, value);
|
||||||
|
|
||||||
|
if (qname.equals(ContentModel.PROP_NAME))
|
||||||
|
{
|
||||||
|
setChildUniqueName(node); // ensure uniqueness
|
||||||
|
}
|
||||||
|
|
||||||
// Invoke policy behaviours
|
// Invoke policy behaviours
|
||||||
invokeOnUpdateNode(nodeRef);
|
invokeOnUpdateNode(nodeRef);
|
||||||
invokeOnUpdateProperties(nodeRef, propertiesBefore, propertiesAfter);
|
invokeOnUpdateProperties(nodeRef, propertiesBefore, propertiesAfter);
|
||||||
@@ -930,16 +962,15 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
* Does the work of setting a property value. Returns the values of the properties after the set operation is
|
* Does the work of setting a property value. Returns the values of the properties after the set operation is
|
||||||
* complete.
|
* complete.
|
||||||
*
|
*
|
||||||
* @param nodeRef the node reference
|
* @param node the node
|
||||||
* @param qname the qname of the property
|
* @param qname the qname of the property
|
||||||
* @param value the value of the property
|
* @param value the value of the property
|
||||||
* @return the values of the properties after the set operation is complete
|
* @return the values of the properties after the set operation is complete
|
||||||
* @throws InvalidNodeRefException
|
* @throws InvalidNodeRefException
|
||||||
*/
|
*/
|
||||||
public Map<QName, Serializable> setPropertyImpl(NodeRef nodeRef, QName qname, Serializable value) throws InvalidNodeRefException
|
public Map<QName, Serializable> setPropertyImpl(Node node, QName qname, Serializable value) throws InvalidNodeRefException
|
||||||
{
|
{
|
||||||
// get the node
|
NodeRef nodeRef = node.getNodeRef();
|
||||||
Node node = getNodeNotNull(nodeRef);
|
|
||||||
|
|
||||||
Map<QName, PropertyValue> properties = node.getProperties();
|
Map<QName, PropertyValue> properties = node.getProperties();
|
||||||
PropertyDefinition propertyDef = dictionaryService.getProperty(qname);
|
PropertyDefinition propertyDef = dictionaryService.getProperty(qname);
|
||||||
@@ -950,7 +981,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
// update the node status
|
// update the node status
|
||||||
nodeDaoService.recordChangeId(nodeRef);
|
nodeDaoService.recordChangeId(nodeRef);
|
||||||
|
|
||||||
return getProperties(nodeRef);
|
return getPropertiesImpl(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1009,36 +1040,50 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
{
|
{
|
||||||
Node node = getNodeNotNull(nodeRef);
|
Node node = getNodeNotNull(nodeRef);
|
||||||
// get the assocs pointing from it
|
// get the assocs pointing from it
|
||||||
Collection<ChildAssoc> childAssocs = node.getChildAssocs();
|
Collection<ChildAssociationRef> childAssocRefs = nodeDaoService.getChildAssocRefs(node);
|
||||||
// shortcut if there are no assocs
|
// shortcut if there are no assocs
|
||||||
if (childAssocs.size() == 0)
|
if (childAssocRefs.size() == 0)
|
||||||
{
|
{
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
// sort results
|
// sort results
|
||||||
ArrayList<ChildAssoc> orderedList = new ArrayList<ChildAssoc>(childAssocs);
|
ArrayList<ChildAssociationRef> orderedList = new ArrayList<ChildAssociationRef>(childAssocRefs);
|
||||||
Collections.sort(orderedList);
|
Collections.sort(orderedList);
|
||||||
|
|
||||||
// list of results
|
// list of results
|
||||||
List<ChildAssociationRef> results = new ArrayList<ChildAssociationRef>(childAssocs.size());
|
|
||||||
int nthSibling = 0;
|
int nthSibling = 0;
|
||||||
for (ChildAssoc assoc : orderedList)
|
Iterator<ChildAssociationRef> iterator = orderedList.iterator();
|
||||||
|
while(iterator.hasNext())
|
||||||
{
|
{
|
||||||
|
ChildAssociationRef childAssocRef = iterator.next();
|
||||||
// does the qname match the pattern?
|
// does the qname match the pattern?
|
||||||
if (!qnamePattern.isMatch(assoc.getQname()) || !typeQNamePattern.isMatch(assoc.getTypeQName()))
|
if (!qnamePattern.isMatch(childAssocRef.getQName()) || !typeQNamePattern.isMatch(childAssocRef.getTypeQName()))
|
||||||
{
|
{
|
||||||
// no match - ignore
|
// no match - remove
|
||||||
continue;
|
iterator.remove();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
childAssocRef.setNthSibling(nthSibling);
|
||||||
|
nthSibling++;
|
||||||
}
|
}
|
||||||
ChildAssociationRef assocRef = assoc.getChildAssocRef();
|
|
||||||
// slot the value in the right spot
|
|
||||||
assocRef.setNthSibling(nthSibling);
|
|
||||||
nthSibling++;
|
|
||||||
// get the child
|
|
||||||
results.add(assoc.getChildAssocRef());
|
|
||||||
}
|
}
|
||||||
// done
|
// done
|
||||||
return results;
|
return orderedList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NodeRef getChildByName(NodeRef nodeRef, QName assocTypeQName, String childName)
|
||||||
|
{
|
||||||
|
Node node = getNodeNotNull(nodeRef);
|
||||||
|
ChildAssoc childAssoc = nodeDaoService.getChildAssoc(node, assocTypeQName, childName);
|
||||||
|
if (childAssoc != null)
|
||||||
|
{
|
||||||
|
return childAssoc.getChild().getNodeRef();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChildAssociationRef getPrimaryParent(NodeRef nodeRef) throws InvalidNodeRefException
|
public ChildAssociationRef getPrimaryParent(NodeRef nodeRef) throws InvalidNodeRefException
|
||||||
@@ -1111,11 +1156,10 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<AssociationRef> getTargetAssocs(NodeRef sourceRef, QNamePattern qnamePattern)
|
public List<AssociationRef> getTargetAssocs(NodeRef sourceRef, QNamePattern qnamePattern)
|
||||||
throws InvalidNodeRefException
|
|
||||||
{
|
{
|
||||||
Node sourceNode = getNodeNotNull(sourceRef);
|
Node sourceNode = getNodeNotNull(sourceRef);
|
||||||
// get all assocs to target
|
// get all assocs to target
|
||||||
Collection<NodeAssoc> assocs = sourceNode.getTargetNodeAssocs();
|
Collection<NodeAssoc> assocs = nodeDaoService.getTargetNodeAssocs(sourceNode);
|
||||||
List<AssociationRef> nodeAssocRefs = new ArrayList<AssociationRef>(assocs.size());
|
List<AssociationRef> nodeAssocRefs = new ArrayList<AssociationRef>(assocs.size());
|
||||||
for (NodeAssoc assoc : assocs)
|
for (NodeAssoc assoc : assocs)
|
||||||
{
|
{
|
||||||
@@ -1131,11 +1175,10 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<AssociationRef> getSourceAssocs(NodeRef targetRef, QNamePattern qnamePattern)
|
public List<AssociationRef> getSourceAssocs(NodeRef targetRef, QNamePattern qnamePattern)
|
||||||
throws InvalidNodeRefException
|
|
||||||
{
|
{
|
||||||
Node sourceNode = getNodeNotNull(targetRef);
|
Node targetNode = getNodeNotNull(targetRef);
|
||||||
// get all assocs to source
|
// get all assocs to source
|
||||||
Collection<NodeAssoc> assocs = sourceNode.getSourceNodeAssocs();
|
Collection<NodeAssoc> assocs = nodeDaoService.getSourceNodeAssocs(targetNode);
|
||||||
List<AssociationRef> nodeAssocRefs = new ArrayList<AssociationRef>(assocs.size());
|
List<AssociationRef> nodeAssocRefs = new ArrayList<AssociationRef>(assocs.size());
|
||||||
for (NodeAssoc assoc : assocs)
|
for (NodeAssoc assoc : assocs)
|
||||||
{
|
{
|
||||||
@@ -1437,7 +1480,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
// add the node to the map
|
// add the node to the map
|
||||||
nodesById.put(id, node);
|
nodesById.put(id, node);
|
||||||
// recurse into the primary children
|
// recurse into the primary children
|
||||||
Collection<ChildAssoc> childAssocs = node.getChildAssocs();
|
Collection<ChildAssoc> childAssocs = nodeDaoService.getChildAssocs(node);
|
||||||
for (ChildAssoc childAssoc : childAssocs)
|
for (ChildAssoc childAssoc : childAssocs)
|
||||||
{
|
{
|
||||||
// cascade into primary associations
|
// cascade into primary associations
|
||||||
@@ -1464,7 +1507,8 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
List<ChildAssoc> childAssocsToDelete = new ArrayList<ChildAssoc>(5);
|
List<ChildAssoc> childAssocsToDelete = new ArrayList<ChildAssoc>(5);
|
||||||
// child associations
|
// child associations
|
||||||
ArrayList<ChildAssociationRef> archivedChildAssocRefs = new ArrayList<ChildAssociationRef>(5);
|
ArrayList<ChildAssociationRef> archivedChildAssocRefs = new ArrayList<ChildAssociationRef>(5);
|
||||||
for (ChildAssoc assoc : node.getChildAssocs())
|
Collection<ChildAssoc> childAssocs = nodeDaoService.getChildAssocs(node);
|
||||||
|
for (ChildAssoc assoc : childAssocs)
|
||||||
{
|
{
|
||||||
Long relatedNodeId = assoc.getChild().getId();
|
Long relatedNodeId = assoc.getChild().getId();
|
||||||
if (nodesById.containsKey(relatedNodeId))
|
if (nodesById.containsKey(relatedNodeId))
|
||||||
@@ -1497,7 +1541,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
List<NodeAssoc> nodeAssocsToDelete = new ArrayList<NodeAssoc>(5);
|
List<NodeAssoc> nodeAssocsToDelete = new ArrayList<NodeAssoc>(5);
|
||||||
// source associations
|
// source associations
|
||||||
ArrayList<AssociationRef> archivedSourceAssocRefs = new ArrayList<AssociationRef>(5);
|
ArrayList<AssociationRef> archivedSourceAssocRefs = new ArrayList<AssociationRef>(5);
|
||||||
for (NodeAssoc assoc : node.getSourceNodeAssocs())
|
for (NodeAssoc assoc : nodeDaoService.getSourceNodeAssocs(node))
|
||||||
{
|
{
|
||||||
Long relatedNodeId = assoc.getSource().getId();
|
Long relatedNodeId = assoc.getSource().getId();
|
||||||
if (nodesById.containsKey(relatedNodeId))
|
if (nodesById.containsKey(relatedNodeId))
|
||||||
@@ -1510,7 +1554,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
}
|
}
|
||||||
// target associations
|
// target associations
|
||||||
ArrayList<AssociationRef> archivedTargetAssocRefs = new ArrayList<AssociationRef>(5);
|
ArrayList<AssociationRef> archivedTargetAssocRefs = new ArrayList<AssociationRef>(5);
|
||||||
for (NodeAssoc assoc : node.getTargetNodeAssocs())
|
for (NodeAssoc assoc : nodeDaoService.getTargetNodeAssocs(node))
|
||||||
{
|
{
|
||||||
Long relatedNodeId = assoc.getTarget().getId();
|
Long relatedNodeId = assoc.getTarget().getId();
|
||||||
if (nodesById.containsKey(relatedNodeId))
|
if (nodesById.containsKey(relatedNodeId))
|
||||||
@@ -1669,10 +1713,21 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Node parentNode = getNodeNotNull(parentNodeRef);
|
Node parentNode = getNodeNotNull(parentNodeRef);
|
||||||
nodeDaoService.newChildAssoc(parentNode, node, assocRef.isPrimary(), assocRef.getTypeQName(), assocRef.getQName());
|
// get the name to use for the unique child check
|
||||||
|
QName assocTypeQName = assocRef.getTypeQName();
|
||||||
|
nodeDaoService.newChildAssoc(
|
||||||
|
parentNode,
|
||||||
|
node,
|
||||||
|
assocRef.isPrimary(),
|
||||||
|
assocTypeQName,
|
||||||
|
assocRef.getQName());
|
||||||
}
|
}
|
||||||
properties.remove(ContentModel.PROP_ARCHIVED_PARENT_ASSOCS);
|
properties.remove(ContentModel.PROP_ARCHIVED_PARENT_ASSOCS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// make sure that the node name uniqueness is enforced
|
||||||
|
setChildUniqueName(node);
|
||||||
|
|
||||||
// restore child associations
|
// restore child associations
|
||||||
Collection<ChildAssociationRef> childAssocRefs = (Collection<ChildAssociationRef>) getProperty(
|
Collection<ChildAssociationRef> childAssocRefs = (Collection<ChildAssociationRef>) getProperty(
|
||||||
nodeRef,
|
nodeRef,
|
||||||
@@ -1687,7 +1742,16 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Node childNode = getNodeNotNull(childNodeRef);
|
Node childNode = getNodeNotNull(childNodeRef);
|
||||||
nodeDaoService.newChildAssoc(node, childNode, assocRef.isPrimary(), assocRef.getTypeQName(), assocRef.getQName());
|
QName assocTypeQName = assocRef.getTypeQName();
|
||||||
|
// get the name to use for the unique child check
|
||||||
|
nodeDaoService.newChildAssoc(
|
||||||
|
node,
|
||||||
|
childNode,
|
||||||
|
assocRef.isPrimary(),
|
||||||
|
assocTypeQName,
|
||||||
|
assocRef.getQName());
|
||||||
|
// ensure that the name uniqueness is enforced for the child node
|
||||||
|
setChildUniqueName(childNode);
|
||||||
}
|
}
|
||||||
properties.remove(ContentModel.PROP_ARCHIVED_CHILD_ASSOCS);
|
properties.remove(ContentModel.PROP_ARCHIVED_CHILD_ASSOCS);
|
||||||
}
|
}
|
||||||
@@ -1730,4 +1794,56 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
// remove the aspect
|
// remove the aspect
|
||||||
node.getAspects().remove(ContentModel.ASPECT_ARCHIVED_ASSOCS);
|
node.getAspects().remove(ContentModel.ASPECT_ARCHIVED_ASSOCS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the dictionary's definition of the association to assign a unique name to the child node.
|
||||||
|
*
|
||||||
|
* @param assocTypeQName the type of the child association
|
||||||
|
* @param childNode the child node being added. The name will be extracted from it, if necessary.
|
||||||
|
* @return Returns the value to be put on the child association for uniqueness, or null if
|
||||||
|
*/
|
||||||
|
private void setChildUniqueName(Node childNode)
|
||||||
|
{
|
||||||
|
// get the name property
|
||||||
|
Map<QName, PropertyValue> properties = childNode.getProperties();
|
||||||
|
PropertyValue nameValue = properties.get(ContentModel.PROP_NAME);
|
||||||
|
String useName = null;
|
||||||
|
if (nameValue == null)
|
||||||
|
{
|
||||||
|
// no name has been assigned, so assign the ID of the child node
|
||||||
|
useName = childNode.getUuid();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
useName = (String) nameValue.getValue(DataTypeDefinition.TEXT);
|
||||||
|
}
|
||||||
|
// get all the parent assocs
|
||||||
|
Collection<ChildAssoc> parentAssocs = childNode.getParentAssocs();
|
||||||
|
for (ChildAssoc assoc : parentAssocs)
|
||||||
|
{
|
||||||
|
QName assocTypeQName = assoc.getTypeQName();
|
||||||
|
AssociationDefinition assocDef = dictionaryService.getAssociation(assocTypeQName);
|
||||||
|
if (!assocDef.isChild())
|
||||||
|
{
|
||||||
|
throw new DataIntegrityViolationException("Child association has non-child type: " + assoc.getId());
|
||||||
|
}
|
||||||
|
ChildAssociationDefinition childAssocDef = (ChildAssociationDefinition) assocDef;
|
||||||
|
if (childAssocDef.getDuplicateChildNamesAllowed())
|
||||||
|
{
|
||||||
|
// the name is irrelevant, so it doesn't need to be put into the unique key
|
||||||
|
nodeDaoService.setChildNameUnique(assoc, null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nodeDaoService.setChildNameUnique(assoc, useName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// done
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug(
|
||||||
|
"Unique name set for all " + parentAssocs.size() + " parent associations: \n" +
|
||||||
|
" name: " + useName);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.repo.node.db;
|
package org.alfresco.repo.node.db;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.alfresco.repo.domain.ChildAssoc;
|
import org.alfresco.repo.domain.ChildAssoc;
|
||||||
@@ -24,6 +25,7 @@ import org.alfresco.repo.domain.NodeAssoc;
|
|||||||
import org.alfresco.repo.domain.NodeStatus;
|
import org.alfresco.repo.domain.NodeStatus;
|
||||||
import org.alfresco.repo.domain.Store;
|
import org.alfresco.repo.domain.Store;
|
||||||
import org.alfresco.service.cmr.dictionary.InvalidTypeException;
|
import org.alfresco.service.cmr.dictionary.InvalidTypeException;
|
||||||
|
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
|
|
||||||
@@ -128,6 +130,30 @@ public interface NodeDaoService
|
|||||||
QName assocTypeQName,
|
QName assocTypeQName,
|
||||||
QName qname);
|
QName qname);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the name of the child node.
|
||||||
|
*
|
||||||
|
* @param childAssoc the child association to change
|
||||||
|
* @param childName the name to put on the association
|
||||||
|
*/
|
||||||
|
public void setChildNameUnique(ChildAssoc childAssoc, String childName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all child associations for a given node
|
||||||
|
*
|
||||||
|
* @param parentNode the parent of the child associations
|
||||||
|
* @return Returns all child associations for the given node
|
||||||
|
*/
|
||||||
|
public Collection<ChildAssoc> getChildAssocs(final Node parentNode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a collection of all child association references for a given parent node.
|
||||||
|
*
|
||||||
|
* @param parentNode the parent node
|
||||||
|
* @return Returns a collection of association references
|
||||||
|
*/
|
||||||
|
public Collection<ChildAssociationRef> getChildAssocRefs(Node parentNode);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Returns a matching association or null if one was not found
|
* @return Returns a matching association or null if one was not found
|
||||||
*
|
*
|
||||||
@@ -138,7 +164,12 @@ public interface NodeDaoService
|
|||||||
Node childNode,
|
Node childNode,
|
||||||
QName assocTypeQName,
|
QName assocTypeQName,
|
||||||
QName qname);
|
QName qname);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns an association matching the given parent, type and child name - or null
|
||||||
|
* if not found
|
||||||
|
*/
|
||||||
|
public ChildAssoc getChildAssoc(Node parentNode, QName assocTypeQName, String childName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param assoc the child association to remove
|
* @param assoc the child association to remove
|
||||||
@@ -164,6 +195,11 @@ public interface NodeDaoService
|
|||||||
Node targetNode,
|
Node targetNode,
|
||||||
QName assocTypeQName);
|
QName assocTypeQName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns a list of all node associations associated with the given node
|
||||||
|
*/
|
||||||
|
public List<NodeAssoc> getNodeAssocsToAndFrom(final Node node);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Returns the node association or null if not found
|
* @return Returns the node association or null if not found
|
||||||
*/
|
*/
|
||||||
@@ -172,6 +208,16 @@ public interface NodeDaoService
|
|||||||
Node targetNode,
|
Node targetNode,
|
||||||
QName assocTypeQName);
|
QName assocTypeQName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns all the node associations where the node is the <b>source</b>
|
||||||
|
*/
|
||||||
|
public List<NodeAssoc> getTargetNodeAssocs(Node sourceNode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns all the node associations where the node is the </b>target</b>
|
||||||
|
*/
|
||||||
|
public List<NodeAssoc> getSourceNodeAssocs(Node targetNode);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param assoc the node association to remove
|
* @param assoc the node association to remove
|
||||||
*/
|
*/
|
||||||
|
@@ -19,7 +19,9 @@ package org.alfresco.repo.node.db.hibernate;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.zip.CRC32;
|
||||||
|
|
||||||
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
import org.alfresco.model.ContentModel;
|
import org.alfresco.model.ContentModel;
|
||||||
import org.alfresco.repo.domain.ChildAssoc;
|
import org.alfresco.repo.domain.ChildAssoc;
|
||||||
import org.alfresco.repo.domain.Node;
|
import org.alfresco.repo.domain.Node;
|
||||||
@@ -37,9 +39,11 @@ import org.alfresco.repo.node.db.NodeDaoService;
|
|||||||
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
||||||
import org.alfresco.repo.transaction.TransactionalDao;
|
import org.alfresco.repo.transaction.TransactionalDao;
|
||||||
import org.alfresco.service.cmr.dictionary.InvalidTypeException;
|
import org.alfresco.service.cmr.dictionary.InvalidTypeException;
|
||||||
import org.alfresco.service.cmr.repository.AssociationRef;
|
import org.alfresco.service.cmr.repository.AssociationExistsException;
|
||||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||||
|
import org.alfresco.service.cmr.repository.DuplicateChildNodeNameException;
|
||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
import org.alfresco.service.cmr.repository.StoreRef;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
import org.alfresco.util.GUID;
|
import org.alfresco.util.GUID;
|
||||||
import org.hibernate.ObjectDeletedException;
|
import org.hibernate.ObjectDeletedException;
|
||||||
@@ -58,6 +62,14 @@ import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
|
|||||||
public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements NodeDaoService, TransactionalDao
|
public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements NodeDaoService, TransactionalDao
|
||||||
{
|
{
|
||||||
private static final String QUERY_GET_ALL_STORES = "store.GetAllStores";
|
private static final String QUERY_GET_ALL_STORES = "store.GetAllStores";
|
||||||
|
private static final String UPDATE_SET_CHILD_ASSOC_NAME = "node.updateChildAssocName";
|
||||||
|
private static final String QUERY_GET_CHILD_ASSOCS = "node.GetChildAssocs";
|
||||||
|
private static final String QUERY_GET_CHILD_ASSOC_BY_TYPE_AND_NAME = "node.GetChildAssocByTypeAndName";
|
||||||
|
private static final String QUERY_GET_CHILD_ASSOC_REFS = "node.GetChildAssocRefs";
|
||||||
|
private static final String QUERY_GET_NODE_ASSOC = "node.GetNodeAssoc";
|
||||||
|
private static final String QUERY_GET_NODE_ASSOCS_TO_AND_FROM = "node.GetNodeAssocsToAndFrom";
|
||||||
|
private static final String QUERY_GET_TARGET_ASSOCS = "node.GetTargetAssocs";
|
||||||
|
private static final String QUERY_GET_SOURCE_ASSOCS = "node.GetSourceAssocs";
|
||||||
private static final String QUERY_GET_CONTENT_DATA_STRINGS = "node.GetContentDataStrings";
|
private static final String QUERY_GET_CONTENT_DATA_STRINGS = "node.GetContentDataStrings";
|
||||||
|
|
||||||
/** a uuid identifying this unique instance */
|
/** a uuid identifying this unique instance */
|
||||||
@@ -303,25 +315,17 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
|
|||||||
deleteChildAssoc(assoc, false); // we don't cascade upwards
|
deleteChildAssoc(assoc, false); // we don't cascade upwards
|
||||||
}
|
}
|
||||||
// delete all child assocs
|
// delete all child assocs
|
||||||
Collection<ChildAssoc> childAssocs = node.getChildAssocs();
|
Collection<ChildAssoc> childAssocs = getChildAssocs(node);
|
||||||
childAssocs = new ArrayList<ChildAssoc>(childAssocs);
|
childAssocs = new ArrayList<ChildAssoc>(childAssocs);
|
||||||
for (ChildAssoc assoc : childAssocs)
|
for (ChildAssoc assoc : childAssocs)
|
||||||
{
|
{
|
||||||
deleteChildAssoc(assoc, cascade); // potentially cascade downwards
|
deleteChildAssoc(assoc, cascade); // potentially cascade downwards
|
||||||
}
|
}
|
||||||
// delete all target assocs
|
// delete all node associations to and from
|
||||||
Collection<NodeAssoc> targetAssocs = node.getTargetNodeAssocs();
|
List<NodeAssoc> nodeAssocs = getNodeAssocsToAndFrom(node);
|
||||||
targetAssocs = new ArrayList<NodeAssoc>(targetAssocs);
|
for (NodeAssoc assoc : nodeAssocs)
|
||||||
for (NodeAssoc assoc : targetAssocs)
|
|
||||||
{
|
{
|
||||||
deleteNodeAssoc(assoc);
|
getHibernateTemplate().delete(assoc);
|
||||||
}
|
|
||||||
// delete all source assocs
|
|
||||||
Collection<NodeAssoc> sourceAssocs = node.getSourceNodeAssocs();
|
|
||||||
sourceAssocs = new ArrayList<NodeAssoc>(sourceAssocs);
|
|
||||||
for (NodeAssoc assoc : sourceAssocs)
|
|
||||||
{
|
|
||||||
deleteNodeAssoc(assoc);
|
|
||||||
}
|
}
|
||||||
// update the node status
|
// update the node status
|
||||||
NodeRef nodeRef = node.getNodeRef();
|
NodeRef nodeRef = node.getNodeRef();
|
||||||
@@ -330,9 +334,34 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
|
|||||||
nodeStatus.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId());
|
nodeStatus.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId());
|
||||||
// finally delete the node
|
// finally delete the node
|
||||||
getHibernateTemplate().delete(node);
|
getHibernateTemplate().delete(node);
|
||||||
|
// flush to ensure constraints can't be violated
|
||||||
|
getSession().flush();
|
||||||
// done
|
// done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private long getCrc(String str)
|
||||||
|
{
|
||||||
|
CRC32 crc = new CRC32();
|
||||||
|
crc.update(str.getBytes());
|
||||||
|
return crc.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String TRUNCATED_NAME_INDICATOR = "~~~";
|
||||||
|
private String getShortName(String str)
|
||||||
|
{
|
||||||
|
int length = str.length();
|
||||||
|
if (length <= 50)
|
||||||
|
{
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StringBuilder ret = new StringBuilder(50);
|
||||||
|
ret.append(str.substring(0, 47)).append(TRUNCATED_NAME_INDICATOR);
|
||||||
|
return ret.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public ChildAssoc newChildAssoc(
|
public ChildAssoc newChildAssoc(
|
||||||
Node parentNode,
|
Node parentNode,
|
||||||
Node childNode,
|
Node childNode,
|
||||||
@@ -340,17 +369,170 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
|
|||||||
QName assocTypeQName,
|
QName assocTypeQName,
|
||||||
QName qname)
|
QName qname)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* This initial child association creation will fail IFF there is already
|
||||||
|
* an association of the given type between the two nodes. For new association
|
||||||
|
* creation, this can only occur if two transactions attempt to create a secondary
|
||||||
|
* child association between the same two nodes. As this is unlikely, it is
|
||||||
|
* appropriate to just throw a runtime exception and let the second transaction
|
||||||
|
* fail.
|
||||||
|
*
|
||||||
|
* We don't need to flush the session beforehand as there won't be any deletions
|
||||||
|
* of the assocation pending. The association deletes, on the other hand, have
|
||||||
|
* to flush early in order to ensure that the database unique index isn't violated
|
||||||
|
* if the association is recreated subsequently.
|
||||||
|
*/
|
||||||
|
|
||||||
|
String uuid = childNode.getUuid();
|
||||||
|
|
||||||
ChildAssoc assoc = new ChildAssocImpl();
|
ChildAssoc assoc = new ChildAssocImpl();
|
||||||
assoc.setTypeQName(assocTypeQName);
|
assoc.setTypeQName(assocTypeQName);
|
||||||
assoc.setIsPrimary(isPrimary);
|
assoc.setChildNodeName(getShortName(uuid));
|
||||||
|
assoc.setChildNodeNameCrc(getCrc(uuid));
|
||||||
assoc.setQname(qname);
|
assoc.setQname(qname);
|
||||||
|
assoc.setIsPrimary(isPrimary);
|
||||||
assoc.buildAssociation(parentNode, childNode);
|
assoc.buildAssociation(parentNode, childNode);
|
||||||
// persist
|
// persist it, catching the duplicate child name
|
||||||
getHibernateTemplate().save(assoc);
|
try
|
||||||
|
{
|
||||||
|
getHibernateTemplate().save(assoc);
|
||||||
|
}
|
||||||
|
catch (DataIntegrityViolationException e)
|
||||||
|
{
|
||||||
|
throw new AlfrescoRuntimeException("An association already exists between the two nodes: \n" +
|
||||||
|
" parent: " + parentNode.getId() + "\n" +
|
||||||
|
" child: " + childNode.getId() + "\n" +
|
||||||
|
" assoc: " + assocTypeQName,
|
||||||
|
e);
|
||||||
|
}
|
||||||
// done
|
// done
|
||||||
return assoc;
|
return assoc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setChildNameUnique(final ChildAssoc childAssoc, String childName)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* As the Hibernate session is rendered useless when an exception is
|
||||||
|
* bubbled up, we go direct to the database to update the child association.
|
||||||
|
* This preserves the session and client code can catch the resulting
|
||||||
|
* exception and react to it whilst in the same transaction.
|
||||||
|
*
|
||||||
|
* We ensure that case-insensitivity is maintained by persisting
|
||||||
|
* the lowercase version of the child node name.
|
||||||
|
*/
|
||||||
|
|
||||||
|
String childNameNew = null;
|
||||||
|
if (childName == null)
|
||||||
|
{
|
||||||
|
childNameNew = childAssoc.getChild().getUuid();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
childNameNew = childName.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
final String childNameNewShort = getShortName(childNameNew);
|
||||||
|
final long childNameNewCrc = getCrc(childNameNew);
|
||||||
|
|
||||||
|
// check if the name has changed
|
||||||
|
if (childAssoc.getChildNodeNameCrc() == childNameNewCrc)
|
||||||
|
{
|
||||||
|
if (childAssoc.getChildNodeName().equals(childNameNewShort))
|
||||||
|
{
|
||||||
|
// nothing changed
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HibernateCallback callback = new HibernateCallback()
|
||||||
|
{
|
||||||
|
public Object doInHibernate(Session session)
|
||||||
|
{
|
||||||
|
session.flush();
|
||||||
|
Query query = session
|
||||||
|
.getNamedQuery(HibernateNodeDaoServiceImpl.UPDATE_SET_CHILD_ASSOC_NAME)
|
||||||
|
.setString("newName", childNameNewShort)
|
||||||
|
.setLong("newNameCrc", childNameNewCrc)
|
||||||
|
.setLong("childAssocId", childAssoc.getId());
|
||||||
|
return (Integer) query.executeUpdate();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Integer count = (Integer) getHibernateTemplate().execute(callback);
|
||||||
|
// refresh the entity directly
|
||||||
|
getHibernateTemplate().refresh(childAssoc);
|
||||||
|
if (count.intValue() == 0)
|
||||||
|
{
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("ChildAssoc not updated: " + childAssoc.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (DataIntegrityViolationException e)
|
||||||
|
{
|
||||||
|
NodeRef parentNodeRef = childAssoc.getParent().getNodeRef();
|
||||||
|
QName assocTypeQName = childAssoc.getTypeQName();
|
||||||
|
throw new DuplicateChildNodeNameException(parentNodeRef, assocTypeQName, childName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Collection<ChildAssoc> getChildAssocs(final Node parentNode)
|
||||||
|
{
|
||||||
|
HibernateCallback callback = new HibernateCallback()
|
||||||
|
{
|
||||||
|
public Object doInHibernate(Session session)
|
||||||
|
{
|
||||||
|
Query query = session
|
||||||
|
.getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CHILD_ASSOCS)
|
||||||
|
.setLong("parentId", parentNode.getId());
|
||||||
|
return query.list();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
List<ChildAssoc> queryResults = (List) getHibernateTemplate().execute(callback);
|
||||||
|
return queryResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Collection<ChildAssociationRef> getChildAssocRefs(final Node parentNode)
|
||||||
|
{
|
||||||
|
HibernateCallback callback = new HibernateCallback()
|
||||||
|
{
|
||||||
|
public Object doInHibernate(Session session)
|
||||||
|
{
|
||||||
|
Query query = session
|
||||||
|
.getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CHILD_ASSOC_REFS)
|
||||||
|
.setLong("parentId", parentNode.getId());
|
||||||
|
return query.list();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
List<Object[]> queryResults = (List<Object[]>) getHibernateTemplate().execute(callback);
|
||||||
|
Collection<ChildAssociationRef> refs = new ArrayList<ChildAssociationRef>(queryResults.size());
|
||||||
|
NodeRef parentNodeRef = parentNode.getNodeRef();
|
||||||
|
for (Object[] row : queryResults)
|
||||||
|
{
|
||||||
|
String childProtocol = (String) row[5];
|
||||||
|
String childIdentifier = (String) row[6];
|
||||||
|
String childUuid = (String) row[7];
|
||||||
|
NodeRef childNodeRef = new NodeRef(new StoreRef(childProtocol, childIdentifier), childUuid);
|
||||||
|
QName assocTypeQName = (QName) row[0];
|
||||||
|
QName assocQName = (QName) row[1];
|
||||||
|
Boolean assocIsPrimary = (Boolean) row[2];
|
||||||
|
Integer assocIndex = (Integer) row[3];
|
||||||
|
ChildAssociationRef assocRef = new ChildAssociationRef(
|
||||||
|
assocTypeQName,
|
||||||
|
parentNodeRef,
|
||||||
|
assocQName,
|
||||||
|
childNodeRef,
|
||||||
|
assocIsPrimary.booleanValue(),
|
||||||
|
assocIndex.intValue());
|
||||||
|
refs.add(assocRef);
|
||||||
|
}
|
||||||
|
return refs;
|
||||||
|
}
|
||||||
|
|
||||||
public ChildAssoc getChildAssoc(
|
public ChildAssoc getChildAssoc(
|
||||||
Node parentNode,
|
Node parentNode,
|
||||||
Node childNode,
|
Node childNode,
|
||||||
@@ -363,7 +545,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
|
|||||||
qname,
|
qname,
|
||||||
childNode.getNodeRef());
|
childNode.getNodeRef());
|
||||||
// get all the parent's child associations
|
// get all the parent's child associations
|
||||||
Collection<ChildAssoc> assocs = parentNode.getChildAssocs();
|
Collection<ChildAssoc> assocs = getChildAssocs(parentNode);
|
||||||
// hunt down the desired assoc
|
// hunt down the desired assoc
|
||||||
for (ChildAssoc assoc : assocs)
|
for (ChildAssoc assoc : assocs)
|
||||||
{
|
{
|
||||||
@@ -381,6 +563,28 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ChildAssoc getChildAssoc(final Node parentNode, final QName assocTypeQName, final String childName)
|
||||||
|
{
|
||||||
|
HibernateCallback callback = new HibernateCallback()
|
||||||
|
{
|
||||||
|
public Object doInHibernate(Session session)
|
||||||
|
{
|
||||||
|
String childNameLower = childName.toLowerCase();
|
||||||
|
String childNameShort = getShortName(childNameLower);
|
||||||
|
long childNameLowerCrc = getCrc(childNameLower);
|
||||||
|
Query query = session
|
||||||
|
.getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CHILD_ASSOC_BY_TYPE_AND_NAME)
|
||||||
|
.setLong("parentId", parentNode.getId())
|
||||||
|
.setParameter("typeQName", assocTypeQName)
|
||||||
|
.setParameter("childNodeName", childNameShort)
|
||||||
|
.setLong("childNodeNameCrc", childNameLowerCrc);
|
||||||
|
return query.uniqueResult();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ChildAssoc childAssoc = (ChildAssoc) getHibernateTemplate().execute(callback);
|
||||||
|
return childAssoc;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manually enforces cascade deletions down primary associations
|
* Manually enforces cascade deletions down primary associations
|
||||||
*/
|
*/
|
||||||
@@ -403,6 +607,10 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
|
|||||||
* duplicate call will be received to do this
|
* duplicate call will be received to do this
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// To ensure the validity of the constraint enforcement by the database,
|
||||||
|
// we have to flush here
|
||||||
|
getSession().flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChildAssoc getPrimaryParentAssoc(Node node)
|
public ChildAssoc getPrimaryParentAssoc(Node node)
|
||||||
@@ -458,45 +666,101 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
|
|||||||
assoc.setTypeQName(assocTypeQName);
|
assoc.setTypeQName(assocTypeQName);
|
||||||
assoc.buildAssociation(sourceNode, targetNode);
|
assoc.buildAssociation(sourceNode, targetNode);
|
||||||
// persist
|
// persist
|
||||||
getHibernateTemplate().save(assoc);
|
try
|
||||||
|
{
|
||||||
|
getHibernateTemplate().save(assoc);
|
||||||
|
}
|
||||||
|
catch (DataIntegrityViolationException e)
|
||||||
|
{
|
||||||
|
throw new AssociationExistsException(
|
||||||
|
sourceNode.getNodeRef(),
|
||||||
|
targetNode.getNodeRef(),
|
||||||
|
assocTypeQName,
|
||||||
|
e);
|
||||||
|
}
|
||||||
// done
|
// done
|
||||||
return assoc;
|
return assoc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public List<NodeAssoc> getNodeAssocsToAndFrom(final Node node)
|
||||||
|
{
|
||||||
|
HibernateCallback callback = new HibernateCallback()
|
||||||
|
{
|
||||||
|
public Object doInHibernate(Session session)
|
||||||
|
{
|
||||||
|
Query query = session
|
||||||
|
.getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_NODE_ASSOCS_TO_AND_FROM)
|
||||||
|
.setLong("nodeId", node.getId());
|
||||||
|
return query.list();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
List<NodeAssoc> results = (List<NodeAssoc>) getHibernateTemplate().execute(callback);
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
public NodeAssoc getNodeAssoc(
|
public NodeAssoc getNodeAssoc(
|
||||||
final Node sourceNode,
|
final Node sourceNode,
|
||||||
final Node targetNode,
|
final Node targetNode,
|
||||||
final QName assocTypeQName)
|
final QName assocTypeQName)
|
||||||
{
|
{
|
||||||
AssociationRef nodeAssocRef = new AssociationRef(
|
HibernateCallback callback = new HibernateCallback()
|
||||||
sourceNode.getNodeRef(),
|
|
||||||
assocTypeQName,
|
|
||||||
targetNode.getNodeRef());
|
|
||||||
// get all the source's target associations
|
|
||||||
Collection<NodeAssoc> assocs = sourceNode.getTargetNodeAssocs();
|
|
||||||
// hunt down the desired assoc
|
|
||||||
for (NodeAssoc assoc : assocs)
|
|
||||||
{
|
{
|
||||||
// is it a match?
|
public Object doInHibernate(Session session)
|
||||||
if (!assoc.getNodeAssocRef().equals(nodeAssocRef)) // not a match
|
|
||||||
{
|
{
|
||||||
continue;
|
Query query = session
|
||||||
|
.getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_NODE_ASSOC)
|
||||||
|
.setLong("sourceId", sourceNode.getId())
|
||||||
|
.setLong("targetId", targetNode.getId())
|
||||||
|
.setParameter("assocTypeQName", assocTypeQName);
|
||||||
|
return query.uniqueResult();
|
||||||
}
|
}
|
||||||
else
|
};
|
||||||
|
NodeAssoc result = (NodeAssoc) getHibernateTemplate().execute(callback);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public List<NodeAssoc> getTargetNodeAssocs(final Node sourceNode)
|
||||||
|
{
|
||||||
|
HibernateCallback callback = new HibernateCallback()
|
||||||
|
{
|
||||||
|
public Object doInHibernate(Session session)
|
||||||
{
|
{
|
||||||
return assoc;
|
Query query = session
|
||||||
|
.getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_TARGET_ASSOCS)
|
||||||
|
.setLong("sourceId", sourceNode.getId());
|
||||||
|
return query.list();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
// not found
|
List<NodeAssoc> queryResults = (List<NodeAssoc>) getHibernateTemplate().execute(callback);
|
||||||
return null;
|
return queryResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public List<NodeAssoc> getSourceNodeAssocs(final Node targetNode)
|
||||||
|
{
|
||||||
|
HibernateCallback callback = new HibernateCallback()
|
||||||
|
{
|
||||||
|
public Object doInHibernate(Session session)
|
||||||
|
{
|
||||||
|
Query query = session
|
||||||
|
.getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_SOURCE_ASSOCS)
|
||||||
|
.setLong("targetId", targetNode.getId());
|
||||||
|
return query.list();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
List<NodeAssoc> queryResults = (List<NodeAssoc>) getHibernateTemplate().execute(callback);
|
||||||
|
return queryResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteNodeAssoc(NodeAssoc assoc)
|
public void deleteNodeAssoc(NodeAssoc assoc)
|
||||||
{
|
{
|
||||||
// maintain inverse association sets
|
// Remove instance
|
||||||
assoc.removeAssociation();
|
|
||||||
// remove instance
|
|
||||||
getHibernateTemplate().delete(assoc);
|
getHibernateTemplate().delete(assoc);
|
||||||
|
// Flush to ensure that the database constraints aren't violated if the assoc
|
||||||
|
// is recreated in the transaction
|
||||||
|
getSession().flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@@ -513,24 +777,4 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
|
|||||||
List<String> queryResults = (List) getHibernateTemplate().execute(callback);
|
List<String> queryResults = (List) getHibernateTemplate().execute(callback);
|
||||||
return queryResults;
|
return queryResults;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@@ -21,14 +21,10 @@ import java.util.List;
|
|||||||
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
|
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
|
||||||
import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition;
|
import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition;
|
||||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||||
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.NodeRef;
|
||||||
import org.alfresco.service.cmr.repository.NodeService;
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
import org.alfresco.service.namespace.RegexQNamePattern;
|
import org.alfresco.service.namespace.RegexQNamePattern;
|
||||||
import org.apache.commons.logging.Log;
|
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event to check the association target role name
|
* Event to check the association target role name
|
||||||
@@ -37,8 +33,6 @@ import org.apache.commons.logging.LogFactory;
|
|||||||
*/
|
*/
|
||||||
public class AssocTargetRoleIntegrityEvent extends AbstractIntegrityEvent
|
public class AssocTargetRoleIntegrityEvent extends AbstractIntegrityEvent
|
||||||
{
|
{
|
||||||
private static Log logger = LogFactory.getLog(AssocTargetRoleIntegrityEvent.class);
|
|
||||||
|
|
||||||
public AssocTargetRoleIntegrityEvent(
|
public AssocTargetRoleIntegrityEvent(
|
||||||
NodeService nodeService,
|
NodeService nodeService,
|
||||||
DictionaryService dictionaryService,
|
DictionaryService dictionaryService,
|
||||||
@@ -51,7 +45,6 @@ public class AssocTargetRoleIntegrityEvent extends AbstractIntegrityEvent
|
|||||||
|
|
||||||
public void checkIntegrity(List<IntegrityRecord> eventResults)
|
public void checkIntegrity(List<IntegrityRecord> eventResults)
|
||||||
{
|
{
|
||||||
NodeRef sourceNodeRef = getNodeRef();
|
|
||||||
QName assocTypeQName = getTypeQName();
|
QName assocTypeQName = getTypeQName();
|
||||||
QName assocQName = getQName();
|
QName assocQName = getQName();
|
||||||
|
|
||||||
@@ -80,7 +73,6 @@ public class AssocTargetRoleIntegrityEvent extends AbstractIntegrityEvent
|
|||||||
|
|
||||||
// perform required checks
|
// perform required checks
|
||||||
checkAssocQNameRegex(eventResults, childAssocDef, assocQName);
|
checkAssocQNameRegex(eventResults, childAssocDef, assocQName);
|
||||||
checkAssocQNameDuplicate(eventResults, childAssocDef, sourceNodeRef, assocQName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -108,39 +100,4 @@ public class AssocTargetRoleIntegrityEvent extends AbstractIntegrityEvent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks that the association name matches the constraints imposed by the model.
|
|
||||||
*/
|
|
||||||
protected void checkAssocQNameDuplicate(
|
|
||||||
List<IntegrityRecord> eventResults,
|
|
||||||
ChildAssociationDefinition assocDef,
|
|
||||||
NodeRef sourceNodeRef,
|
|
||||||
QName assocQName)
|
|
||||||
{
|
|
||||||
if (assocDef.getDuplicateChildNamesAllowed())
|
|
||||||
{
|
|
||||||
// nothing to do
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
QName assocTypeQName = assocDef.getName();
|
|
||||||
// see if there is another association with the same name
|
|
||||||
try
|
|
||||||
{
|
|
||||||
List<ChildAssociationRef> childAssocs = nodeService.getChildAssocs(sourceNodeRef, assocTypeQName, assocQName);
|
|
||||||
// duplicates not allowed
|
|
||||||
if (childAssocs.size() > 1)
|
|
||||||
{
|
|
||||||
IntegrityRecord result = new IntegrityRecord(
|
|
||||||
"Duplicate child associations are not allowed: \n" +
|
|
||||||
" Association: " + assocDef + "\n" +
|
|
||||||
" Name: " + assocQName);
|
|
||||||
eventResults.add(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (InvalidNodeRefException e)
|
|
||||||
{
|
|
||||||
// node has gone
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -442,6 +442,15 @@ public class NodeServiceImpl implements NodeService, VersionModel
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws UnsupportedOperationException always
|
||||||
|
*/
|
||||||
|
public NodeRef getChildByName(NodeRef nodeRef, QName assocTypeQName, String childName)
|
||||||
|
{
|
||||||
|
// This operation is not supported for a verion store
|
||||||
|
throw new UnsupportedOperationException(MSG_UNSUPPORTED);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simulates the node begin attached ot the root node of the version store.
|
* Simulates the node begin attached ot the root node of the version store.
|
||||||
*/
|
*/
|
||||||
|
@@ -16,29 +16,37 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.service.cmr.model;
|
package org.alfresco.service.cmr.model;
|
||||||
|
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common, checked exception thrown when an operation fails because
|
* Common, checked exception thrown when an operation fails because
|
||||||
* of a name clash.
|
* of a name clash.
|
||||||
*
|
*
|
||||||
* @author Derek Hulley
|
* @author Derek Hulley
|
||||||
*/
|
*/
|
||||||
public class FileExistsException extends Exception
|
public class FileExistsException extends RuntimeException
|
||||||
{
|
{
|
||||||
private static final long serialVersionUID = -4133713912784624118L;
|
private static final long serialVersionUID = -4133713912784624118L;
|
||||||
|
|
||||||
private FileInfo existing;
|
private NodeRef parentNodeRef;
|
||||||
|
private String name;
|
||||||
|
|
||||||
public FileExistsException(FileInfo existing)
|
public FileExistsException(NodeRef parentNodeRef, String name)
|
||||||
{
|
{
|
||||||
super("" +
|
super("Existing file or folder " +
|
||||||
(existing.isFolder() ? "Folder " : "File ") +
|
name +
|
||||||
existing.getName() +
|
|
||||||
" already exists");
|
" already exists");
|
||||||
this.existing = existing;
|
this.parentNodeRef = parentNodeRef;
|
||||||
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FileInfo getExisting()
|
public NodeRef getParentNodeRef()
|
||||||
{
|
{
|
||||||
return existing;
|
return parentNodeRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName()
|
||||||
|
{
|
||||||
|
return name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -61,6 +61,16 @@ public interface FileFolderService
|
|||||||
@Auditable(key = Auditable.Key.ARG_0, parameters = {"contextNodeRef"})
|
@Auditable(key = Auditable.Key.ARG_0, parameters = {"contextNodeRef"})
|
||||||
public List<FileInfo> listFolders(NodeRef contextNodeRef);
|
public List<FileInfo> listFolders(NodeRef contextNodeRef);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a simple list of nodes that have the given name within the parent node
|
||||||
|
*
|
||||||
|
* @param contextNodeRef the parent node
|
||||||
|
* @param name the name of the node to search for
|
||||||
|
* @return Returns the node that has the given name - or null if not found
|
||||||
|
*/
|
||||||
|
@Auditable(key = Auditable.Key.ARG_0, parameters = {"contextNodeRef", "name"})
|
||||||
|
public NodeRef searchSimple(NodeRef contextNodeRef, String name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Searches for all files and folders with the matching name pattern,
|
* Searches for all files and folders with the matching name pattern,
|
||||||
* using wildcard characters <b>*</b> and <b>?</b>.
|
* using wildcard characters <b>*</b> and <b>?</b>.
|
||||||
|
@@ -33,9 +33,7 @@ public class AssociationExistsException extends RuntimeException
|
|||||||
private QName qname;
|
private QName qname;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param sourceRef the source of the association
|
* @see #AssociationExistsException(NodeRef, NodeRef, QName, Throwable)
|
||||||
* @param targetRef the target of the association
|
|
||||||
* @param qname the qualified name of the association
|
|
||||||
*/
|
*/
|
||||||
public AssociationExistsException(NodeRef sourceRef, NodeRef targetRef, QName qname)
|
public AssociationExistsException(NodeRef sourceRef, NodeRef targetRef, QName qname)
|
||||||
{
|
{
|
||||||
@@ -45,6 +43,20 @@ public class AssociationExistsException extends RuntimeException
|
|||||||
this.qname = qname;
|
this.qname = qname;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param sourceRef the source of the association
|
||||||
|
* @param targetRef the target of the association
|
||||||
|
* @param qname the qualified name of the association
|
||||||
|
* @param cause a causal exception
|
||||||
|
*/
|
||||||
|
public AssociationExistsException(NodeRef sourceRef, NodeRef targetRef, QName qname, Throwable cause)
|
||||||
|
{
|
||||||
|
super(cause);
|
||||||
|
this.sourceRef = sourceRef;
|
||||||
|
this.targetRef = targetRef;
|
||||||
|
this.qname = qname;
|
||||||
|
}
|
||||||
|
|
||||||
public NodeRef getSourceRef()
|
public NodeRef getSourceRef()
|
||||||
{
|
{
|
||||||
return sourceRef;
|
return sourceRef;
|
||||||
|
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005 Alfresco, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Mozilla Public License version 1.1
|
||||||
|
* with a permitted attribution clause. You may obtain a
|
||||||
|
* copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.alfresco.org/legal/license.txt
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||||
|
* either express or implied. See the License for the specific
|
||||||
|
* language governing permissions and limitations under the
|
||||||
|
* License.
|
||||||
|
*/
|
||||||
|
package org.alfresco.service.cmr.repository;
|
||||||
|
|
||||||
|
import org.alfresco.i18n.I18NUtil;
|
||||||
|
import org.alfresco.service.namespace.QName;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when a child node <b>cm:name</b> property violates the data dictionary
|
||||||
|
* <b>duplicate</b> child association constraint.
|
||||||
|
*
|
||||||
|
* @author Derek Hulley
|
||||||
|
*/
|
||||||
|
public class DuplicateChildNodeNameException extends RuntimeException
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 5143099335847200453L;
|
||||||
|
|
||||||
|
private static final String ERR_DUPLICATE_NAME = "system.err.duplicate_name";
|
||||||
|
|
||||||
|
private NodeRef parentNodeRef;
|
||||||
|
private QName assocTypeQName;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public DuplicateChildNodeNameException(NodeRef parentNodeRef, QName assocTypeQName, String name)
|
||||||
|
{
|
||||||
|
super(I18NUtil.getMessage(ERR_DUPLICATE_NAME, name));
|
||||||
|
this.parentNodeRef = parentNodeRef;
|
||||||
|
this.assocTypeQName = assocTypeQName;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NodeRef getParentNodeRef()
|
||||||
|
{
|
||||||
|
return parentNodeRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QName getAssocTypeQName()
|
||||||
|
{
|
||||||
|
return assocTypeQName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName()
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
@@ -417,6 +417,21 @@ public interface NodeService
|
|||||||
QNamePattern qnamePattern)
|
QNamePattern qnamePattern)
|
||||||
throws InvalidNodeRefException;
|
throws InvalidNodeRefException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the node with the given name within the context of the parent node. The name
|
||||||
|
* is case-insensitive as Alfresco has to support case-insensitive clients as standard.
|
||||||
|
*
|
||||||
|
* @param nodeRef the parent node - usuall a <b>container</b>
|
||||||
|
* @param assocTypeQName the type of the association
|
||||||
|
* @param childName the name of the node as per the property <b>cm:name</b>
|
||||||
|
* @return Returns the child node or null if not found
|
||||||
|
*/
|
||||||
|
@Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef", "assocTypeQName", "childName"})
|
||||||
|
public NodeRef getChildByName(
|
||||||
|
NodeRef nodeRef,
|
||||||
|
QName assocTypeQName,
|
||||||
|
String childName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches the primary parent-child relationship.
|
* Fetches the primary parent-child relationship.
|
||||||
* <p>
|
* <p>
|
||||||
|
@@ -43,15 +43,15 @@
|
|||||||
<cm:folder>
|
<cm:folder>
|
||||||
<cm:name>L0- Folder C</cm:name>
|
<cm:name>L0- Folder C</cm:name>
|
||||||
<cm:contains>
|
<cm:contains>
|
||||||
<!-- L1 files -->
|
<!-- L1 files -->
|
||||||
<cm:content>
|
<cm:content>
|
||||||
<cm:name>DUPLICATE</cm:name>
|
<cm:name>CHECK_FILE</cm:name>
|
||||||
<cm:content>contentUrl=classpath:quick/quick.txt|mimetype=text/plain|size=|encoding=</cm:content>
|
<cm:content>contentUrl=classpath:quick/quick.txt|mimetype=text/plain|size=|encoding=</cm:content>
|
||||||
</cm:content>
|
</cm:content>
|
||||||
<!-- L2 folders -->
|
<!-- L2 folders -->
|
||||||
<cm:folder>
|
<cm:folder>
|
||||||
<cm:name>DUPLICATE</cm:name>
|
<cm:name>CHECK_FOLDER</cm:name>
|
||||||
</cm:folder>
|
</cm:folder>
|
||||||
</cm:contains>
|
</cm:contains>
|
||||||
</cm:folder>
|
</cm:folder>
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user