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 true to add the tag and false to remove the tag
+ * @param isTagged true if the node already has the tag aspect applied,
+ * otherwise false
+ */
+ 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);
+ }
+ }
+ }
+}
diff --git a/source/java/org/alfresco/repo/node/integrity/IncompleteNodeTaggerTest.java b/source/java/org/alfresco/repo/node/integrity/IncompleteNodeTaggerTest.java
new file mode 100644
index 0000000000..ed0bf18b8b
--- /dev/null
+++ b/source/java/org/alfresco/repo/node/integrity/IncompleteNodeTaggerTest.java
@@ -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 incomplete 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);
+ }
+}
diff --git a/source/java/org/alfresco/repo/node/integrity/IntegrityChecker.java b/source/java/org/alfresco/repo/node/integrity/IntegrityChecker.java
index 20e2efce0d..95a9d20322 100644
--- a/source/java/org/alfresco/repo/node/integrity/IntegrityChecker.java
+++ b/source/java/org/alfresco/repo/node/integrity/IntegrityChecker.java
@@ -23,7 +23,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.node.NodeServicePolicies;
import org.alfresco.repo.policy.JavaBehaviour;
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.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;
/**
- * Implementation of the {@link org.alfresco.repo.integrity.IntegrityService integrity service}
- * that uses the domain persistence mechanism to store and recall integrity events.
+ * Component that generates and processes integrity events, enforcing the dictionary
+ * model's node structure.
*
* 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
@@ -172,12 +172,9 @@ public class IntegrityChecker
public void init()
{
// check that required properties have been set
- if (dictionaryService == null)
- throw new AlfrescoRuntimeException("IntegrityChecker property not set: dictionaryService");
- if (nodeService == null)
- throw new AlfrescoRuntimeException("IntegrityChecker property not set: nodeService");
- if (policyComponent == null)
- throw new AlfrescoRuntimeException("IntegrityChecker property not set: policyComponent");
+ PropertyCheck.mandatory("IntegrityChecker", "dictionaryService", dictionaryService);
+ PropertyCheck.mandatory("IntegrityChecker", "nodeService", nodeService);
+ PropertyCheck.mandatory("IntegrityChecker", "policyComponent", policyComponent);
if (enabled) // only register behaviour if integrity checking is on
{
@@ -276,6 +273,8 @@ public class IntegrityChecker
/**
* @see PropertiesIntegrityEvent
+ * @see AssocTargetRoleIntegrityEvent
+ * @see AssocTargetMultiplicityIntegrityEvent
*/
public void onCreateNode(ChildAssociationRef childAssocRef)
{
@@ -344,6 +343,7 @@ public class IntegrityChecker
/**
* @see PropertiesIntegrityEvent
+ * @see AssocTargetMultiplicityIntegrityEvent
*/
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)
{
IntegrityEvent event = null;
@@ -426,7 +433,8 @@ public class IntegrityChecker
}
/**
- * @see CreateChildAssocIntegrityEvent
+ * @see AssocSourceMultiplicityIntegrityEvent
+ * @see AssocTargetMultiplicityIntegrityEvent
*/
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)
{
@@ -488,7 +499,8 @@ public class IntegrityChecker
}
/**
- * @see AbstractAssocIntegrityEvent
+ * @see AssocSourceMultiplicityIntegrityEvent
+ * @see AssocTargetMultiplicityIntegrityEvent
*/
public void onDeleteAssociation(AssociationRef nodeAssocRef)
{
@@ -583,7 +595,7 @@ public class IntegrityChecker
private List processAllEvents()
{
// the results
- ArrayList allIntegrityResults = new ArrayList(0); // generally unused
+ ArrayList allIntegrityResults = new ArrayList(0); // generally empty
// get all the events for the transaction (or unit of work)
// duplicates have been elimiated
diff --git a/source/java/org/alfresco/repo/node/integrity/IntegrityTest.java b/source/java/org/alfresco/repo/node/integrity/IntegrityTest.java
index 108f9b44c9..8d4c7933c4 100644
--- a/source/java/org/alfresco/repo/node/integrity/IntegrityTest.java
+++ b/source/java/org/alfresco/repo/node/integrity/IntegrityTest.java
@@ -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_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_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;
static
diff --git a/source/java/org/alfresco/repo/node/integrity/IntegrityTest_model.xml b/source/java/org/alfresco/repo/node/integrity/IntegrityTest_model.xml
index 3ddf0c8dec..f39557d573 100644
--- a/source/java/org/alfresco/repo/node/integrity/IntegrityTest_model.xml
+++ b/source/java/org/alfresco/repo/node/integrity/IntegrityTest_model.xml
@@ -27,11 +27,15 @@
d:text
- true
+ true
d:text
+
+ d:text
+ true
+
@@ -116,6 +120,10 @@
d:int
+
+ d:int
+ true
+
diff --git a/source/java/org/alfresco/repo/node/integrity/PropertiesIntegrityEvent.java b/source/java/org/alfresco/repo/node/integrity/PropertiesIntegrityEvent.java
index c3dc77e811..60ba5c1fc5 100644
--- a/source/java/org/alfresco/repo/node/integrity/PropertiesIntegrityEvent.java
+++ b/source/java/org/alfresco/repo/node/integrity/PropertiesIntegrityEvent.java
@@ -126,8 +126,8 @@ public class PropertiesIntegrityEvent extends AbstractIntegrityEvent
for (PropertyDefinition propertyDef : propertyDefs)
{
QName propertyQName = propertyDef.getName();
- // check that mandatory properties are set
- if (propertyDef.isMandatory() && !nodeProperties.containsKey(propertyQName))
+ // check that enforced, mandatoryproperties are set
+ if (propertyDef.isMandatory() && propertyDef.isMandatoryEnforced() && !nodeProperties.containsKey(propertyQName))
{
IntegrityRecord result = new IntegrityRecord(
"Mandatory property not set: \n" +
diff --git a/source/java/org/alfresco/repo/transaction/TransactionListener.java b/source/java/org/alfresco/repo/transaction/TransactionListener.java
index 7b9d191fde..690ddb4193 100644
--- a/source/java/org/alfresco/repo/transaction/TransactionListener.java
+++ b/source/java/org/alfresco/repo/transaction/TransactionListener.java
@@ -29,6 +29,8 @@ public interface TransactionListener
* Allows the listener to flush any consuming resources. This mechanism is
* used primarily during long-lived transactions to ensure that system resources
* are not used up.
+ *
+ * This method must not be used for implementing business logic.
*/
void flush();
diff --git a/source/java/org/alfresco/service/cmr/dictionary/PropertyDefinition.java b/source/java/org/alfresco/service/cmr/dictionary/PropertyDefinition.java
index 4b38993c6f..f8a5d4c392 100644
--- a/source/java/org/alfresco/service/cmr/dictionary/PropertyDefinition.java
+++ b/source/java/org/alfresco/service/cmr/dictionary/PropertyDefinition.java
@@ -72,6 +72,13 @@ public interface PropertyDefinition
*/
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
*/
diff --git a/source/java/org/alfresco/util/PropertyCheck.java b/source/java/org/alfresco/util/PropertyCheck.java
new file mode 100644
index 0000000000..91464f34fc
--- /dev/null
+++ b/source/java/org/alfresco/util/PropertyCheck.java
@@ -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});
+ }
+ }
+}