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:
Derek Hulley
2006-05-04 16:04:55 +00:00
parent 35f3eda32f
commit fcad8b7a1f
29 changed files with 722 additions and 198 deletions

View File

@@ -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>

View 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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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 -->

View File

@@ -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

View File

@@ -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
// //

View File

@@ -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

View File

@@ -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()

View File

@@ -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();

View File

@@ -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)

View File

@@ -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">

View File

@@ -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"/>

View File

@@ -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);
} }

View File

@@ -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;

View File

@@ -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;
} }

View File

@@ -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();

View 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);
}
}
}

View File

@@ -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)
{ {

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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
*/ */

View File

@@ -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");

View File

@@ -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;
} }
} }

View File

@@ -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
* *

View File

@@ -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