mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
Content and folder node archival
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2767 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -64,6 +64,17 @@
|
|||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<bean id="spacesArchiveBootstrap" parent="spacesArchiveStoreImporter">
|
||||||
|
<property name="bootstrapViews">
|
||||||
|
<list>
|
||||||
|
<props>
|
||||||
|
<prop key="path">/</prop>
|
||||||
|
<prop key="location">alfresco/bootstrap/spacesArchive.xml</prop>
|
||||||
|
</props>
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
<bean id="spacesBootstrap" parent="spacesStoreImporter">
|
<bean id="spacesBootstrap" parent="spacesStoreImporter">
|
||||||
<property name="bootstrapViews">
|
<property name="bootstrapViews">
|
||||||
<list>
|
<list>
|
||||||
|
16
config/alfresco/bootstrap/spacesArchive.xml
Normal file
16
config/alfresco/bootstrap/spacesArchive.xml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<view:view xmlns:view="http://www.alfresco.org/view/repository/1.0"
|
||||||
|
xmlns:cm="http://www.alfresco.org/model/content/1.0"
|
||||||
|
xmlns:sys="http://www.alfresco.org/model/system/1.0"
|
||||||
|
xmlns:app="http://www.alfresco.org/model/application/1.0">
|
||||||
|
|
||||||
|
<view:reference view:pathref="/">
|
||||||
|
<!-- Apply Read access to Everyone on root node of Spaces Store -->
|
||||||
|
<view:acl>
|
||||||
|
<view:ace view:access="ALLOWED">
|
||||||
|
<view:authority>GROUP_EVERYONE</view:authority>
|
||||||
|
<view:permission>Read</view:permission>
|
||||||
|
</view:ace>
|
||||||
|
</view:acl>
|
||||||
|
</view:reference>
|
||||||
|
|
||||||
|
</view:view>
|
@@ -186,6 +186,23 @@
|
|||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<bean id="spacesArchiveStoreImporter" parent="storeImporter" abstract="true">
|
||||||
|
<property name="storeUrl">
|
||||||
|
<value>${spaces.archive.store}</value>
|
||||||
|
</property>
|
||||||
|
<property name="mustNotExistStoreUrls">
|
||||||
|
<list>
|
||||||
|
<value>${spaces.archive.store}</value>
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
<!--
|
||||||
|
<property name="configuration">
|
||||||
|
<props>
|
||||||
|
</props>
|
||||||
|
</property>
|
||||||
|
-->
|
||||||
|
</bean>
|
||||||
|
|
||||||
<bean id="spacesStoreImporter" parent="storeImporter" abstract="true">
|
<bean id="spacesStoreImporter" parent="storeImporter" abstract="true">
|
||||||
<property name="storeUrl">
|
<property name="storeUrl">
|
||||||
<value>${spaces.store}</value>
|
<value>${spaces.store}</value>
|
||||||
|
@@ -44,6 +44,7 @@
|
|||||||
<type name="cm:folder">
|
<type name="cm:folder">
|
||||||
<title>Folder</title>
|
<title>Folder</title>
|
||||||
<parent>cm:cmobject</parent>
|
<parent>cm:cmobject</parent>
|
||||||
|
<archive>true</archive>
|
||||||
<properties>
|
<properties>
|
||||||
<property name="cm:orderedchildren">
|
<property name="cm:orderedchildren">
|
||||||
<type>d:boolean</type>
|
<type>d:boolean</type>
|
||||||
@@ -68,6 +69,7 @@
|
|||||||
<type name="cm:content">
|
<type name="cm:content">
|
||||||
<title>Content</title>
|
<title>Content</title>
|
||||||
<parent>cm:cmobject</parent>
|
<parent>cm:cmobject</parent>
|
||||||
|
<archive>true</archive>
|
||||||
<properties>
|
<properties>
|
||||||
<property name="cm:content">
|
<property name="cm:content">
|
||||||
<type>d:content</type>
|
<type>d:content</type>
|
||||||
|
@@ -78,6 +78,16 @@
|
|||||||
<java-class>org.alfresco.service.cmr.repository.NodeRef</java-class>
|
<java-class>org.alfresco.service.cmr.repository.NodeRef</java-class>
|
||||||
</data-type>
|
</data-type>
|
||||||
|
|
||||||
|
<data-type name="d:childassocref">
|
||||||
|
<analyser-class>org.apache.lucene.analysis.standard.StandardAnalyzer</analyser-class>
|
||||||
|
<java-class>org.alfresco.service.cmr.repository.ChildAssociationRef</java-class>
|
||||||
|
</data-type>
|
||||||
|
|
||||||
|
<data-type name="d:assocref">
|
||||||
|
<analyser-class>org.apache.lucene.analysis.standard.StandardAnalyzer</analyser-class>
|
||||||
|
<java-class>org.alfresco.service.cmr.repository.AssociationRef</java-class>
|
||||||
|
</data-type>
|
||||||
|
|
||||||
<data-type name="d:path">
|
<data-type name="d:path">
|
||||||
<analyser-class>org.apache.lucene.analysis.standard.StandardAnalyzer</analyser-class>
|
<analyser-class>org.apache.lucene.analysis.standard.StandardAnalyzer</analyser-class>
|
||||||
<java-class>org.alfresco.service.cmr.repository.Path</java-class>
|
<java-class>org.alfresco.service.cmr.repository.Path</java-class>
|
||||||
|
@@ -123,6 +123,53 @@
|
|||||||
<title>Incomplete</title>
|
<title>Incomplete</title>
|
||||||
</aspect>
|
</aspect>
|
||||||
|
|
||||||
|
<!-- details stored on archived nodes -->
|
||||||
|
<aspect name="sys:archived">
|
||||||
|
<title>Archived</title>
|
||||||
|
<properties>
|
||||||
|
<property name="sys:archivedOriginalParent">
|
||||||
|
<type>d:noderef</type>
|
||||||
|
<mandatory>true</mandatory>
|
||||||
|
</property>
|
||||||
|
<property name="sys:archivedBy">
|
||||||
|
<type>d:text</type>
|
||||||
|
<mandatory>true</mandatory>
|
||||||
|
</property>
|
||||||
|
<property name="sys:archivedDate">
|
||||||
|
<type>d:datetime</type>
|
||||||
|
<mandatory>true</mandatory>
|
||||||
|
</property>
|
||||||
|
</properties>
|
||||||
|
</aspect>
|
||||||
|
<aspect name="sys:archived-assocs">
|
||||||
|
<properties>
|
||||||
|
<property name="sys:archivedParentAssocs">
|
||||||
|
<type>d:childassocref</type>
|
||||||
|
<mandatory>false</mandatory>
|
||||||
|
<multiple>true</multiple>
|
||||||
|
<index enabled="false" />
|
||||||
|
</property>
|
||||||
|
<property name="sys:archivedChildAssocs">
|
||||||
|
<type>d:childassocref</type>
|
||||||
|
<mandatory>false</mandatory>
|
||||||
|
<multiple>true</multiple>
|
||||||
|
<index enabled="false" />
|
||||||
|
</property>
|
||||||
|
<property name="sys:archivedSourceAssocs">
|
||||||
|
<type>d:assocref</type>
|
||||||
|
<mandatory>false</mandatory>
|
||||||
|
<multiple>true</multiple>
|
||||||
|
<index enabled="false" />
|
||||||
|
</property>
|
||||||
|
<property name="sys:archivedTargetAssocs">
|
||||||
|
<type>d:assocref</type>
|
||||||
|
<mandatory>false</mandatory>
|
||||||
|
<multiple>true</multiple>
|
||||||
|
<index enabled="false" />
|
||||||
|
</property>
|
||||||
|
</properties>
|
||||||
|
</aspect>
|
||||||
|
|
||||||
</aspects>
|
</aspects>
|
||||||
|
|
||||||
</model>
|
</model>
|
@@ -19,17 +19,29 @@
|
|||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<!-- Map stores to archive stores -->
|
||||||
|
<bean id="storeArchiveMap" class="org.alfresco.repo.node.StoreArchiveMap">
|
||||||
|
<property name="archiveMap">
|
||||||
|
<map>
|
||||||
|
<entry key="workspace://SpacesStore"><value>archive://SpacesStore</value></entry>
|
||||||
|
</map>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
<!-- NodeService implemented to persist to Database -->
|
<!-- NodeService implemented to persist to Database -->
|
||||||
<bean id="dbNodeService" class="org.alfresco.repo.node.db.DbNodeServiceImpl" init-method="init" >
|
<bean id="dbNodeService" class="org.alfresco.repo.node.db.DbNodeServiceImpl" init-method="init" >
|
||||||
<constructor-arg>
|
<property name="dictionaryService">
|
||||||
<ref bean="policyComponent"/>
|
|
||||||
</constructor-arg>
|
|
||||||
<constructor-arg>
|
|
||||||
<ref bean="dictionaryService" />
|
<ref bean="dictionaryService" />
|
||||||
</constructor-arg>
|
</property>
|
||||||
<constructor-arg>
|
<property name="nodeDaoService">
|
||||||
<ref bean="nodeDaoService" />
|
<ref bean="nodeDaoService" />
|
||||||
</constructor-arg>
|
</property>
|
||||||
|
<property name="policyComponent">
|
||||||
|
<ref bean="policyComponent"/>
|
||||||
|
</property>
|
||||||
|
<property name="storeArchiveMap">
|
||||||
|
<ref bean="storeArchiveMap"/>
|
||||||
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<!-- Handles policy callbacks to ensure that node hierarchy gets indexed -->
|
<!-- Handles policy callbacks to ensure that node hierarchy gets indexed -->
|
||||||
|
@@ -80,6 +80,9 @@ alfresco_user_store.system_container.childname=sys:system
|
|||||||
alfresco_user_store.user_container.childname=sys:people
|
alfresco_user_store.user_container.childname=sys:people
|
||||||
alfresco_user_store.authorities_container.childname=sys:authorities
|
alfresco_user_store.authorities_container.childname=sys:authorities
|
||||||
|
|
||||||
|
# Spaces Archive Configuration
|
||||||
|
spaces.archive.store=archive://SpacesStore
|
||||||
|
|
||||||
# Spaces Configuration
|
# Spaces Configuration
|
||||||
|
|
||||||
spaces.store=workspace://SpacesStore
|
spaces.store=workspace://SpacesStore
|
||||||
|
@@ -39,6 +39,17 @@ public interface ContentModel
|
|||||||
// tag for incomplete nodes
|
// tag for incomplete nodes
|
||||||
static final QName ASPECT_INCOMPLETE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "incomplete");
|
static final QName ASPECT_INCOMPLETE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "incomplete");
|
||||||
|
|
||||||
|
// archived nodes aspect constants
|
||||||
|
static final QName ASPECT_ARCHIVED = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archived");
|
||||||
|
static final QName PROP_ARCHIVED_ORIGINAL_PARENT = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedOriginalParent");
|
||||||
|
static final QName PROP_ARCHIVED_BY = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedBy");
|
||||||
|
static final QName PROP_ARCHIVED_DATE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedDate");
|
||||||
|
static final QName ASPECT_ARCHIVED_ASSOCS = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archived-assocs");
|
||||||
|
static final QName PROP_ARCHIVED_PARENT_ASSOCS = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedParentAssocs");
|
||||||
|
static final QName PROP_ARCHIVED_CHILD_ASSOCS = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedChildAssocs");
|
||||||
|
static final QName PROP_ARCHIVED_SOURCE_ASSOCS = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedSourceAssocs");
|
||||||
|
static final QName PROP_ARCHIVED_TARGET_ASSOCS = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedTargetAssocs");
|
||||||
|
|
||||||
// referenceable aspect constants
|
// referenceable aspect constants
|
||||||
static final QName TYPE_REFERENCE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "reference");
|
static final QName TYPE_REFERENCE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "reference");
|
||||||
static final QName PROP_REFERENCE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "reference");
|
static final QName PROP_REFERENCE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "reference");
|
||||||
@@ -60,8 +71,6 @@ public interface ContentModel
|
|||||||
static final QName PROP_SYS_VERSION_SCHEMA = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionSchema");
|
static final QName PROP_SYS_VERSION_SCHEMA = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionSchema");
|
||||||
static final QName PROP_SYS_VERSION_EDITION = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionEdition");
|
static final QName PROP_SYS_VERSION_EDITION = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionEdition");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Content Model Definitions
|
// Content Model Definitions
|
||||||
//
|
//
|
||||||
|
@@ -146,6 +146,17 @@ public class DictionaryDAOTest extends TestCase
|
|||||||
assertTrue("Expected type REGEX constraint", constraint instanceof RegexConstraint);
|
assertTrue("Expected type REGEX constraint", constraint instanceof RegexConstraint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testArchive()
|
||||||
|
{
|
||||||
|
QName testFileQName = QName.createQName(TEST_URL, "file");
|
||||||
|
ClassDefinition fileClassDef = service.getClass(testFileQName);
|
||||||
|
assertTrue("File type should have the archive flag", fileClassDef.isArchive());
|
||||||
|
|
||||||
|
QName testFolderQName = QName.createQName(TEST_URL, "folder");
|
||||||
|
ClassDefinition folderClassDef = service.getClass(testFolderQName);
|
||||||
|
assertFalse("Folder type should not have the archive flag", folderClassDef.isArchive());
|
||||||
|
}
|
||||||
|
|
||||||
public void testMandatoryEnforced()
|
public void testMandatoryEnforced()
|
||||||
{
|
{
|
||||||
// get the properties for the test type
|
// get the properties for the test type
|
||||||
|
@@ -130,6 +130,10 @@ import org.alfresco.service.namespace.QName;
|
|||||||
return type.isAspect();
|
return type.isAspect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isArchive()
|
||||||
|
{
|
||||||
|
return type.isArchive();
|
||||||
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see org.alfresco.repo.dictionary.ClassDefinition#getProperties()
|
* @see org.alfresco.repo.dictionary.ClassDefinition#getProperties()
|
||||||
|
@@ -32,6 +32,7 @@ public abstract class M2Class
|
|||||||
private String title = null;
|
private String title = null;
|
||||||
private String description = null;
|
private String description = null;
|
||||||
private String parentName = null;
|
private String parentName = null;
|
||||||
|
private boolean archive = false;
|
||||||
|
|
||||||
private List<M2Property> properties = new ArrayList<M2Property>();
|
private List<M2Property> properties = new ArrayList<M2Property>();
|
||||||
private List<M2PropertyOverride> propertyOverrides = new ArrayList<M2PropertyOverride>();
|
private List<M2PropertyOverride> propertyOverrides = new ArrayList<M2PropertyOverride>();
|
||||||
@@ -91,6 +92,16 @@ public abstract class M2Class
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isArchive()
|
||||||
|
{
|
||||||
|
return archive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setArchive(boolean archive)
|
||||||
|
{
|
||||||
|
this.archive = archive;
|
||||||
|
}
|
||||||
|
|
||||||
public M2Property createProperty(String name)
|
public M2Property createProperty(String name)
|
||||||
{
|
{
|
||||||
M2Property property = new M2Property();
|
M2Property property = new M2Property();
|
||||||
|
@@ -46,6 +46,7 @@ import org.alfresco.service.namespace.QName;
|
|||||||
protected M2Class m2Class;
|
protected M2Class m2Class;
|
||||||
protected QName name;
|
protected QName name;
|
||||||
protected QName parentName = null;
|
protected QName parentName = null;
|
||||||
|
protected boolean archive = false;
|
||||||
|
|
||||||
private Map<QName, M2PropertyOverride> propertyOverrides = new HashMap<QName, M2PropertyOverride>();
|
private Map<QName, M2PropertyOverride> propertyOverrides = new HashMap<QName, M2PropertyOverride>();
|
||||||
private Map<QName, PropertyDefinition> properties = new HashMap<QName, PropertyDefinition>();
|
private Map<QName, PropertyDefinition> properties = new HashMap<QName, PropertyDefinition>();
|
||||||
@@ -73,6 +74,7 @@ import org.alfresco.service.namespace.QName;
|
|||||||
|
|
||||||
// Resolve Names
|
// Resolve Names
|
||||||
this.name = QName.createQName(m2Class.getName(), resolver);
|
this.name = QName.createQName(m2Class.getName(), resolver);
|
||||||
|
this.archive = m2Class.isArchive();
|
||||||
if (m2Class.getParentName() != null && m2Class.getParentName().length() > 0)
|
if (m2Class.getParentName() != null && m2Class.getParentName().length() > 0)
|
||||||
{
|
{
|
||||||
this.parentName = QName.createQName(m2Class.getParentName(), resolver);
|
this.parentName = QName.createQName(m2Class.getParentName(), resolver);
|
||||||
@@ -317,6 +319,14 @@ import org.alfresco.service.namespace.QName;
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see org.alfresco.repo.dictionary.ClassDefinition#getParentName()
|
||||||
|
*/
|
||||||
|
public QName getParentName()
|
||||||
|
{
|
||||||
|
return parentName;
|
||||||
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see org.alfresco.repo.dictionary.ClassDefinition#isAspect()
|
* @see org.alfresco.repo.dictionary.ClassDefinition#isAspect()
|
||||||
*/
|
*/
|
||||||
@@ -325,12 +335,9 @@ import org.alfresco.service.namespace.QName;
|
|||||||
return (m2Class instanceof M2Aspect);
|
return (m2Class instanceof M2Aspect);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
public boolean isArchive()
|
||||||
* @see org.alfresco.repo.dictionary.ClassDefinition#getParentName()
|
|
||||||
*/
|
|
||||||
public QName getParentName()
|
|
||||||
{
|
{
|
||||||
return parentName;
|
return archive;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
|
@@ -110,6 +110,7 @@
|
|||||||
|
|
||||||
<type name="test:file">
|
<type name="test:file">
|
||||||
<parent>test:base</parent>
|
<parent>test:base</parent>
|
||||||
|
<archive>true</archive>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<property name="test:fileprop">
|
<property name="test:fileprop">
|
||||||
|
@@ -60,6 +60,7 @@
|
|||||||
<value name="title" field="title" usage="optional"/>
|
<value name="title" field="title" usage="optional"/>
|
||||||
<value name="description" field="description" usage="optional"/>
|
<value name="description" field="description" usage="optional"/>
|
||||||
<value name="parent" field="parentName" usage="optional"/>
|
<value name="parent" field="parentName" usage="optional"/>
|
||||||
|
<value name="archive" field="archive" usage="optional"/>
|
||||||
|
|
||||||
<structure name="properties" usage="optional">
|
<structure name="properties" usage="optional">
|
||||||
<collection field="properties" item-type="org.alfresco.repo.dictionary.M2Property" factory="org.alfresco.repo.dictionary.M2Model.createList"/>
|
<collection field="properties" item-type="org.alfresco.repo.dictionary.M2Property" factory="org.alfresco.repo.dictionary.M2Model.createList"/>
|
||||||
|
@@ -25,6 +25,8 @@ import java.util.Map;
|
|||||||
|
|
||||||
import org.alfresco.error.AlfrescoRuntimeException;
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
||||||
|
import org.alfresco.service.cmr.repository.AssociationRef;
|
||||||
|
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.NodeRef;
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
import org.alfresco.service.cmr.repository.Path;
|
import org.alfresco.service.cmr.repository.Path;
|
||||||
@@ -183,6 +185,34 @@ public class PropertyValue implements Cloneable, Serializable
|
|||||||
return DefaultTypeConverter.INSTANCE.convert(NodeRef.class, value);
|
return DefaultTypeConverter.INSTANCE.convert(NodeRef.class, value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
CHILD_ASSOC_REF
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
protected ValueType getPersistedType(Serializable value)
|
||||||
|
{
|
||||||
|
return ValueType.STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Serializable convert(Serializable value)
|
||||||
|
{
|
||||||
|
return DefaultTypeConverter.INSTANCE.convert(ChildAssociationRef.class, value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ASSOC_REF
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
protected ValueType getPersistedType(Serializable value)
|
||||||
|
{
|
||||||
|
return ValueType.STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Serializable convert(Serializable value)
|
||||||
|
{
|
||||||
|
return DefaultTypeConverter.INSTANCE.convert(AssociationRef.class, value);
|
||||||
|
}
|
||||||
|
},
|
||||||
QNAME
|
QNAME
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
@@ -296,6 +326,14 @@ public class PropertyValue implements Cloneable, Serializable
|
|||||||
{
|
{
|
||||||
return ValueType.NODEREF;
|
return ValueType.NODEREF;
|
||||||
}
|
}
|
||||||
|
else if (value instanceof ChildAssociationRef)
|
||||||
|
{
|
||||||
|
return ValueType.CHILD_ASSOC_REF;
|
||||||
|
}
|
||||||
|
else if (value instanceof AssociationRef)
|
||||||
|
{
|
||||||
|
return ValueType.ASSOC_REF;
|
||||||
|
}
|
||||||
else if (value instanceof QName)
|
else if (value instanceof QName)
|
||||||
{
|
{
|
||||||
return ValueType.QNAME;
|
return ValueType.QNAME;
|
||||||
@@ -328,6 +366,8 @@ public class PropertyValue implements Cloneable, Serializable
|
|||||||
valueTypesByPropertyType.put(DataTypeDefinition.CONTENT, ValueType.CONTENT);
|
valueTypesByPropertyType.put(DataTypeDefinition.CONTENT, ValueType.CONTENT);
|
||||||
valueTypesByPropertyType.put(DataTypeDefinition.TEXT, ValueType.STRING);
|
valueTypesByPropertyType.put(DataTypeDefinition.TEXT, ValueType.STRING);
|
||||||
valueTypesByPropertyType.put(DataTypeDefinition.NODE_REF, ValueType.NODEREF);
|
valueTypesByPropertyType.put(DataTypeDefinition.NODE_REF, ValueType.NODEREF);
|
||||||
|
valueTypesByPropertyType.put(DataTypeDefinition.CHILD_ASSOC_REF, ValueType.CHILD_ASSOC_REF);
|
||||||
|
valueTypesByPropertyType.put(DataTypeDefinition.ASSOC_REF, ValueType.ASSOC_REF);
|
||||||
valueTypesByPropertyType.put(DataTypeDefinition.PATH, ValueType.PATH);
|
valueTypesByPropertyType.put(DataTypeDefinition.PATH, ValueType.PATH);
|
||||||
valueTypesByPropertyType.put(DataTypeDefinition.QNAME, ValueType.QNAME);
|
valueTypesByPropertyType.put(DataTypeDefinition.QNAME, ValueType.QNAME);
|
||||||
}
|
}
|
||||||
|
@@ -30,7 +30,6 @@ 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.NodeAssoc;
|
||||||
import org.alfresco.repo.domain.NodeStatus;
|
|
||||||
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;
|
||||||
|
@@ -113,13 +113,15 @@ public abstract class AbstractNodeServiceImpl implements NodeService
|
|||||||
private AssociationPolicyDelegate<OnDeleteAssociationPolicy> onDeleteAssociationDelegate;
|
private AssociationPolicyDelegate<OnDeleteAssociationPolicy> onDeleteAssociationDelegate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param policyComponent the component with which to register class policies and behaviour
|
*
|
||||||
* @param dictionaryService
|
|
||||||
* used to check that node operations conform to the model
|
|
||||||
*/
|
*/
|
||||||
protected AbstractNodeServiceImpl(PolicyComponent policyComponent)
|
protected AbstractNodeServiceImpl()
|
||||||
{
|
{
|
||||||
this.uuid = GUID.generate();
|
this.uuid = GUID.generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPolicyComponent(PolicyComponent policyComponent)
|
||||||
|
{
|
||||||
this.policyComponent = policyComponent;
|
this.policyComponent = policyComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -373,16 +373,21 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int countNodesById(NodeRef nodeRef)
|
private int countNodesByReference(NodeRef nodeRef)
|
||||||
{
|
{
|
||||||
String query =
|
String query =
|
||||||
"select count(node.uuid)" +
|
"select count(node.uuid)" +
|
||||||
" from " +
|
" from " +
|
||||||
NodeImpl.class.getName() + " node" +
|
NodeImpl.class.getName() + " node" +
|
||||||
" where node.uuid = ?";
|
" 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)
|
||||||
.setString(0, nodeRef.getId())
|
.setString(0, nodeRef.getId())
|
||||||
|
.setString(1, nodeRef.getStoreRef().getProtocol())
|
||||||
|
.setString(2, nodeRef.getStoreRef().getIdentifier())
|
||||||
.list();
|
.list();
|
||||||
Integer count = (Integer) results.get(0);
|
Integer count = (Integer) results.get(0);
|
||||||
return count.intValue();
|
return count.intValue();
|
||||||
@@ -591,7 +596,7 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
|
|||||||
ContentModel.TYPE_CONTAINER);
|
ContentModel.TYPE_CONTAINER);
|
||||||
NodeRef nodeRef = assocRef.getChildRef();
|
NodeRef nodeRef = assocRef.getChildRef();
|
||||||
// count the nodes with the given id
|
// count the nodes with the given id
|
||||||
int count = countNodesById(nodeRef);
|
int count = countNodesByReference(nodeRef);
|
||||||
assertEquals("Unexpected number of nodes present", 1, count);
|
assertEquals("Unexpected number of nodes present", 1, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -693,11 +698,11 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
|
|||||||
|
|
||||||
// delete n1
|
// delete n1
|
||||||
nodeService.deleteNode(n1Ref);
|
nodeService.deleteNode(n1Ref);
|
||||||
assertEquals("Node not directly deleted", 0, countNodesById(n1Ref));
|
assertEquals("Node not directly deleted", 0, countNodesByReference(n1Ref));
|
||||||
assertEquals("Node not cascade deleted", 0, countNodesById(n3Ref));
|
assertEquals("Node not cascade deleted", 0, countNodesByReference(n3Ref));
|
||||||
assertEquals("Node incorrectly cascade deleted", 1, countNodesById(n4Ref));
|
assertEquals("Node incorrectly cascade deleted", 1, countNodesByReference(n4Ref));
|
||||||
assertEquals("Node not cascade deleted", 0, countNodesById(n6Ref));
|
assertEquals("Node not cascade deleted", 0, countNodesByReference(n6Ref));
|
||||||
assertEquals("Node not cascade deleted", 0, countNodesById(n8Ref));
|
assertEquals("Node not cascade deleted", 0, countNodesByReference(n8Ref));
|
||||||
|
|
||||||
// commit to check
|
// commit to check
|
||||||
setComplete();
|
setComplete();
|
||||||
|
65
source/java/org/alfresco/repo/node/StoreArchiveMap.java
Normal file
65
source/java/org/alfresco/repo/node/StoreArchiveMap.java
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005 Alfresco, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Mozilla Public License version 1.1
|
||||||
|
* with a permitted attribution clause. You may obtain a
|
||||||
|
* copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.alfresco.org/legal/license.txt
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||||
|
* either express or implied. See the License for the specific
|
||||||
|
* language governing permissions and limitations under the
|
||||||
|
* License.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.node;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
|
import org.alfresco.service.cmr.repository.StoreRef;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A map component that maps <b>node stores</b> to their archive <b>stores</b>.
|
||||||
|
*
|
||||||
|
* @author Derek Hulley
|
||||||
|
*/
|
||||||
|
public class StoreArchiveMap
|
||||||
|
{
|
||||||
|
private Map<StoreRef, StoreRef> storeArchiveMap;
|
||||||
|
|
||||||
|
public StoreArchiveMap()
|
||||||
|
{
|
||||||
|
storeArchiveMap = new HashMap<StoreRef, StoreRef>(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<StoreRef, StoreRef> getArchiveMap()
|
||||||
|
{
|
||||||
|
return storeArchiveMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setArchiveMap(Map<String, String> archiveMap)
|
||||||
|
{
|
||||||
|
// translate all the entries to references
|
||||||
|
for (Map.Entry<String, String> entry : archiveMap.entrySet())
|
||||||
|
{
|
||||||
|
String storeRefKeyStr = entry.getKey();
|
||||||
|
String storeRefValueStr = entry.getValue();
|
||||||
|
StoreRef storeRefKey = null;
|
||||||
|
StoreRef storeRefValue = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
storeRefKey = new StoreRef(storeRefKeyStr);
|
||||||
|
storeRefValue = new StoreRef(storeRefValueStr);
|
||||||
|
}
|
||||||
|
catch (Throwable e)
|
||||||
|
{
|
||||||
|
throw new AlfrescoRuntimeException("Unable create store references from map entry: " + entry);
|
||||||
|
}
|
||||||
|
storeArchiveMap.put(storeRefKey, storeRefValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -20,6 +20,7 @@ import java.io.Serializable;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -35,7 +36,9 @@ import org.alfresco.repo.domain.NodeStatus;
|
|||||||
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.repo.node.AbstractNodeServiceImpl;
|
import org.alfresco.repo.node.AbstractNodeServiceImpl;
|
||||||
import org.alfresco.repo.policy.PolicyComponent;
|
import org.alfresco.repo.node.StoreArchiveMap;
|
||||||
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||||
|
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
||||||
import org.alfresco.service.cmr.dictionary.AspectDefinition;
|
import org.alfresco.service.cmr.dictionary.AspectDefinition;
|
||||||
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;
|
||||||
@@ -56,8 +59,11 @@ import org.alfresco.service.cmr.repository.Path;
|
|||||||
import org.alfresco.service.cmr.repository.StoreExistsException;
|
import org.alfresco.service.cmr.repository.StoreExistsException;
|
||||||
import org.alfresco.service.cmr.repository.StoreRef;
|
import org.alfresco.service.cmr.repository.StoreRef;
|
||||||
import org.alfresco.service.cmr.repository.NodeRef.Status;
|
import org.alfresco.service.cmr.repository.NodeRef.Status;
|
||||||
|
import org.alfresco.service.namespace.NamespaceService;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
import org.alfresco.service.namespace.QNamePattern;
|
import org.alfresco.service.namespace.QNamePattern;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,20 +73,31 @@ import org.springframework.util.Assert;
|
|||||||
*/
|
*/
|
||||||
public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||||
{
|
{
|
||||||
private final DictionaryService dictionaryService;
|
private static Log logger = LogFactory.getLog(DbNodeServiceImpl.class);
|
||||||
private final NodeDaoService nodeDaoService;
|
|
||||||
|
|
||||||
public DbNodeServiceImpl(
|
private DictionaryService dictionaryService;
|
||||||
PolicyComponent policyComponent,
|
private NodeDaoService nodeDaoService;
|
||||||
DictionaryService dictionaryService,
|
private StoreArchiveMap storeArchiveMap;
|
||||||
NodeDaoService nodeDaoService)
|
|
||||||
|
public DbNodeServiceImpl()
|
||||||
{
|
{
|
||||||
super(policyComponent);
|
}
|
||||||
|
|
||||||
|
public void setDictionaryService(DictionaryService dictionaryService)
|
||||||
|
{
|
||||||
this.dictionaryService = dictionaryService;
|
this.dictionaryService = dictionaryService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNodeDaoService(NodeDaoService nodeDaoService)
|
||||||
|
{
|
||||||
this.nodeDaoService = nodeDaoService;
|
this.nodeDaoService = nodeDaoService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setStoreArchiveMap(StoreArchiveMap storeArchiveMap)
|
||||||
|
{
|
||||||
|
this.storeArchiveMap = storeArchiveMap;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs a null-safe get of the node
|
* Performs a null-safe get of the node
|
||||||
*
|
*
|
||||||
@@ -98,6 +115,22 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
return unchecked;
|
return unchecked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a null-safe get of the store
|
||||||
|
* @param storeRef the store to retrieve
|
||||||
|
* @return Returns the store entity (never null)
|
||||||
|
* @throws InvalidStoreRefException if the referenced store could not be found
|
||||||
|
*/
|
||||||
|
private Store getStoreNotNull(StoreRef storeRef) throws InvalidStoreRefException
|
||||||
|
{
|
||||||
|
Store unchecked = nodeDaoService.getStore(storeRef.getProtocol(), storeRef.getIdentifier());
|
||||||
|
if (unchecked == null)
|
||||||
|
{
|
||||||
|
throw new InvalidStoreRefException("Store does not exist: " + storeRef, storeRef);
|
||||||
|
}
|
||||||
|
return unchecked;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean exists(StoreRef storeRef)
|
public boolean exists(StoreRef storeRef)
|
||||||
{
|
{
|
||||||
Store store = nodeDaoService.getStore(storeRef.getProtocol(), storeRef.getIdentifier());
|
Store store = nodeDaoService.getStore(storeRef.getProtocol(), storeRef.getIdentifier());
|
||||||
@@ -116,7 +149,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
|
|
||||||
public Status getNodeStatus(NodeRef nodeRef)
|
public Status getNodeStatus(NodeRef nodeRef)
|
||||||
{
|
{
|
||||||
NodeStatus nodeStatus = nodeDaoService.getNodeStatus(nodeRef);
|
NodeStatus nodeStatus = nodeDaoService.getNodeStatus(nodeRef, false);
|
||||||
if (nodeStatus == null) // node never existed
|
if (nodeStatus == null) // node never existed
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
@@ -616,101 +649,229 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
// get type and aspect QNames as they will be unavailable after the delete
|
// get type and aspect QNames as they will be unavailable after the delete
|
||||||
QName nodeTypeQName = node.getTypeQName();
|
QName nodeTypeQName = node.getTypeQName();
|
||||||
Set<QName> nodeAspectQNames = node.getAspects();
|
Set<QName> nodeAspectQNames = node.getAspects();
|
||||||
// delete it
|
|
||||||
|
// check if we need to archive the node
|
||||||
|
StoreRef storeRef = nodeRef.getStoreRef();
|
||||||
|
StoreRef archiveStoreRef = storeArchiveMap.getArchiveMap().get(storeRef);
|
||||||
|
// get the type and check if we need archiving
|
||||||
|
TypeDefinition typeDef = dictionaryService.getType(node.getTypeQName());
|
||||||
|
if (typeDef == null || !typeDef.isArchive() || archiveStoreRef == null)
|
||||||
|
{
|
||||||
|
// perform a normal deletion
|
||||||
nodeDaoService.deleteNode(node, true);
|
nodeDaoService.deleteNode(node, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// archive it
|
||||||
|
archiveNode(nodeRef, archiveStoreRef);
|
||||||
|
}
|
||||||
|
|
||||||
// Invoke policy behaviours
|
// Invoke policy behaviours
|
||||||
invokeOnDeleteNode(childAssocRef, nodeTypeQName, nodeAspectQNames);
|
invokeOnDeleteNode(childAssocRef, nodeTypeQName, nodeAspectQNames);
|
||||||
}
|
}
|
||||||
// /**
|
|
||||||
// * Recursive method to ensure cascade-deletion works with full invocation of policy behaviours.
|
private void archiveNode(NodeRef nodeRef, StoreRef archiveStoreRef)
|
||||||
// * <p>
|
{
|
||||||
// * The recursion will first cascade down primary associations before deleting all regular and
|
Node node = getNodeNotNull(nodeRef);
|
||||||
// * child associations to and from it. After this, the node itself is deleted. This bottom-up
|
Store archiveStore = getStoreNotNull(archiveStoreRef);
|
||||||
// * behaviour ensures that the policy invocation behaviour, which currently relies on being able
|
ChildAssoc primaryParentAssoc = nodeDaoService.getPrimaryParentAssoc(node);
|
||||||
// * to inspect association source types, gets fired correctly.
|
|
||||||
// */
|
// add the aspect
|
||||||
// public void deleteNode(NodeRef nodeRef)
|
node.getAspects().add(ContentModel.ASPECT_ARCHIVED);
|
||||||
// {
|
Map<QName, PropertyValue> properties = node.getProperties();
|
||||||
// // Invoke policy behaviours
|
PropertyValue archivedByProperty = makePropertyValue(
|
||||||
// invokeBeforeDeleteNode(nodeRef);
|
dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_BY),
|
||||||
//
|
AuthenticationUtil.getCurrentUserName());
|
||||||
// // get the node
|
properties.put(ContentModel.PROP_ARCHIVED_BY, archivedByProperty);
|
||||||
// Node node = getNodeNotNull(nodeRef);
|
PropertyValue archivedDateProperty = makePropertyValue(
|
||||||
//
|
dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_DATE),
|
||||||
// // get node info (for invocation purposes) before any deletions occur
|
new Date());
|
||||||
// // get the primary parent-child relationship before it is gone
|
properties.put(ContentModel.PROP_ARCHIVED_DATE, archivedDateProperty);
|
||||||
// ChildAssociationRef primaryParentAssocRef = getPrimaryParent(nodeRef);
|
PropertyValue archivedPrimaryParentNodeRefProperty = makePropertyValue(
|
||||||
// // get type and aspect QNames as they will be unavailable after the delete
|
dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT),
|
||||||
// QName nodeTypeQName = node.getTypeQName();
|
primaryParentAssoc.getParent().getNodeRef());
|
||||||
// Set<QName> nodeAspectQNames = node.getAspects();
|
properties.put(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT, archivedPrimaryParentNodeRefProperty);
|
||||||
//
|
|
||||||
// // get all associations, forcing a load of the collections
|
// get the IDs of all the node's primary children, including its own
|
||||||
// Collection<ChildAssoc> childAssocs = new ArrayList<ChildAssoc>(node.getChildAssocs());
|
Map<Long, Node> nodesById = new HashMap<Long, Node>(29);
|
||||||
// Collection<ChildAssoc> parentAssocs = new ArrayList<ChildAssoc>(node.getParentAssocs());
|
getPrimaryChildren(node, nodesById);
|
||||||
// Collection<NodeAssoc> sourceAssocs = new ArrayList<NodeAssoc>(node.getSourceNodeAssocs());
|
|
||||||
// Collection<NodeAssoc> targetAssocs = new ArrayList<NodeAssoc>(node.getTargetNodeAssocs());
|
// move each node into the archive store
|
||||||
//
|
for (Node nodeToMove : nodesById.values())
|
||||||
// // remove all child associations, including the primary one
|
{
|
||||||
// for (ChildAssoc childAssoc : childAssocs)
|
NodeRef oldNodeRef = nodeToMove.getNodeRef();
|
||||||
// {
|
nodeToMove.setStore(archiveStore);
|
||||||
// ChildAssociationRef childAssocRef = childAssoc.getChildAssocRef();
|
NodeRef newNodeRef = nodeToMove.getNodeRef();
|
||||||
// // cascade into primary associations
|
|
||||||
// if (childAssoc.getIsPrimary())
|
// update change statuses
|
||||||
// {
|
String txnId = AlfrescoTransactionSupport.getTransactionId();
|
||||||
// NodeRef childNodeRef = childAssocRef.getChildRef();
|
NodeStatus oldNodeStatus = nodeDaoService.getNodeStatus(oldNodeRef, true);
|
||||||
// this.deleteNode(childNodeRef);
|
oldNodeStatus.setNode(null);
|
||||||
// }
|
oldNodeStatus.setChangeTxnId(txnId);
|
||||||
//
|
NodeStatus newNodeStatus = nodeDaoService.getNodeStatus(newNodeRef, true);
|
||||||
// // one or more of these associations may have been dealt with when deleting the
|
newNodeStatus.setNode(nodeToMove);
|
||||||
// // child, so check that the association is valid
|
newNodeStatus.setChangeTxnId(txnId);
|
||||||
//
|
}
|
||||||
// // invoke pre-deletion behaviour
|
|
||||||
// invokeBeforeDeleteChildAssociation(childAssocRef);
|
// archive all the associations between the archived nodes and non-archived nodes
|
||||||
// // remove it - cascade just to be sure
|
for (Node nodeToArchive : nodesById.values())
|
||||||
// nodeDaoService.deleteChildAssoc(childAssoc, true);
|
{
|
||||||
// // invoke post-deletion behaviour
|
archiveAssocs(nodeToArchive, nodesById);
|
||||||
// invokeOnDeleteChildAssociation(childAssocRef);
|
}
|
||||||
// }
|
|
||||||
// // remove all parent associations, including the primary one
|
// the node reference has changed due to the store move
|
||||||
// for (ChildAssoc parentAssoc : parentAssocs)
|
nodeRef = node.getNodeRef();
|
||||||
// {
|
|
||||||
// ChildAssociationRef parentAssocRef = parentAssoc.getChildAssocRef();
|
// now associate the top-level node with the root of the new store
|
||||||
// // invoke pre-deletion behaviour
|
NodeRef archiveStoreRootNodeRef = getRootNode(archiveStoreRef);
|
||||||
// invokeBeforeDeleteChildAssociation(parentAssocRef);
|
Node archiveStoreRootNode = getNodeNotNull(archiveStoreRootNodeRef);
|
||||||
// // remove it - don't cascade as this is a parent assoc
|
QName assocTypeQName = ContentModel.ASSOC_CHILDREN;
|
||||||
// nodeDaoService.deleteChildAssoc(parentAssoc, false);
|
QName assocQName = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedItem");
|
||||||
// // invoke post-deletion behaviour
|
|
||||||
// invokeOnDeleteChildAssociation(parentAssocRef);
|
invokeBeforeCreateChildAssociation(archiveStoreRootNodeRef, nodeRef, assocTypeQName, assocQName);
|
||||||
// }
|
invokeBeforeUpdateNode(archiveStoreRootNodeRef);
|
||||||
// // remove all source node associations
|
|
||||||
// for (NodeAssoc sourceAssoc : sourceAssocs)
|
// create a new assoc
|
||||||
// {
|
ChildAssoc newAssoc = nodeDaoService.newChildAssoc(archiveStoreRootNode, node, true, assocTypeQName, assocQName);
|
||||||
// AssociationRef sourceAssocRef = sourceAssoc.getNodeAssocRef();
|
|
||||||
// // remove it
|
// invoke policy behaviour
|
||||||
// nodeDaoService.deleteNodeAssoc(sourceAssoc);
|
invokeOnCreateChildAssociation(newAssoc.getChildAssocRef());
|
||||||
// // invoke post-deletion behaviour
|
invokeOnUpdateNode(archiveStoreRootNodeRef);
|
||||||
// invokeOnDeleteAssociation(sourceAssocRef);
|
}
|
||||||
// }
|
|
||||||
// // remove all target node associations
|
/**
|
||||||
// for (NodeAssoc targetAssoc : targetAssocs)
|
* Fill the map of all primary children below the given node.
|
||||||
// {
|
* The given node will be added to the map and the method is recursive
|
||||||
// AssociationRef targetAssocRef = targetAssoc.getNodeAssocRef();
|
* to all primary children.
|
||||||
// // remove it
|
*/
|
||||||
// nodeDaoService.deleteNodeAssoc(targetAssoc);
|
private void getPrimaryChildren(Node node, Map<Long, Node> nodesById)
|
||||||
// // invoke post-deletion behaviour
|
{
|
||||||
// invokeOnDeleteAssociation(targetAssocRef);
|
Long id = node.getId();
|
||||||
// }
|
if (nodesById.containsKey(id))
|
||||||
//
|
{
|
||||||
// // delete it
|
// this ID was already added - circular reference
|
||||||
// // We cascade so that we are sure that any new children created by policy implementations are
|
logger.warn("Circular hierarchy found including node " + id);
|
||||||
// // removed. There won't be any noticiations for these, but it prevents the cascade and
|
return;
|
||||||
// // notifications from chasing each other
|
}
|
||||||
// nodeDaoService.deleteNode(node, true);
|
// add the node to the map
|
||||||
//
|
nodesById.put(id, node);
|
||||||
// // Invoke policy behaviours
|
// recurse into the primary children
|
||||||
// invokeOnDeleteNode(primaryParentAssocRef, nodeTypeQName, nodeAspectQNames);
|
Collection<ChildAssoc> childAssocs = node.getChildAssocs();
|
||||||
// }
|
for (ChildAssoc childAssoc : childAssocs)
|
||||||
|
{
|
||||||
|
// cascade into primary associations
|
||||||
|
if (childAssoc.getIsPrimary())
|
||||||
|
{
|
||||||
|
Node primaryChild = childAssoc.getChild();
|
||||||
|
getPrimaryChildren(primaryChild, nodesById);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Archive all associations to and from the given node, with the
|
||||||
|
* exception of associations to or from nodes in the given map.
|
||||||
|
* @param node the node whose associations must be archived
|
||||||
|
* @param nodesById a map of nodes partaking in the archival process
|
||||||
|
*/
|
||||||
|
private void archiveAssocs(Node node, Map<Long, Node> nodesById)
|
||||||
|
{
|
||||||
|
List<ChildAssoc> childAssocsToDelete = new ArrayList<ChildAssoc>(5);
|
||||||
|
// child associations
|
||||||
|
ArrayList<ChildAssociationRef> archivedChildAssocRefs = new ArrayList<ChildAssociationRef>(5);
|
||||||
|
for (ChildAssoc assoc : node.getChildAssocs())
|
||||||
|
{
|
||||||
|
Long relatedNodeId = assoc.getChild().getId();
|
||||||
|
if (nodesById.containsKey(relatedNodeId))
|
||||||
|
{
|
||||||
|
// a sibling in the archive process
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
childAssocsToDelete.add(assoc);
|
||||||
|
archivedChildAssocRefs.add(assoc.getChildAssocRef());
|
||||||
|
}
|
||||||
|
// parent associations
|
||||||
|
ArrayList<ChildAssociationRef> archivedParentAssocRefs = new ArrayList<ChildAssociationRef>(5);
|
||||||
|
for (ChildAssoc assoc : node.getParentAssocs())
|
||||||
|
{
|
||||||
|
Long relatedNodeId = assoc.getParent().getId();
|
||||||
|
if (nodesById.containsKey(relatedNodeId))
|
||||||
|
{
|
||||||
|
// a sibling in the archive process
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
childAssocsToDelete.add(assoc);
|
||||||
|
archivedParentAssocRefs.add(assoc.getChildAssocRef());
|
||||||
|
}
|
||||||
|
|
||||||
|
List<NodeAssoc> nodeAssocsToDelete = new ArrayList<NodeAssoc>(5);
|
||||||
|
// source associations
|
||||||
|
ArrayList<AssociationRef> archivedSourceAssocRefs = new ArrayList<AssociationRef>(5);
|
||||||
|
for (NodeAssoc assoc : node.getSourceNodeAssocs())
|
||||||
|
{
|
||||||
|
Long relatedNodeId = assoc.getSource().getId();
|
||||||
|
if (nodesById.containsKey(relatedNodeId))
|
||||||
|
{
|
||||||
|
// a sibling in the archive process
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
nodeAssocsToDelete.add(assoc);
|
||||||
|
archivedSourceAssocRefs.add(assoc.getNodeAssocRef());
|
||||||
|
}
|
||||||
|
// target associations
|
||||||
|
ArrayList<AssociationRef> archivedTargetAssocRefs = new ArrayList<AssociationRef>(5);
|
||||||
|
for (NodeAssoc assoc : node.getTargetNodeAssocs())
|
||||||
|
{
|
||||||
|
Long relatedNodeId = assoc.getSource().getId();
|
||||||
|
if (nodesById.containsKey(relatedNodeId))
|
||||||
|
{
|
||||||
|
// a sibling in the archive process
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
nodeAssocsToDelete.add(assoc);
|
||||||
|
archivedTargetAssocRefs.add(assoc.getNodeAssocRef());
|
||||||
|
}
|
||||||
|
// delete child assocs
|
||||||
|
for (ChildAssoc assoc : childAssocsToDelete)
|
||||||
|
{
|
||||||
|
nodeDaoService.deleteChildAssoc(assoc, false);
|
||||||
|
}
|
||||||
|
// delete node assocs
|
||||||
|
for (NodeAssoc assoc : nodeAssocsToDelete)
|
||||||
|
{
|
||||||
|
nodeDaoService.deleteNodeAssoc(assoc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add archived aspect
|
||||||
|
node.getAspects().add(ContentModel.ASPECT_ARCHIVED_ASSOCS);
|
||||||
|
// set properties
|
||||||
|
Map<QName, PropertyValue> properties = node.getProperties();
|
||||||
|
|
||||||
|
if (archivedParentAssocRefs.size() > 0)
|
||||||
|
{
|
||||||
|
PropertyDefinition propertyDef = dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_PARENT_ASSOCS);
|
||||||
|
PropertyValue propertyValue = makePropertyValue(propertyDef, archivedParentAssocRefs);
|
||||||
|
properties.put(ContentModel.PROP_ARCHIVED_PARENT_ASSOCS, propertyValue);
|
||||||
|
}
|
||||||
|
if (archivedChildAssocRefs.size() > 0)
|
||||||
|
{
|
||||||
|
PropertyDefinition propertyDef = dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_CHILD_ASSOCS);
|
||||||
|
PropertyValue propertyValue = makePropertyValue(propertyDef, archivedChildAssocRefs);
|
||||||
|
properties.put(ContentModel.PROP_ARCHIVED_CHILD_ASSOCS, propertyValue);
|
||||||
|
}
|
||||||
|
if (archivedSourceAssocRefs.size() > 0)
|
||||||
|
{
|
||||||
|
PropertyDefinition propertyDef = dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_SOURCE_ASSOCS);
|
||||||
|
PropertyValue propertyValue = makePropertyValue(propertyDef, archivedSourceAssocRefs);
|
||||||
|
properties.put(ContentModel.PROP_ARCHIVED_SOURCE_ASSOCS, propertyValue);
|
||||||
|
}
|
||||||
|
if (archivedTargetAssocRefs.size() > 0)
|
||||||
|
{
|
||||||
|
PropertyDefinition propertyDef = dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_TARGET_ASSOCS);
|
||||||
|
PropertyValue propertyValue = makePropertyValue(propertyDef, archivedTargetAssocRefs);
|
||||||
|
properties.put(ContentModel.PROP_ARCHIVED_TARGET_ASSOCS, propertyValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public ChildAssociationRef addChild(NodeRef parentRef, NodeRef childRef, QName assocTypeQName, QName assocQName)
|
public ChildAssociationRef addChild(NodeRef parentRef, NodeRef childRef, QName assocTypeQName, QName assocQName)
|
||||||
{
|
{
|
||||||
|
@@ -112,7 +112,7 @@ public class DbNodeServiceImplTest extends BaseNodeServiceTest
|
|||||||
* <li>deletion</li>
|
* <li>deletion</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public void testNodeStatus() throws Exception
|
public void testNodeStatus() throws Throwable
|
||||||
{
|
{
|
||||||
Map<QName, ChildAssociationRef> assocRefs = buildNodeGraph();
|
Map<QName, ChildAssociationRef> assocRefs = buildNodeGraph();
|
||||||
// get the node to play with
|
// get the node to play with
|
||||||
@@ -190,13 +190,13 @@ public class DbNodeServiceImplTest extends BaseNodeServiceTest
|
|||||||
public Object doWork()
|
public Object doWork()
|
||||||
{
|
{
|
||||||
// check n6
|
// check n6
|
||||||
NodeStatus n6Status = nodeDaoService.getNodeStatus(n6Ref);
|
NodeStatus n6Status = nodeDaoService.getNodeStatus(n6Ref, false);
|
||||||
if (!n6Status.isDeleted())
|
if (!n6Status.isDeleted())
|
||||||
{
|
{
|
||||||
throw new RuntimeException("Deleted node does not have deleted status");
|
throw new RuntimeException("Deleted node does not have deleted status");
|
||||||
}
|
}
|
||||||
// n8 is a primary child - it should be deleted too
|
// n8 is a primary child - it should be deleted too
|
||||||
NodeStatus n8Status = nodeDaoService.getNodeStatus(n8Ref);
|
NodeStatus n8Status = nodeDaoService.getNodeStatus(n8Ref, false);
|
||||||
if (!n8Status.isDeleted())
|
if (!n8Status.isDeleted())
|
||||||
{
|
{
|
||||||
throw new RuntimeException("Cascade-deleted node does not have deleted status");
|
throw new RuntimeException("Cascade-deleted node does not have deleted status");
|
||||||
@@ -228,7 +228,7 @@ public class DbNodeServiceImplTest extends BaseNodeServiceTest
|
|||||||
TransactionUtil.executeInUserTransaction(txnService, checkRecreateWork);
|
TransactionUtil.executeInUserTransaction(txnService, checkRecreateWork);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void executeAndCheck(NodeRef nodeRef, TransactionWork<Object> work) throws Exception
|
private void executeAndCheck(NodeRef nodeRef, TransactionWork<Object> work) throws Throwable
|
||||||
{
|
{
|
||||||
UserTransaction txn = txnService.getUserTransaction();
|
UserTransaction txn = txnService.getUserTransaction();
|
||||||
txn.begin();
|
txn.begin();
|
||||||
@@ -248,7 +248,7 @@ public class DbNodeServiceImplTest extends BaseNodeServiceTest
|
|||||||
assertEquals("Change didn't update status", currentTxnId, newStatus.getChangeTxnId());
|
assertEquals("Change didn't update status", currentTxnId, newStatus.getChangeTxnId());
|
||||||
txn.commit();
|
txn.commit();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Throwable e)
|
||||||
{
|
{
|
||||||
try { txn.rollback(); } catch (Throwable ee) {}
|
try { txn.rollback(); } catch (Throwable ee) {}
|
||||||
throw e;
|
throw e;
|
||||||
|
@@ -70,10 +70,11 @@ public interface NodeDaoService
|
|||||||
* <code>null</code> is returned.
|
* <code>null</code> is returned.
|
||||||
*
|
*
|
||||||
* @param nodeRef the node reference
|
* @param nodeRef the node reference
|
||||||
|
* @param create true to create the entity if it doesn't exist
|
||||||
* @return Returns the node status if the node exists or once existed, otherwise
|
* @return Returns the node status if the node exists or once existed, otherwise
|
||||||
* returns <code>null</code>.
|
* returns <code>null</code> if <code>create == false</code>
|
||||||
*/
|
*/
|
||||||
public NodeStatus getNodeStatus(NodeRef nodeRef);
|
public NodeStatus getNodeStatus(NodeRef nodeRef, boolean create);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the current transaction ID on the node status. Note that the node
|
* Sets the current transaction ID on the node status. Note that the node
|
||||||
|
@@ -177,14 +177,13 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
|
|||||||
/**
|
/**
|
||||||
* Fetch the node status, if it exists
|
* Fetch the node status, if it exists
|
||||||
*/
|
*/
|
||||||
public NodeStatus getNodeStatus(NodeRef nodeRef)
|
public NodeStatus getNodeStatus(NodeRef nodeRef, boolean create)
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
NodeKey nodeKey = new NodeKey(nodeRef);
|
NodeKey nodeKey = new NodeKey(nodeRef);
|
||||||
Object obj = getHibernateTemplate().get(NodeStatusImpl.class, nodeKey);
|
NodeStatus status = null;
|
||||||
// done
|
try
|
||||||
return (NodeStatus) obj;
|
{
|
||||||
|
status = (NodeStatus) getHibernateTemplate().get(NodeStatusImpl.class, nodeKey);
|
||||||
}
|
}
|
||||||
catch (DataAccessException e)
|
catch (DataAccessException e)
|
||||||
{
|
{
|
||||||
@@ -195,6 +194,16 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
|
|||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
// create if necessary
|
||||||
|
if (status == null && create)
|
||||||
|
{
|
||||||
|
status = new NodeStatusImpl();
|
||||||
|
status.setKey(nodeKey);
|
||||||
|
status.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId());
|
||||||
|
getHibernateTemplate().save(status);
|
||||||
|
}
|
||||||
|
// done
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void recordChangeId(NodeRef nodeRef)
|
public void recordChangeId(NodeRef nodeRef)
|
||||||
@@ -260,7 +269,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
|
|||||||
public Node getNode(NodeRef nodeRef)
|
public Node getNode(NodeRef nodeRef)
|
||||||
{
|
{
|
||||||
// get it via the node status
|
// get it via the node status
|
||||||
NodeStatus status = getNodeStatus(nodeRef);
|
NodeStatus status = getNodeStatus(nodeRef, false);
|
||||||
if (status == null)
|
if (status == null)
|
||||||
{
|
{
|
||||||
// no status implies no node
|
// no status implies no node
|
||||||
@@ -309,17 +318,9 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
|
|||||||
}
|
}
|
||||||
// update the node status
|
// update the node status
|
||||||
NodeRef nodeRef = node.getNodeRef();
|
NodeRef nodeRef = node.getNodeRef();
|
||||||
NodeStatus nodeStatus = getNodeStatus(nodeRef);
|
NodeStatus nodeStatus = getNodeStatus(nodeRef, true);
|
||||||
if (nodeStatus == null)
|
|
||||||
{
|
|
||||||
logger.warn("Node to be deleted does not have a status: \n" +
|
|
||||||
" node: " + node.getId());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
nodeStatus.setNode(null);
|
nodeStatus.setNode(null);
|
||||||
nodeStatus.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId());
|
nodeStatus.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId());
|
||||||
}
|
|
||||||
// finally delete the node
|
// finally delete the node
|
||||||
getHibernateTemplate().delete(node);
|
getHibernateTemplate().delete(node);
|
||||||
// done
|
// done
|
||||||
|
@@ -59,6 +59,11 @@ public interface ClassDefinition
|
|||||||
*/
|
*/
|
||||||
public boolean isAspect();
|
public boolean isAspect();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Return true if the type should be archived on delete
|
||||||
|
*/
|
||||||
|
public boolean isArchive();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the properties of the class, including inherited properties
|
* @return the properties of the class, including inherited properties
|
||||||
*/
|
*/
|
||||||
|
@@ -45,6 +45,8 @@ public interface DataTypeDefinition
|
|||||||
public QName QNAME = QName.createQName(NamespaceService.DICTIONARY_MODEL_1_0_URI, "qname");
|
public QName QNAME = QName.createQName(NamespaceService.DICTIONARY_MODEL_1_0_URI, "qname");
|
||||||
public QName CATEGORY = QName.createQName(NamespaceService.DICTIONARY_MODEL_1_0_URI, "category");
|
public QName CATEGORY = QName.createQName(NamespaceService.DICTIONARY_MODEL_1_0_URI, "category");
|
||||||
public QName NODE_REF = QName.createQName(NamespaceService.DICTIONARY_MODEL_1_0_URI, "noderef");
|
public QName NODE_REF = QName.createQName(NamespaceService.DICTIONARY_MODEL_1_0_URI, "noderef");
|
||||||
|
public QName CHILD_ASSOC_REF = QName.createQName(NamespaceService.DICTIONARY_MODEL_1_0_URI, "childassocref");
|
||||||
|
public QName ASSOC_REF = QName.createQName(NamespaceService.DICTIONARY_MODEL_1_0_URI, "assocref");
|
||||||
public QName PATH = QName.createQName(NamespaceService.DICTIONARY_MODEL_1_0_URI, "path");
|
public QName PATH = QName.createQName(NamespaceService.DICTIONARY_MODEL_1_0_URI, "path");
|
||||||
|
|
||||||
|
|
||||||
|
@@ -17,7 +17,9 @@
|
|||||||
package org.alfresco.service.cmr.repository;
|
package org.alfresco.service.cmr.repository;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
import org.alfresco.util.EqualsHelper;
|
import org.alfresco.util.EqualsHelper;
|
||||||
|
|
||||||
@@ -30,6 +32,8 @@ public class AssociationRef implements EntityRef, Serializable
|
|||||||
{
|
{
|
||||||
private static final long serialVersionUID = 3977867284482439475L;
|
private static final long serialVersionUID = 3977867284482439475L;
|
||||||
|
|
||||||
|
private static final String FILLER = "|";
|
||||||
|
|
||||||
private NodeRef sourceRef;
|
private NodeRef sourceRef;
|
||||||
private QName assocTypeQName;
|
private QName assocTypeQName;
|
||||||
private NodeRef targetRef;
|
private NodeRef targetRef;
|
||||||
@@ -67,30 +71,34 @@ public class AssociationRef implements EntityRef, Serializable
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the qualified name of the source-target association
|
* @param childAssocRefStr a string of the form <b>sourceNodeRef|targetNodeRef|assocTypeQName</b>
|
||||||
*
|
|
||||||
* @return Returns the qualified name of the source-target association.
|
|
||||||
*/
|
*/
|
||||||
public QName getTypeQName()
|
public AssociationRef(String assocRefStr)
|
||||||
{
|
{
|
||||||
return assocTypeQName;
|
StringTokenizer tokenizer = new StringTokenizer(assocRefStr, FILLER);
|
||||||
|
if (tokenizer.countTokens() != 3)
|
||||||
|
{
|
||||||
|
throw new AlfrescoRuntimeException("Unable to parse association string: " + assocRefStr);
|
||||||
|
}
|
||||||
|
String sourceNodeRefStr = tokenizer.nextToken();
|
||||||
|
String targetNodeRefStr = tokenizer.nextToken();
|
||||||
|
String assocTypeQNameStr = tokenizer.nextToken();
|
||||||
|
|
||||||
|
this.sourceRef = new NodeRef(sourceNodeRefStr);
|
||||||
|
this.targetRef = new NodeRef(targetNodeRefStr);
|
||||||
|
this.assocTypeQName = QName.createQName(assocTypeQNameStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Returns the child node reference - never null
|
* @return Returns a string of the form <b>sourceNodeRef|targetNodeRef|assocTypeQName|assocQName</b>
|
||||||
*/
|
*/
|
||||||
public NodeRef getTargetRef()
|
public String toString()
|
||||||
{
|
{
|
||||||
return targetRef;
|
StringBuilder sb = new StringBuilder(180);
|
||||||
}
|
sb.append(sourceRef).append(FILLER)
|
||||||
|
.append(targetRef).append(FILLER)
|
||||||
/**
|
.append(assocTypeQName);
|
||||||
* @return Returns the parent node reference, which may be null if this
|
return sb.toString();
|
||||||
* represents the imaginary reference to the root node
|
|
||||||
*/
|
|
||||||
public NodeRef getSourceRef()
|
|
||||||
{
|
|
||||||
return sourceRef;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -126,12 +134,30 @@ public class AssociationRef implements EntityRef, Serializable
|
|||||||
return hashCode;
|
return hashCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString()
|
/**
|
||||||
|
* Get the qualified name of the source-target association
|
||||||
|
*
|
||||||
|
* @return Returns the qualified name of the source-target association.
|
||||||
|
*/
|
||||||
|
public QName getTypeQName()
|
||||||
{
|
{
|
||||||
StringBuffer buffer = new StringBuffer();
|
return assocTypeQName;
|
||||||
buffer.append(getSourceRef());
|
}
|
||||||
buffer.append(" --- ").append(getTypeQName()).append(" ---> ");
|
|
||||||
buffer.append(getTargetRef());
|
/**
|
||||||
return buffer.toString();
|
* @return Returns the child node reference - never null
|
||||||
|
*/
|
||||||
|
public NodeRef getTargetRef()
|
||||||
|
{
|
||||||
|
return targetRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns the parent node reference, which may be null if this
|
||||||
|
* represents the imaginary reference to the root node
|
||||||
|
*/
|
||||||
|
public NodeRef getSourceRef()
|
||||||
|
{
|
||||||
|
return sourceRef;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -17,7 +17,9 @@
|
|||||||
package org.alfresco.service.cmr.repository;
|
package org.alfresco.service.cmr.repository;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
import org.alfresco.util.EqualsHelper;
|
import org.alfresco.util.EqualsHelper;
|
||||||
|
|
||||||
@@ -40,6 +42,8 @@ public class ChildAssociationRef
|
|||||||
{
|
{
|
||||||
private static final long serialVersionUID = 4051322336257127729L;
|
private static final long serialVersionUID = 4051322336257127729L;
|
||||||
|
|
||||||
|
private static final String FILLER = "|";
|
||||||
|
|
||||||
private QName assocTypeQName;
|
private QName assocTypeQName;
|
||||||
private NodeRef parentRef;
|
private NodeRef parentRef;
|
||||||
private QName childQName;
|
private QName childQName;
|
||||||
@@ -98,6 +102,46 @@ public class ChildAssociationRef
|
|||||||
this(assocTypeQName, parentRef, childQName, childRef, false, -1);
|
this(assocTypeQName, parentRef, childQName, childRef, false, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param childAssocRefStr a string of the form <b>parentNodeRef|childNodeRef|assocTypeQName|assocQName|isPrimary|nthSibling</b>
|
||||||
|
*/
|
||||||
|
public ChildAssociationRef(String childAssocRefStr)
|
||||||
|
{
|
||||||
|
StringTokenizer tokenizer = new StringTokenizer(childAssocRefStr, FILLER);
|
||||||
|
if (tokenizer.countTokens() != 6)
|
||||||
|
{
|
||||||
|
throw new AlfrescoRuntimeException("Unable to parse child association string: " + childAssocRefStr);
|
||||||
|
}
|
||||||
|
String parentNodeRefStr = tokenizer.nextToken();
|
||||||
|
String childNodeRefStr = tokenizer.nextToken();
|
||||||
|
String assocTypeQNameStr = tokenizer.nextToken();
|
||||||
|
String assocQNameStr = tokenizer.nextToken();
|
||||||
|
String isPrimaryStr = tokenizer.nextToken();
|
||||||
|
String nthSiblingStr = tokenizer.nextToken();
|
||||||
|
|
||||||
|
this.parentRef = new NodeRef(parentNodeRefStr);
|
||||||
|
this.childRef = new NodeRef(childNodeRefStr);
|
||||||
|
this.assocTypeQName = QName.createQName(assocTypeQNameStr);
|
||||||
|
this.childQName = QName.createQName(assocQNameStr);
|
||||||
|
this.isPrimary = Boolean.parseBoolean(isPrimaryStr);
|
||||||
|
this.nthSibling = Integer.parseInt(nthSiblingStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns a string of the form <b>parentNodeRef|childNodeRef|assocTypeQName|assocQName|isPrimary|nthSibling</b>
|
||||||
|
*/
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder(250);
|
||||||
|
sb.append(parentRef).append(FILLER)
|
||||||
|
.append(childRef).append(FILLER)
|
||||||
|
.append(assocTypeQName).append(FILLER)
|
||||||
|
.append(childQName).append(FILLER)
|
||||||
|
.append(isPrimary).append(FILLER)
|
||||||
|
.append(nthSibling);
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compares:
|
* Compares:
|
||||||
* <ul>
|
* <ul>
|
||||||
@@ -144,16 +188,6 @@ public class ChildAssociationRef
|
|||||||
return (thisVal < anotherVal ? -1 : (thisVal == anotherVal ? 0 : 1));
|
return (thisVal < anotherVal ? -1 : (thisVal == anotherVal ? 0 : 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
StringBuffer sb = new StringBuffer();
|
|
||||||
sb.append("[").append(getTypeQName()).append("]");
|
|
||||||
sb.append(getParentRef());
|
|
||||||
sb.append(" --- ").append(getQName()).append(" ---> ");
|
|
||||||
sb.append(getChildRef());
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the qualified name of the association type
|
* Get the qualified name of the association type
|
||||||
*
|
*
|
||||||
|
@@ -30,8 +30,11 @@ import java.util.Calendar;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import org.alfresco.error.AlfrescoRuntimeException;
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
|
import org.alfresco.service.cmr.repository.AssociationRef;
|
||||||
|
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.ContentReader;
|
import org.alfresco.service.cmr.repository.ContentReader;
|
||||||
|
import org.alfresco.service.cmr.repository.EntityRef;
|
||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
import org.alfresco.service.cmr.repository.Path;
|
import org.alfresco.service.cmr.repository.Path;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
@@ -223,6 +226,24 @@ public class DefaultTypeConverter
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
INSTANCE.addConverter(String.class, ChildAssociationRef.class, new TypeConverter.Converter<String, ChildAssociationRef>()
|
||||||
|
{
|
||||||
|
public ChildAssociationRef convert(String source)
|
||||||
|
{
|
||||||
|
return new ChildAssociationRef(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
INSTANCE.addConverter(String.class, AssociationRef.class, new TypeConverter.Converter<String, AssociationRef>()
|
||||||
|
{
|
||||||
|
public AssociationRef convert(String source)
|
||||||
|
{
|
||||||
|
return new AssociationRef(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
INSTANCE.addConverter(String.class, InputStream.class, new TypeConverter.Converter<String, InputStream>()
|
INSTANCE.addConverter(String.class, InputStream.class, new TypeConverter.Converter<String, InputStream>()
|
||||||
{
|
{
|
||||||
public InputStream convert(String source)
|
public InputStream convert(String source)
|
||||||
@@ -582,18 +603,18 @@ public class DefaultTypeConverter
|
|||||||
INSTANCE.addDynamicTwoStageConverter(QName.class, String.class, InputStream.class);
|
INSTANCE.addDynamicTwoStageConverter(QName.class, String.class, InputStream.class);
|
||||||
|
|
||||||
//
|
//
|
||||||
// NodeRef
|
// EntityRef (NodeRef, ChildAssociationRef, NodeAssociationRef)
|
||||||
//
|
//
|
||||||
|
|
||||||
INSTANCE.addConverter(NodeRef.class, String.class, new TypeConverter.Converter<NodeRef, String>()
|
INSTANCE.addConverter(EntityRef.class, String.class, new TypeConverter.Converter<EntityRef, String>()
|
||||||
{
|
{
|
||||||
public String convert(NodeRef source)
|
public String convert(EntityRef source)
|
||||||
{
|
{
|
||||||
return source.toString();
|
return source.toString();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
INSTANCE.addDynamicTwoStageConverter(NodeRef.class, String.class, InputStream.class);
|
INSTANCE.addDynamicTwoStageConverter(EntityRef.class, String.class, InputStream.class);
|
||||||
|
|
||||||
//
|
//
|
||||||
// ContentData
|
// ContentData
|
||||||
|
Reference in New Issue
Block a user