mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
Added sys:incomplete aspect.
Extended <mandatory> definition in the DD. The "mandatory" properties in our system have, until now, been optional, i.e. the integrity has not been enforced. It is possible to have <mandatory enforced="true">true</mandatory>, which means "mandatory and enforced", but <mandatory enforced="false">true</mandatory>, which means "mandatory but not enforced". Our system properties have been marked as "mandatory". Dublin core has had the properties marked as "required". Currently, if the Dublin Core is added, the node is tagged with sys:incomplete. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2562 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -64,6 +64,8 @@
|
|||||||
<bean id="resourceBundles" class="org.alfresco.i18n.ResourceBundleBootstrapComponent">
|
<bean id="resourceBundles" class="org.alfresco.i18n.ResourceBundleBootstrapComponent">
|
||||||
<property name="resourceBundles">
|
<property name="resourceBundles">
|
||||||
<list>
|
<list>
|
||||||
|
<value>alfresco.messages.system-messages</value>
|
||||||
|
<value>alfresco.messages.dictionary-messages</value>
|
||||||
<value>alfresco.messages.version-service</value>
|
<value>alfresco.messages.version-service</value>
|
||||||
<value>alfresco.messages.permissions-service</value>
|
<value>alfresco.messages.permissions-service</value>
|
||||||
<value>alfresco.messages.content-service</value>
|
<value>alfresco.messages.content-service</value>
|
||||||
@@ -71,7 +73,6 @@
|
|||||||
<value>alfresco.messages.template-service</value>
|
<value>alfresco.messages.template-service</value>
|
||||||
<value>alfresco.messages.lock-service</value>
|
<value>alfresco.messages.lock-service</value>
|
||||||
<value>alfresco.messages.patch-service</value>
|
<value>alfresco.messages.patch-service</value>
|
||||||
<value>alfresco.messages.dictionary-messages</value>
|
|
||||||
</list>
|
</list>
|
||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
@@ -19,6 +19,8 @@ d_dictionary.property.err.property_type_not_specified=Property type of property
|
|||||||
d_dictionary.property.err.property_type_not_found=Property type ''{0}'' of property ''{1}'' is not found
|
d_dictionary.property.err.property_type_not_found=Property type ''{0}'' of property ''{1}'' is not found
|
||||||
d_dictionary.property.err.single_valued_content=Content properties must be single-valued
|
d_dictionary.property.err.single_valued_content=Content properties must be single-valued
|
||||||
d_dictionary.property.err.duplicate_constraint_on_property=Found duplicate constraint definition ''{0}'' within property ''{1}''
|
d_dictionary.property.err.duplicate_constraint_on_property=Found duplicate constraint definition ''{0}'' within property ''{1}''
|
||||||
|
d_dictionary.property.err.cannot_relax_mandatory=Cannot relax mandatory attribute of property ''{0}
|
||||||
|
d_dictionary.property.err.cannot_relax_mandatory_enforcement=Cannot relax mandatory attribute enforcement of property ''{0}
|
||||||
|
|
||||||
d_dictionary.constraint.regex.no_match=Value ''{0}'' does not match regular expression: {1}
|
d_dictionary.constraint.regex.no_match=Value ''{0}'' does not match regular expression: {1}
|
||||||
d_dictionary.constraint.regex.match=Value ''{0}'' matches regular expression: {1}
|
d_dictionary.constraint.regex.match=Value ''{0}'' matches regular expression: {1}
|
3
config/alfresco/messages/system-messages.properties
Normal file
3
config/alfresco/messages/system-messages.properties
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# System-related messages
|
||||||
|
|
||||||
|
system.err.property_not_set=Property ''{0}'' has not been set: {1}
|
@@ -30,7 +30,7 @@
|
|||||||
<property name="cm:name">
|
<property name="cm:name">
|
||||||
<title>Name</title>
|
<title>Name</title>
|
||||||
<type>d:text</type>
|
<type>d:text</type>
|
||||||
<mandatory>true</mandatory>
|
<mandatory enforced="true">true</mandatory>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint ref="cm:filename" />
|
<constraint ref="cm:filename" />
|
||||||
</constraints>
|
</constraints>
|
||||||
@@ -356,34 +356,42 @@
|
|||||||
<property name="cm:publisher">
|
<property name="cm:publisher">
|
||||||
<title>Publisher</title>
|
<title>Publisher</title>
|
||||||
<type>d:text</type>
|
<type>d:text</type>
|
||||||
|
<mandatory enforced="false">true</mandatory>
|
||||||
</property>
|
</property>
|
||||||
<property name="cm:contributor">
|
<property name="cm:contributor">
|
||||||
<title>Contributor</title>
|
<title>Contributor</title>
|
||||||
<type>d:text</type>
|
<type>d:text</type>
|
||||||
|
<mandatory enforced="false">true</mandatory>
|
||||||
</property>
|
</property>
|
||||||
<property name="cm:type">
|
<property name="cm:type">
|
||||||
<title>Type</title>
|
<title>Type</title>
|
||||||
<type>d:text</type>
|
<type>d:text</type>
|
||||||
|
<mandatory enforced="false">true</mandatory>
|
||||||
</property>
|
</property>
|
||||||
<property name="cm:identifier">
|
<property name="cm:identifier">
|
||||||
<title>Identifier</title>
|
<title>Identifier</title>
|
||||||
<type>d:text</type>
|
<type>d:text</type>
|
||||||
|
<mandatory enforced="false">true</mandatory>
|
||||||
</property>
|
</property>
|
||||||
<property name="cm:dcsource">
|
<property name="cm:dcsource">
|
||||||
<title>Source</title>
|
<title>Source</title>
|
||||||
<type>d:text</type>
|
<type>d:text</type>
|
||||||
|
<mandatory enforced="false">true</mandatory>
|
||||||
</property>
|
</property>
|
||||||
<property name="cm:coverage">
|
<property name="cm:coverage">
|
||||||
<title>Coverage</title>
|
<title>Coverage</title>
|
||||||
<type>d:text</type>
|
<type>d:text</type>
|
||||||
|
<mandatory enforced="false">true</mandatory>
|
||||||
</property>
|
</property>
|
||||||
<property name="cm:rights">
|
<property name="cm:rights">
|
||||||
<title>Rights</title>
|
<title>Rights</title>
|
||||||
<type>d:text</type>
|
<type>d:text</type>
|
||||||
|
<mandatory enforced="false">true</mandatory>
|
||||||
</property>
|
</property>
|
||||||
<property name="cm:subject">
|
<property name="cm:subject">
|
||||||
<title>Subject</title>
|
<title>Subject</title>
|
||||||
<type>d:text</type>
|
<type>d:text</type>
|
||||||
|
<mandatory enforced="false">true</mandatory>
|
||||||
</property>
|
</property>
|
||||||
</properties>
|
</properties>
|
||||||
<mandatory-aspects>
|
<mandatory-aspects>
|
||||||
@@ -392,20 +400,6 @@
|
|||||||
</mandatory-aspects>
|
</mandatory-aspects>
|
||||||
</aspect>
|
</aspect>
|
||||||
|
|
||||||
<!--
|
|
||||||
<aspect name="cm:subjectable">
|
|
||||||
<title>Subjectable</title>
|
|
||||||
<mandatory-aspects>
|
|
||||||
<aspect>cm:auditable</aspect>
|
|
||||||
</mandatory-aspects>
|
|
||||||
<properties>
|
|
||||||
<property name="cm:subject">
|
|
||||||
<type>d:text</type>
|
|
||||||
</property>
|
|
||||||
</properties>
|
|
||||||
</aspect>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<aspect name="cm:basable">
|
<aspect name="cm:basable">
|
||||||
<title>Basable</title>
|
<title>Basable</title>
|
||||||
<associations>
|
<associations>
|
||||||
|
@@ -28,22 +28,22 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<property name="sys:versionMajor">
|
<property name="sys:versionMajor">
|
||||||
<type>d:text</type>
|
<type>d:text</type>
|
||||||
<mandatory>true</mandatory>
|
<mandatory enforced="true">true</mandatory>
|
||||||
</property>
|
</property>
|
||||||
<property name="sys:versionMinor">
|
<property name="sys:versionMinor">
|
||||||
<type>d:text</type>
|
<type>d:text</type>
|
||||||
<mandatory>true</mandatory>
|
<mandatory enforced="true">true</mandatory>
|
||||||
</property>
|
</property>
|
||||||
<property name="sys:versionRevision">
|
<property name="sys:versionRevision">
|
||||||
<type>d:text</type>
|
<type>d:text</type>
|
||||||
<mandatory>true</mandatory>
|
<mandatory enforced="true">true</mandatory>
|
||||||
</property>
|
</property>
|
||||||
<property name="sys:versionLabel">
|
<property name="sys:versionLabel">
|
||||||
<type>d:text</type>
|
<type>d:text</type>
|
||||||
</property>
|
</property>
|
||||||
<property name="sys:versionSchema">
|
<property name="sys:versionSchema">
|
||||||
<type>d:int</type>
|
<type>d:int</type>
|
||||||
<mandatory>true</mandatory>
|
<mandatory enforced="true">true</mandatory>
|
||||||
<default>0</default>
|
<default>0</default>
|
||||||
</property>
|
</property>
|
||||||
</properties>
|
</properties>
|
||||||
@@ -81,7 +81,7 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<property name="sys:reference">
|
<property name="sys:reference">
|
||||||
<type>d:noderef</type>
|
<type>d:noderef</type>
|
||||||
<mandatory>true</mandatory>
|
<mandatory enforced="true">true</mandatory>
|
||||||
</property>
|
</property>
|
||||||
</properties>
|
</properties>
|
||||||
</type>
|
</type>
|
||||||
@@ -101,19 +101,24 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<property name="sys:store-protocol">
|
<property name="sys:store-protocol">
|
||||||
<type>d:text</type>
|
<type>d:text</type>
|
||||||
<mandatory>true</mandatory>
|
<mandatory enforced="true">true</mandatory>
|
||||||
</property>
|
</property>
|
||||||
<property name="sys:store-identifier">
|
<property name="sys:store-identifier">
|
||||||
<type>d:text</type>
|
<type>d:text</type>
|
||||||
<mandatory>true</mandatory>
|
<mandatory enforced="true">true</mandatory>
|
||||||
</property>
|
</property>
|
||||||
<property name="sys:node-uuid">
|
<property name="sys:node-uuid">
|
||||||
<type>d:text</type>
|
<type>d:text</type>
|
||||||
<mandatory>true</mandatory>
|
<mandatory enforced="true">true</mandatory>
|
||||||
</property>
|
</property>
|
||||||
</properties>
|
</properties>
|
||||||
</aspect>
|
</aspect>
|
||||||
|
|
||||||
|
<!-- aspect to tag incomplete nodes -->
|
||||||
|
<aspect name="sys:incomplete">
|
||||||
|
<title>Incomplete</title>
|
||||||
|
</aspect>
|
||||||
|
|
||||||
</aspects>
|
</aspects>
|
||||||
|
|
||||||
</model>
|
</model>
|
@@ -112,4 +112,17 @@
|
|||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<!-- tags nodes that are incomplete w.r.t. properties-->
|
||||||
|
<bean id="incompleteNodeTagger" class="org.alfresco.repo.node.integrity.IncompleteNodeTagger" init-method="init">
|
||||||
|
<property name="policyComponent">
|
||||||
|
<ref bean="policyComponent"/>
|
||||||
|
</property>
|
||||||
|
<property name="dictionaryService">
|
||||||
|
<ref bean="dictionaryService" />
|
||||||
|
</property>
|
||||||
|
<property name="nodeService">
|
||||||
|
<ref bean="nodeService" />
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
</beans>
|
</beans>
|
||||||
|
@@ -36,6 +36,9 @@ public interface ContentModel
|
|||||||
static final QName PROP_STORE_IDENTIFIER = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "store-identifier");
|
static final QName PROP_STORE_IDENTIFIER = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "store-identifier");
|
||||||
static final QName PROP_NODE_UUID = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "node-uuid");
|
static final QName PROP_NODE_UUID = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "node-uuid");
|
||||||
|
|
||||||
|
// tag for incomplete nodes
|
||||||
|
static final QName ASPECT_INCOMPLETE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "incomplete");
|
||||||
|
|
||||||
// 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");
|
||||||
|
@@ -18,12 +18,14 @@ package org.alfresco.repo.dictionary;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
import org.alfresco.i18n.I18NUtil;
|
import org.alfresco.i18n.I18NUtil;
|
||||||
import org.alfresco.repo.dictionary.constraint.RegexConstraint;
|
import org.alfresco.repo.dictionary.constraint.RegexConstraint;
|
||||||
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
|
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
|
||||||
|
import org.alfresco.service.cmr.dictionary.ClassDefinition;
|
||||||
import org.alfresco.service.cmr.dictionary.Constraint;
|
import org.alfresco.service.cmr.dictionary.Constraint;
|
||||||
import org.alfresco.service.cmr.dictionary.ConstraintDefinition;
|
import org.alfresco.service.cmr.dictionary.ConstraintDefinition;
|
||||||
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
||||||
@@ -144,6 +146,42 @@ public class DictionaryDAOTest extends TestCase
|
|||||||
assertTrue("Expected type REGEX constraint", constraint instanceof RegexConstraint);
|
assertTrue("Expected type REGEX constraint", constraint instanceof RegexConstraint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testMandatoryEnforced()
|
||||||
|
{
|
||||||
|
// get the properties for the test type
|
||||||
|
QName testEnforcedQName = QName.createQName(TEST_URL, "enforced");
|
||||||
|
ClassDefinition testEnforcedClassDef = service.getClass(testEnforcedQName);
|
||||||
|
Map<QName, PropertyDefinition> testEnforcedPropertyDefs = testEnforcedClassDef.getProperties();
|
||||||
|
|
||||||
|
PropertyDefinition propertyDef = null;
|
||||||
|
|
||||||
|
QName testMandatoryEnforcedQName = QName.createQName(TEST_URL, "mandatory-enforced");
|
||||||
|
propertyDef = testEnforcedPropertyDefs.get(testMandatoryEnforcedQName);
|
||||||
|
assertNotNull("Property not found: " + testMandatoryEnforcedQName,
|
||||||
|
propertyDef);
|
||||||
|
assertTrue("Expected property to be mandatory: " + testMandatoryEnforcedQName,
|
||||||
|
propertyDef.isMandatory());
|
||||||
|
assertTrue("Expected property to be mandatory-enforced: " + testMandatoryEnforcedQName,
|
||||||
|
propertyDef.isMandatoryEnforced());
|
||||||
|
|
||||||
|
QName testMandatoryNotEnforcedQName = QName.createQName(TEST_URL, "mandatory-not-enforced");
|
||||||
|
propertyDef = testEnforcedPropertyDefs.get(testMandatoryNotEnforcedQName);
|
||||||
|
assertNotNull("Property not found: " + testMandatoryNotEnforcedQName,
|
||||||
|
propertyDef);
|
||||||
|
assertTrue("Expected property to be mandatory: " + testMandatoryNotEnforcedQName,
|
||||||
|
propertyDef.isMandatory());
|
||||||
|
assertFalse("Expected property to be mandatory-not-enforced: " + testMandatoryNotEnforcedQName,
|
||||||
|
propertyDef.isMandatoryEnforced());
|
||||||
|
|
||||||
|
QName testMandatoryDefaultEnforcedQName = QName.createQName(TEST_URL, "mandatory-default-enforced");
|
||||||
|
propertyDef = testEnforcedPropertyDefs.get(testMandatoryDefaultEnforcedQName);
|
||||||
|
assertNotNull("Property not found: " + testMandatoryDefaultEnforcedQName,
|
||||||
|
propertyDef);
|
||||||
|
assertTrue("Expected property to be mandatory: " + testMandatoryDefaultEnforcedQName,
|
||||||
|
propertyDef.isMandatory());
|
||||||
|
assertFalse("Expected property to be mandatory-not-enforced: " + testMandatoryDefaultEnforcedQName,
|
||||||
|
propertyDef.isMandatoryEnforced());
|
||||||
|
}
|
||||||
|
|
||||||
public void testSubClassOf()
|
public void testSubClassOf()
|
||||||
{
|
{
|
||||||
|
@@ -34,6 +34,7 @@ public class M2Property
|
|||||||
private String propertyType = null;
|
private String propertyType = null;
|
||||||
private boolean isProtected = false;
|
private boolean isProtected = false;
|
||||||
private boolean isMandatory = false;
|
private boolean isMandatory = false;
|
||||||
|
private boolean isMandatoryEnforced = false;
|
||||||
private boolean isMultiValued = false;
|
private boolean isMultiValued = false;
|
||||||
private String defaultValue = null;
|
private String defaultValue = null;
|
||||||
private boolean isIndexed = true;
|
private boolean isIndexed = true;
|
||||||
@@ -118,12 +119,20 @@ public class M2Property
|
|||||||
return isMandatory;
|
return isMandatory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setMandatory(boolean isMandatory)
|
public void setMandatory(boolean isMandatory)
|
||||||
{
|
{
|
||||||
this.isMandatory = isMandatory;
|
this.isMandatory = isMandatory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isMandatoryEnforced()
|
||||||
|
{
|
||||||
|
return isMandatoryEnforced;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMandatoryEnforced(boolean isMandatoryEnforced)
|
||||||
|
{
|
||||||
|
this.isMandatoryEnforced = isMandatoryEnforced;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isMultiValued()
|
public boolean isMultiValued()
|
||||||
{
|
{
|
||||||
|
@@ -139,15 +139,27 @@ import org.alfresco.service.namespace.QName;
|
|||||||
property.setDefaultValue(defaultValue == null ? propertyDef.getDefaultValue() : defaultValue);
|
property.setDefaultValue(defaultValue == null ? propertyDef.getDefaultValue() : defaultValue);
|
||||||
|
|
||||||
// Process Mandatory Value
|
// Process Mandatory Value
|
||||||
Boolean isMandatory = override.isMandatory();
|
Boolean isOverrideMandatory = override.isMandatory();
|
||||||
if (isMandatory != null)
|
boolean isOverrideMandatoryEnforced = override.isMandatoryEnforced();
|
||||||
|
if (isOverrideMandatory != null && propertyDef.isMandatory())
|
||||||
{
|
{
|
||||||
if (propertyDef.isMandatory() == true && isMandatory == false)
|
// the override specified whether the property should be mandatory or not
|
||||||
|
// check that the mandatory enforcement is not relaxed
|
||||||
|
if (!isOverrideMandatory)
|
||||||
{
|
{
|
||||||
throw new DictionaryException("Cannot relax mandatory attribute of property " + propertyDef.getName().toPrefixString());
|
throw new DictionaryException(
|
||||||
|
"d_dictionary.property.err.cannot_relax_mandatory",
|
||||||
|
propertyDef.getName().toPrefixString());
|
||||||
|
}
|
||||||
|
else if (!isOverrideMandatoryEnforced && propertyDef.isMandatoryEnforced())
|
||||||
|
{
|
||||||
|
throw new DictionaryException(
|
||||||
|
"d_dictionary.property.err.cannot_relax_mandatory_enforcement",
|
||||||
|
propertyDef.getName().toPrefixString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
property.setMandatory(isMandatory == null ? propertyDef.isMandatory() : isMandatory);
|
property.setMandatory(isOverrideMandatory == null ? propertyDef.isMandatory() : isOverrideMandatory);
|
||||||
|
property.setMandatoryEnforced(isOverrideMandatoryEnforced);
|
||||||
|
|
||||||
// Copy all other properties as they are
|
// Copy all other properties as they are
|
||||||
property.setDescription(propertyDef.getDescription());
|
property.setDescription(propertyDef.getDescription());
|
||||||
@@ -260,6 +272,10 @@ import org.alfresco.service.namespace.QName;
|
|||||||
return m2Property.isMandatory();
|
return m2Property.isMandatory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isMandatoryEnforced()
|
||||||
|
{
|
||||||
|
return m2Property.isMandatoryEnforced();
|
||||||
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see org.alfresco.repo.dictionary.PropertyDefinition#isProtected()
|
* @see org.alfresco.repo.dictionary.PropertyDefinition#isProtected()
|
||||||
|
@@ -27,6 +27,7 @@ public class M2PropertyOverride
|
|||||||
{
|
{
|
||||||
private String name;
|
private String name;
|
||||||
private Boolean isMandatory;
|
private Boolean isMandatory;
|
||||||
|
private boolean isMandatoryEnforced = false;
|
||||||
private String defaultValue;
|
private String defaultValue;
|
||||||
|
|
||||||
|
|
||||||
@@ -58,6 +59,10 @@ public class M2PropertyOverride
|
|||||||
this.isMandatory = isMandatory;
|
this.isMandatory = isMandatory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isMandatoryEnforced()
|
||||||
|
{
|
||||||
|
return isMandatoryEnforced;
|
||||||
|
}
|
||||||
|
|
||||||
public String getDefaultValue()
|
public String getDefaultValue()
|
||||||
{
|
{
|
||||||
|
@@ -121,16 +121,31 @@
|
|||||||
|
|
||||||
<type name="test:folder">
|
<type name="test:folder">
|
||||||
<parent>test:base</parent>
|
<parent>test:base</parent>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<property name="test:folderprop">
|
<property name="test:folderprop">
|
||||||
<type>d:text</type>
|
<type>d:text</type>
|
||||||
<protected>true</protected>
|
<protected>true</protected>
|
||||||
<default></default>
|
<default></default>
|
||||||
</property>
|
</property>
|
||||||
|
|
||||||
</properties>
|
</properties>
|
||||||
|
</type>
|
||||||
|
|
||||||
|
<type name="test:enforced">
|
||||||
|
<parent>test:base</parent>
|
||||||
|
<properties>
|
||||||
|
<property name="test:mandatory-enforced">
|
||||||
|
<type>d:text</type>
|
||||||
|
<mandatory enforced="true">true</mandatory>
|
||||||
|
</property>
|
||||||
|
<property name="test:mandatory-not-enforced">
|
||||||
|
<type>d:text</type>
|
||||||
|
<mandatory enforced="false">true</mandatory>
|
||||||
|
</property>
|
||||||
|
<property name="test:mandatory-default-enforced">
|
||||||
|
<type>d:text</type>
|
||||||
|
<mandatory>true</mandatory>
|
||||||
|
</property>
|
||||||
|
</properties>
|
||||||
</type>
|
</type>
|
||||||
|
|
||||||
</types>
|
</types>
|
||||||
|
@@ -71,7 +71,10 @@
|
|||||||
<collection field="propertyOverrides" item-type="org.alfresco.repo.dictionary.M2PropertyOverride" factory="org.alfresco.repo.dictionary.M2Model.createList">
|
<collection field="propertyOverrides" item-type="org.alfresco.repo.dictionary.M2PropertyOverride" factory="org.alfresco.repo.dictionary.M2Model.createList">
|
||||||
<structure name="property" type="org.alfresco.repo.dictionary.M2PropertyOverride" usage="optional">
|
<structure name="property" type="org.alfresco.repo.dictionary.M2PropertyOverride" usage="optional">
|
||||||
<value style="attribute" name="name" field="name"/>
|
<value style="attribute" name="name" field="name"/>
|
||||||
<value name="mandatory" field="isMandatory" usage="optional"/>
|
<structure name="mandatory" usage="optional">
|
||||||
|
<value style="attribute" name="enforced" field="isMandatoryEnforced" usage="optional" />
|
||||||
|
<value style="text" field="isMandatory" />
|
||||||
|
</structure>
|
||||||
<value name="default" field="defaultValue" usage="optional"/>
|
<value name="default" field="defaultValue" usage="optional"/>
|
||||||
</structure>
|
</structure>
|
||||||
</collection>
|
</collection>
|
||||||
@@ -97,7 +100,10 @@
|
|||||||
<value name="description" field="description" usage="optional"/>
|
<value name="description" field="description" usage="optional"/>
|
||||||
<value name="type" field="propertyType"/>
|
<value name="type" field="propertyType"/>
|
||||||
<value name="protected" field="isProtected" usage="optional"/>
|
<value name="protected" field="isProtected" usage="optional"/>
|
||||||
<value name="mandatory" field="isMandatory" usage="optional"/>
|
<structure name="mandatory" usage="optional">
|
||||||
|
<value style="attribute" name="enforced" field="isMandatoryEnforced" usage="optional"/>
|
||||||
|
<value style="text" field="isMandatory" />
|
||||||
|
</structure>
|
||||||
<value name="multiple" field="isMultiValued" usage="optional"/>
|
<value name="multiple" field="isMultiValued" usage="optional"/>
|
||||||
<value name="default" field="defaultValue" usage="optional"/>
|
<value name="default" field="defaultValue" usage="optional"/>
|
||||||
<structure name="index" usage="optional">
|
<structure name="index" usage="optional">
|
||||||
|
@@ -65,7 +65,7 @@ public class NodeIndexer
|
|||||||
/**
|
/**
|
||||||
* Registers the policy behaviour methods
|
* Registers the policy behaviour methods
|
||||||
*/
|
*/
|
||||||
private void init()
|
public void init()
|
||||||
{
|
{
|
||||||
policyComponent.bindClassBehaviour(
|
policyComponent.bindClassBehaviour(
|
||||||
QName.createQName(NamespaceService.ALFRESCO_URI, "beforeCreateStore"),
|
QName.createQName(NamespaceService.ALFRESCO_URI, "beforeCreateStore"),
|
||||||
|
@@ -0,0 +1,336 @@
|
|||||||
|
/*
|
||||||
|
* 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.integrity;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
|
import org.alfresco.model.ContentModel;
|
||||||
|
import org.alfresco.repo.node.NodeServicePolicies;
|
||||||
|
import org.alfresco.repo.policy.JavaBehaviour;
|
||||||
|
import org.alfresco.repo.policy.PolicyComponent;
|
||||||
|
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
||||||
|
import org.alfresco.repo.transaction.TransactionListenerAdapter;
|
||||||
|
import org.alfresco.service.cmr.dictionary.AspectDefinition;
|
||||||
|
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||||
|
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
||||||
|
import org.alfresco.service.cmr.dictionary.TypeDefinition;
|
||||||
|
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
|
import org.alfresco.service.namespace.NamespaceService;
|
||||||
|
import org.alfresco.service.namespace.QName;
|
||||||
|
import org.alfresco.util.PropertyCheck;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component that tags {@link org.alfresco.model.ContentModel#ASPECT_INCOMPLETE incomplete} nodes.
|
||||||
|
*
|
||||||
|
* @author Derek Hulley
|
||||||
|
*/
|
||||||
|
public class IncompleteNodeTagger
|
||||||
|
extends TransactionListenerAdapter
|
||||||
|
implements NodeServicePolicies.OnCreateNodePolicy,
|
||||||
|
NodeServicePolicies.OnUpdatePropertiesPolicy,
|
||||||
|
NodeServicePolicies.OnAddAspectPolicy,
|
||||||
|
NodeServicePolicies.OnRemoveAspectPolicy
|
||||||
|
{
|
||||||
|
private static Log logger = LogFactory.getLog(IncompleteNodeTagger.class);
|
||||||
|
|
||||||
|
/** key against which the set of nodes to check is stored in the current transaction */
|
||||||
|
private static final String KEY_NODE_SET = "IncompleteNodeTagger.NodeSet";
|
||||||
|
|
||||||
|
private PolicyComponent policyComponent;
|
||||||
|
private DictionaryService dictionaryService;
|
||||||
|
private NodeService nodeService;
|
||||||
|
|
||||||
|
public IncompleteNodeTagger()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param policyComponent the component to register behaviour with
|
||||||
|
*/
|
||||||
|
public void setPolicyComponent(PolicyComponent policyComponent)
|
||||||
|
{
|
||||||
|
this.policyComponent = policyComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param dictionaryService the dictionary against which to confirm model details
|
||||||
|
*/
|
||||||
|
public void setDictionaryService(DictionaryService dictionaryService)
|
||||||
|
{
|
||||||
|
this.dictionaryService = dictionaryService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param nodeService the node service to use for browsing node structures
|
||||||
|
*/
|
||||||
|
public void setNodeService(NodeService nodeService)
|
||||||
|
{
|
||||||
|
this.nodeService = nodeService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the system-level policy behaviours
|
||||||
|
*/
|
||||||
|
public void init()
|
||||||
|
{
|
||||||
|
// check that required properties have been set
|
||||||
|
PropertyCheck.mandatory("IncompleteNodeTagger", "dictionaryService", dictionaryService);
|
||||||
|
PropertyCheck.mandatory("IncompleteNodeTagger", "nodeService", nodeService);
|
||||||
|
PropertyCheck.mandatory("IncompleteNodeTagger", "policyComponent", policyComponent);
|
||||||
|
|
||||||
|
// register behaviour
|
||||||
|
policyComponent.bindClassBehaviour(
|
||||||
|
QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateNode"),
|
||||||
|
this,
|
||||||
|
new JavaBehaviour(this, "onCreateNode"));
|
||||||
|
policyComponent.bindClassBehaviour(
|
||||||
|
QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"),
|
||||||
|
this,
|
||||||
|
new JavaBehaviour(this, "onUpdateProperties"));
|
||||||
|
policyComponent.bindClassBehaviour(
|
||||||
|
QName.createQName(NamespaceService.ALFRESCO_URI, "onAddAspect"),
|
||||||
|
this,
|
||||||
|
new JavaBehaviour(this, "onAddAspect"));
|
||||||
|
policyComponent.bindClassBehaviour(
|
||||||
|
QName.createQName(NamespaceService.ALFRESCO_URI, "onRemoveAspect"),
|
||||||
|
this,
|
||||||
|
new JavaBehaviour(this, "onRemoveAspect"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns the set of nodes to check, or null if none were registered
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private Set<NodeRef> getNodeSet()
|
||||||
|
{
|
||||||
|
return (Set<NodeRef>) AlfrescoTransactionSupport.getResource(KEY_NODE_SET);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that this service is registered with the transaction and saves the node
|
||||||
|
* reference for use later.
|
||||||
|
*
|
||||||
|
* @param nodeRef
|
||||||
|
*/
|
||||||
|
private void save(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
// register this service
|
||||||
|
AlfrescoTransactionSupport.bindListener(this);
|
||||||
|
|
||||||
|
// get the event list
|
||||||
|
Set<NodeRef> nodeRefs = getNodeSet();
|
||||||
|
if (nodeRefs == null)
|
||||||
|
{
|
||||||
|
nodeRefs = new HashSet<NodeRef>(31, 0.75F);
|
||||||
|
AlfrescoTransactionSupport.bindResource(KEY_NODE_SET, nodeRefs);
|
||||||
|
}
|
||||||
|
// add node to the set
|
||||||
|
nodeRefs.add(nodeRef);
|
||||||
|
// done
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Added node reference to set: " + nodeRef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCreateNode(ChildAssociationRef childAssocRef)
|
||||||
|
{
|
||||||
|
NodeRef nodeRef = childAssocRef.getChildRef();
|
||||||
|
save(nodeRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onUpdateProperties(
|
||||||
|
NodeRef nodeRef,
|
||||||
|
Map<QName, Serializable> before,
|
||||||
|
Map<QName, Serializable> after)
|
||||||
|
{
|
||||||
|
save(nodeRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the node for checking of properties.
|
||||||
|
* The {@link org.alfresco.model.ContentModel#ASPECT_INCOMPLETE incomplete} aspect is
|
||||||
|
* not processed.
|
||||||
|
*/
|
||||||
|
public void onAddAspect(NodeRef nodeRef, QName aspectTypeQName)
|
||||||
|
{
|
||||||
|
if (aspectTypeQName.equals(ContentModel.ASPECT_INCOMPLETE))
|
||||||
|
{
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Ignoring aspect addition: " + ContentModel.ASPECT_INCOMPLETE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
save(nodeRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recheck the node as an aspect was removed.
|
||||||
|
*/
|
||||||
|
public void onRemoveAspect(NodeRef nodeRef, QName aspectTypeQName)
|
||||||
|
{
|
||||||
|
if (aspectTypeQName.equals(ContentModel.ASPECT_INCOMPLETE))
|
||||||
|
{
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Ignoring aspect removal: " + ContentModel.ASPECT_INCOMPLETE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
save(nodeRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process all the nodes that require checking within the transaction.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void beforeCommit(boolean readOnly)
|
||||||
|
{
|
||||||
|
Set<NodeRef> nodeRefs = getNodeSet();
|
||||||
|
// clear the set out of the transaction
|
||||||
|
// there may be processes that react to the addition/removal of the aspect,
|
||||||
|
// and these will, in turn, lead to further events
|
||||||
|
AlfrescoTransactionSupport.unbindResource(KEY_NODE_SET);
|
||||||
|
// process each node
|
||||||
|
for (NodeRef nodeRef : nodeRefs)
|
||||||
|
{
|
||||||
|
processNode(nodeRef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processNode(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
// ignore the node if the marker aspect is already present
|
||||||
|
boolean isTagged = nodeService.hasAspect(nodeRef, ContentModel.ASPECT_INCOMPLETE);
|
||||||
|
|
||||||
|
// get the node properties
|
||||||
|
Map<QName, Serializable> nodeProperties = nodeService.getProperties(nodeRef);
|
||||||
|
// get the node type
|
||||||
|
QName nodeTypeQName = nodeService.getType(nodeRef);
|
||||||
|
// get property definitions for the node type
|
||||||
|
TypeDefinition typeDef = dictionaryService.getType(nodeTypeQName);
|
||||||
|
if (typeDef == null)
|
||||||
|
{
|
||||||
|
throw new AlfrescoRuntimeException("Node type is not recognised: " + nodeTypeQName);
|
||||||
|
}
|
||||||
|
Collection<PropertyDefinition> propertyDefs = typeDef.getProperties().values();
|
||||||
|
// check them
|
||||||
|
boolean classPropertiesOK = checkProperties(propertyDefs, nodeProperties);
|
||||||
|
|
||||||
|
// were there outstanding properties to check?
|
||||||
|
if (!classPropertiesOK)
|
||||||
|
{
|
||||||
|
addOrRemoveTag(nodeRef, true, isTagged);
|
||||||
|
// no further checking required
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the node aspects
|
||||||
|
Set<QName> aspectTypeQNames = nodeService.getAspects(nodeRef);
|
||||||
|
for (QName aspectTypeQName : aspectTypeQNames)
|
||||||
|
{
|
||||||
|
// get property definitions for the aspect
|
||||||
|
AspectDefinition aspectDef = dictionaryService.getAspect(aspectTypeQName);
|
||||||
|
propertyDefs = aspectDef.getProperties().values();
|
||||||
|
// check them
|
||||||
|
boolean aspectPropertiesOK = checkProperties(propertyDefs, nodeProperties);
|
||||||
|
// were there outstanding properties to check?
|
||||||
|
if (!aspectPropertiesOK)
|
||||||
|
{
|
||||||
|
addOrRemoveTag(nodeRef, true, isTagged);
|
||||||
|
// no further checking required
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// all properties passed (both class- and aspect-defined) - remove aspect
|
||||||
|
addOrRemoveTag(nodeRef, false, isTagged);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param propertyDefs the property definitions to check
|
||||||
|
* @param properties the properties
|
||||||
|
* @return Returns true if the property definitions were all satisified
|
||||||
|
*/
|
||||||
|
private boolean checkProperties(
|
||||||
|
Collection<PropertyDefinition> propertyDefs,
|
||||||
|
Map<QName, Serializable> properties)
|
||||||
|
{
|
||||||
|
for (PropertyDefinition propertyDef : propertyDefs)
|
||||||
|
{
|
||||||
|
if (!propertyDef.isMandatory())
|
||||||
|
{
|
||||||
|
// The property isn't mandatory in any way
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (propertyDef.isMandatoryEnforced())
|
||||||
|
{
|
||||||
|
// The mandatory nature of the property is fully enforced
|
||||||
|
// Leave these for integrity
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// The mandatory nature of the property is 'soft' a.k.a. 'required'
|
||||||
|
// Check that the property value has been supplied
|
||||||
|
if (properties.get(propertyDef.getName()) == null)
|
||||||
|
{
|
||||||
|
// property NOT supplied
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// all properties were present
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds or removes the {@link ContentModel#ASPECT_INCOMPLETE incomplete} marker aspect.
|
||||||
|
* This only performs the operation if the tag aspect is or is not present, depending
|
||||||
|
* on the operation required.
|
||||||
|
*
|
||||||
|
* @param nodeRef the node to apply the change to
|
||||||
|
* @param addTag <tt>true</tt> to add the tag and <tt>false</tt> to remove the tag
|
||||||
|
* @param isTagged <tt>true</tt> if the node already has the tag aspect applied,
|
||||||
|
* otherwise <tt>false</tt>
|
||||||
|
*/
|
||||||
|
private void addOrRemoveTag(NodeRef nodeRef, boolean addTag, boolean isTagged)
|
||||||
|
{
|
||||||
|
if (addTag && !isTagged)
|
||||||
|
{
|
||||||
|
nodeService.addAspect(nodeRef, ContentModel.ASPECT_INCOMPLETE, null);
|
||||||
|
// done
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Tagged node as INCOMPLETE: " + nodeRef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!addTag && isTagged)
|
||||||
|
{
|
||||||
|
nodeService.removeAspect(nodeRef, ContentModel.ASPECT_INCOMPLETE);
|
||||||
|
// done
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Untagged node as INCOMPLETE: " + nodeRef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
* 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.integrity;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import javax.transaction.UserTransaction;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.alfresco.model.ContentModel;
|
||||||
|
import org.alfresco.repo.dictionary.DictionaryDAO;
|
||||||
|
import org.alfresco.repo.dictionary.M2Model;
|
||||||
|
import org.alfresco.repo.node.BaseNodeServiceTest;
|
||||||
|
import org.alfresco.repo.security.authentication.AuthenticationComponent;
|
||||||
|
import org.alfresco.service.ServiceRegistry;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
|
import org.alfresco.service.cmr.repository.StoreRef;
|
||||||
|
import org.alfresco.service.namespace.QName;
|
||||||
|
import org.alfresco.service.transaction.TransactionService;
|
||||||
|
import org.alfresco.util.ApplicationContextHelper;
|
||||||
|
import org.alfresco.util.PropertyMap;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that tagging of <i>incomplete</i> nodes is done properly.
|
||||||
|
*
|
||||||
|
* @author Derek Hulley
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public class IncompleteNodeTaggerTest extends TestCase
|
||||||
|
{
|
||||||
|
private static Log logger = LogFactory.getLog(IncompleteNodeTaggerTest.class);
|
||||||
|
|
||||||
|
private static ApplicationContext ctx;
|
||||||
|
static
|
||||||
|
{
|
||||||
|
ctx = ApplicationContextHelper.getApplicationContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
private IncompleteNodeTagger tagger;
|
||||||
|
private ServiceRegistry serviceRegistry;
|
||||||
|
private NodeService nodeService;
|
||||||
|
private NodeRef rootNodeRef;
|
||||||
|
private PropertyMap properties;
|
||||||
|
private UserTransaction txn;
|
||||||
|
private AuthenticationComponent authenticationComponent;
|
||||||
|
|
||||||
|
public void setUp() throws Exception
|
||||||
|
{
|
||||||
|
DictionaryDAO dictionaryDao = (DictionaryDAO) ctx.getBean("dictionaryDAO");
|
||||||
|
ClassLoader cl = BaseNodeServiceTest.class.getClassLoader();
|
||||||
|
// load the test model
|
||||||
|
InputStream modelStream = cl.getResourceAsStream("org/alfresco/repo/node/integrity/IntegrityTest_model.xml");
|
||||||
|
assertNotNull(modelStream);
|
||||||
|
M2Model model = M2Model.createModel(modelStream);
|
||||||
|
dictionaryDao.putModel(model);
|
||||||
|
|
||||||
|
tagger = (IncompleteNodeTagger) ctx.getBean("incompleteNodeTagger");
|
||||||
|
|
||||||
|
serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY);
|
||||||
|
nodeService = serviceRegistry.getNodeService();
|
||||||
|
this.authenticationComponent = (AuthenticationComponent)ctx.getBean("authenticationComponent");
|
||||||
|
|
||||||
|
this.authenticationComponent.setSystemUserAsCurrentUser();
|
||||||
|
|
||||||
|
// begin a transaction
|
||||||
|
TransactionService transactionService = serviceRegistry.getTransactionService();
|
||||||
|
txn = transactionService.getUserTransaction();
|
||||||
|
txn.begin();
|
||||||
|
StoreRef storeRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, getName());
|
||||||
|
if (!nodeService.exists(storeRef))
|
||||||
|
{
|
||||||
|
nodeService.createStore(storeRef.getProtocol(), storeRef.getIdentifier());
|
||||||
|
}
|
||||||
|
rootNodeRef = nodeService.getRootNode(storeRef);
|
||||||
|
|
||||||
|
properties = new PropertyMap();
|
||||||
|
properties.put(IntegrityTest.TEST_PROP_TEXT_C, "abc");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tearDown() throws Exception
|
||||||
|
{
|
||||||
|
authenticationComponent.clearCurrentSecurityContext();
|
||||||
|
txn.rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a node of the given type, and hanging off the root node
|
||||||
|
*/
|
||||||
|
private NodeRef createNode(String name, QName type, PropertyMap properties)
|
||||||
|
{
|
||||||
|
return nodeService.createNode(
|
||||||
|
rootNodeRef,
|
||||||
|
ContentModel.ASSOC_CHILDREN,
|
||||||
|
QName.createQName(IntegrityTest.NAMESPACE, name),
|
||||||
|
type,
|
||||||
|
properties
|
||||||
|
).getChildRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSetUp() throws Exception
|
||||||
|
{
|
||||||
|
assertNotNull("IncompleteNodeTagger not created", tagger);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkTagging(NodeRef nodeRef, boolean mustBeTagged)
|
||||||
|
{
|
||||||
|
tagger.beforeCommit(false);
|
||||||
|
assertEquals(nodeService.hasAspect(nodeRef, ContentModel.ASPECT_INCOMPLETE), mustBeTagged);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCreateWithoutProperties() throws Exception
|
||||||
|
{
|
||||||
|
NodeRef nodeRef = createNode("abc", IntegrityTest.TEST_TYPE_WITH_PROPERTIES, null);
|
||||||
|
checkTagging(nodeRef, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCreateWithProperties() throws Exception
|
||||||
|
{
|
||||||
|
NodeRef nodeRef = createNode("abc", IntegrityTest.TEST_TYPE_WITH_PROPERTIES, properties);
|
||||||
|
checkTagging(nodeRef, false);
|
||||||
|
}
|
||||||
|
}
|
@@ -23,7 +23,6 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.alfresco.error.AlfrescoRuntimeException;
|
|
||||||
import org.alfresco.repo.node.NodeServicePolicies;
|
import org.alfresco.repo.node.NodeServicePolicies;
|
||||||
import org.alfresco.repo.policy.JavaBehaviour;
|
import org.alfresco.repo.policy.JavaBehaviour;
|
||||||
import org.alfresco.repo.policy.PolicyComponent;
|
import org.alfresco.repo.policy.PolicyComponent;
|
||||||
@@ -39,12 +38,13 @@ import org.alfresco.service.cmr.repository.NodeRef;
|
|||||||
import org.alfresco.service.cmr.repository.NodeService;
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
import org.alfresco.service.namespace.NamespaceService;
|
import org.alfresco.service.namespace.NamespaceService;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
|
import org.alfresco.util.PropertyCheck;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of the {@link org.alfresco.repo.integrity.IntegrityService integrity service}
|
* Component that generates and processes integrity events, enforcing the dictionary
|
||||||
* that uses the domain persistence mechanism to store and recall integrity events.
|
* model's node structure.
|
||||||
* <p>
|
* <p>
|
||||||
* In order to fulfill the contract of the interface, this class registers to receive notifications
|
* In order to fulfill the contract of the interface, this class registers to receive notifications
|
||||||
* pertinent to changes in the node structure. These are then store away in the persistent
|
* pertinent to changes in the node structure. These are then store away in the persistent
|
||||||
@@ -172,12 +172,9 @@ public class IntegrityChecker
|
|||||||
public void init()
|
public void init()
|
||||||
{
|
{
|
||||||
// check that required properties have been set
|
// check that required properties have been set
|
||||||
if (dictionaryService == null)
|
PropertyCheck.mandatory("IntegrityChecker", "dictionaryService", dictionaryService);
|
||||||
throw new AlfrescoRuntimeException("IntegrityChecker property not set: dictionaryService");
|
PropertyCheck.mandatory("IntegrityChecker", "nodeService", nodeService);
|
||||||
if (nodeService == null)
|
PropertyCheck.mandatory("IntegrityChecker", "policyComponent", policyComponent);
|
||||||
throw new AlfrescoRuntimeException("IntegrityChecker property not set: nodeService");
|
|
||||||
if (policyComponent == null)
|
|
||||||
throw new AlfrescoRuntimeException("IntegrityChecker property not set: policyComponent");
|
|
||||||
|
|
||||||
if (enabled) // only register behaviour if integrity checking is on
|
if (enabled) // only register behaviour if integrity checking is on
|
||||||
{
|
{
|
||||||
@@ -276,6 +273,8 @@ public class IntegrityChecker
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @see PropertiesIntegrityEvent
|
* @see PropertiesIntegrityEvent
|
||||||
|
* @see AssocTargetRoleIntegrityEvent
|
||||||
|
* @see AssocTargetMultiplicityIntegrityEvent
|
||||||
*/
|
*/
|
||||||
public void onCreateNode(ChildAssociationRef childAssocRef)
|
public void onCreateNode(ChildAssociationRef childAssocRef)
|
||||||
{
|
{
|
||||||
@@ -344,6 +343,7 @@ public class IntegrityChecker
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @see PropertiesIntegrityEvent
|
* @see PropertiesIntegrityEvent
|
||||||
|
* @see AssocTargetMultiplicityIntegrityEvent
|
||||||
*/
|
*/
|
||||||
public void onAddAspect(NodeRef nodeRef, QName aspectTypeQName)
|
public void onAddAspect(NodeRef nodeRef, QName aspectTypeQName)
|
||||||
{
|
{
|
||||||
@@ -382,6 +382,13 @@ public class IntegrityChecker
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see AssocSourceTypeIntegrityEvent
|
||||||
|
* @see AssocTargetTypeIntegrityEvent
|
||||||
|
* @see AssocSourceMultiplicityIntegrityEvent
|
||||||
|
* @see AssocTargetMultiplicityIntegrityEvent
|
||||||
|
* @see AssocTargetRoleIntegrityEvent
|
||||||
|
*/
|
||||||
public void onCreateChildAssociation(ChildAssociationRef childAssocRef)
|
public void onCreateChildAssociation(ChildAssociationRef childAssocRef)
|
||||||
{
|
{
|
||||||
IntegrityEvent event = null;
|
IntegrityEvent event = null;
|
||||||
@@ -426,7 +433,8 @@ public class IntegrityChecker
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see CreateChildAssocIntegrityEvent
|
* @see AssocSourceMultiplicityIntegrityEvent
|
||||||
|
* @see AssocTargetMultiplicityIntegrityEvent
|
||||||
*/
|
*/
|
||||||
public void onDeleteChildAssociation(ChildAssociationRef childAssocRef)
|
public void onDeleteChildAssociation(ChildAssociationRef childAssocRef)
|
||||||
{
|
{
|
||||||
@@ -450,7 +458,10 @@ public class IntegrityChecker
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see AbstractAssocIntegrityEvent
|
* @see AssocSourceTypeIntegrityEvent
|
||||||
|
* @see AssocTargetTypeIntegrityEvent
|
||||||
|
* @see AssocSourceMultiplicityIntegrityEvent
|
||||||
|
* @see AssocTargetMultiplicityIntegrityEvent
|
||||||
*/
|
*/
|
||||||
public void onCreateAssociation(AssociationRef nodeAssocRef)
|
public void onCreateAssociation(AssociationRef nodeAssocRef)
|
||||||
{
|
{
|
||||||
@@ -488,7 +499,8 @@ public class IntegrityChecker
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see AbstractAssocIntegrityEvent
|
* @see AssocSourceMultiplicityIntegrityEvent
|
||||||
|
* @see AssocTargetMultiplicityIntegrityEvent
|
||||||
*/
|
*/
|
||||||
public void onDeleteAssociation(AssociationRef nodeAssocRef)
|
public void onDeleteAssociation(AssociationRef nodeAssocRef)
|
||||||
{
|
{
|
||||||
@@ -583,7 +595,7 @@ public class IntegrityChecker
|
|||||||
private List<IntegrityRecord> processAllEvents()
|
private List<IntegrityRecord> processAllEvents()
|
||||||
{
|
{
|
||||||
// the results
|
// the results
|
||||||
ArrayList<IntegrityRecord> allIntegrityResults = new ArrayList<IntegrityRecord>(0); // generally unused
|
ArrayList<IntegrityRecord> allIntegrityResults = new ArrayList<IntegrityRecord>(0); // generally empty
|
||||||
|
|
||||||
// get all the events for the transaction (or unit of work)
|
// get all the events for the transaction (or unit of work)
|
||||||
// duplicates have been elimiated
|
// duplicates have been elimiated
|
||||||
|
@@ -74,8 +74,10 @@ public class IntegrityTest extends TestCase
|
|||||||
|
|
||||||
public static final QName TEST_PROP_TEXT_A = QName.createQName(NAMESPACE, "prop-text-a");
|
public static final QName TEST_PROP_TEXT_A = QName.createQName(NAMESPACE, "prop-text-a");
|
||||||
public static final QName TEST_PROP_TEXT_B = QName.createQName(NAMESPACE, "prop-text-b");
|
public static final QName TEST_PROP_TEXT_B = QName.createQName(NAMESPACE, "prop-text-b");
|
||||||
|
public static final QName TEST_PROP_TEXT_C = QName.createQName(NAMESPACE, "prop-text-c");
|
||||||
public static final QName TEST_PROP_INT_A = QName.createQName(NAMESPACE, "prop-int-a");
|
public static final QName TEST_PROP_INT_A = QName.createQName(NAMESPACE, "prop-int-a");
|
||||||
public static final QName TEST_PROP_INT_B = QName.createQName(NAMESPACE, "prop-int-b");
|
public static final QName TEST_PROP_INT_B = QName.createQName(NAMESPACE, "prop-int-b");
|
||||||
|
public static final QName TEST_PROP_INT_C = QName.createQName(NAMESPACE, "prop-int-c");
|
||||||
|
|
||||||
private static ApplicationContext ctx;
|
private static ApplicationContext ctx;
|
||||||
static
|
static
|
||||||
|
@@ -27,11 +27,15 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<property name="test:prop-text-a">
|
<property name="test:prop-text-a">
|
||||||
<type>d:text</type>
|
<type>d:text</type>
|
||||||
<mandatory>true</mandatory>
|
<mandatory enforced="true">true</mandatory>
|
||||||
</property>
|
</property>
|
||||||
<property name="test:prop-text-b">
|
<property name="test:prop-text-b">
|
||||||
<type>d:text</type>
|
<type>d:text</type>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="test:prop-text-c">
|
||||||
|
<type>d:text</type>
|
||||||
|
<mandatory enforced="false">true</mandatory>
|
||||||
|
</property>
|
||||||
</properties>
|
</properties>
|
||||||
</type>
|
</type>
|
||||||
<!-- Type with mandatory aspect -->
|
<!-- Type with mandatory aspect -->
|
||||||
@@ -116,6 +120,10 @@
|
|||||||
<property name="test:prop-int-b">
|
<property name="test:prop-int-b">
|
||||||
<type>d:int</type>
|
<type>d:int</type>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="test:prop-int-c">
|
||||||
|
<type>d:int</type>
|
||||||
|
<mandatory enforced="false">true</mandatory>
|
||||||
|
</property>
|
||||||
</properties>
|
</properties>
|
||||||
</aspect>
|
</aspect>
|
||||||
<!-- aspect with associations -->
|
<!-- aspect with associations -->
|
||||||
|
@@ -126,8 +126,8 @@ public class PropertiesIntegrityEvent extends AbstractIntegrityEvent
|
|||||||
for (PropertyDefinition propertyDef : propertyDefs)
|
for (PropertyDefinition propertyDef : propertyDefs)
|
||||||
{
|
{
|
||||||
QName propertyQName = propertyDef.getName();
|
QName propertyQName = propertyDef.getName();
|
||||||
// check that mandatory properties are set
|
// check that enforced, mandatoryproperties are set
|
||||||
if (propertyDef.isMandatory() && !nodeProperties.containsKey(propertyQName))
|
if (propertyDef.isMandatory() && propertyDef.isMandatoryEnforced() && !nodeProperties.containsKey(propertyQName))
|
||||||
{
|
{
|
||||||
IntegrityRecord result = new IntegrityRecord(
|
IntegrityRecord result = new IntegrityRecord(
|
||||||
"Mandatory property not set: \n" +
|
"Mandatory property not set: \n" +
|
||||||
|
@@ -29,6 +29,8 @@ public interface TransactionListener
|
|||||||
* Allows the listener to flush any consuming resources. This mechanism is
|
* Allows the listener to flush any consuming resources. This mechanism is
|
||||||
* used primarily during long-lived transactions to ensure that system resources
|
* used primarily during long-lived transactions to ensure that system resources
|
||||||
* are not used up.
|
* are not used up.
|
||||||
|
* <p>
|
||||||
|
* This method must not be used for implementing business logic.
|
||||||
*/
|
*/
|
||||||
void flush();
|
void flush();
|
||||||
|
|
||||||
|
@@ -72,6 +72,13 @@ public interface PropertyDefinition
|
|||||||
*/
|
*/
|
||||||
public boolean isMandatory();
|
public boolean isMandatory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns true if the system enforces the presence of
|
||||||
|
* {@link #isMandatory() mandatory} properties, or false if the system
|
||||||
|
* just marks objects that don't have all mandatory properties present.
|
||||||
|
*/
|
||||||
|
public boolean isMandatoryEnforced();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true => system maintained, false => client may maintain
|
* @return true => system maintained, false => client may maintain
|
||||||
*/
|
*/
|
||||||
|
47
source/java/org/alfresco/util/PropertyCheck.java
Normal file
47
source/java/org/alfresco/util/PropertyCheck.java
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class for for use when checking properties. This class uses
|
||||||
|
* I18N for its messages.
|
||||||
|
*
|
||||||
|
* @author Derek Hulley
|
||||||
|
*/
|
||||||
|
public class PropertyCheck
|
||||||
|
{
|
||||||
|
public static final String ERR_PROPERTY_NOT_SET = "system.err.property_not_set";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that the property with the given name is not null.
|
||||||
|
*
|
||||||
|
* @param target the object on which the property must have been set
|
||||||
|
* @param propertyName the name of the property
|
||||||
|
* @param value of the property value
|
||||||
|
*/
|
||||||
|
public static void mandatory(Object target, String propertyName, Object value)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
throw new AlfrescoRuntimeException(
|
||||||
|
ERR_PROPERTY_NOT_SET,
|
||||||
|
new Object[] {propertyName, target});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user