Merged 5.2.N (5.2.1) to HEAD (5.2)

127393 rmunteanu: Merged 5.1.N (5.1.2) to 5.2.N (5.2.1)
      127370 rmunteanu: Merged 5.1.1 (5.1.1) to 5.1.N (5.1.2)
         127309 aleahu: MNT-14332 : Alfresco stops working when Type Hierarchy creates a circular reference
            - added validation to check if circular dependency was introduced when a new namespace is imported


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@127885 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Alan Davis
2016-06-06 09:25:30 +00:00
parent b7a3f669b2
commit 9379b1720a
7 changed files with 234 additions and 0 deletions

View File

@@ -591,6 +591,7 @@
<bean id="modelValidator" class="org.alfresco.repo.dictionary.ModelValidatorImpl">
<property name="dictionaryDAO" ref="dictionaryDAO"/>
<property name="dictionaryService" ref="dictionaryService"/>
<property name="namespaceService" ref="namespaceService"/>
<property name="workflowService" ref="WorkflowService"/>
<property name="tenantService" ref="tenantService"/>

View File

@@ -25,6 +25,7 @@
*/
package org.alfresco.repo.dictionary;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
@@ -45,6 +46,7 @@ import org.alfresco.service.cmr.dictionary.AspectDefinition;
import org.alfresco.service.cmr.dictionary.ClassDefinition;
import org.alfresco.service.cmr.dictionary.ConstraintDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryException;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.ModelDefinition;
import org.alfresco.service.cmr.dictionary.NamespaceDefinition;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
@@ -71,6 +73,7 @@ public class ModelValidatorImpl implements ModelValidator
private static final Log logger = LogFactory.getLog(ModelValidatorImpl.class);
private DictionaryDAO dictionaryDAO;
private DictionaryService dictionaryService;
private QNameDAO qnameDAO;
private NamespaceService namespaceService;
private TransactionService transactionService;
@@ -119,6 +122,11 @@ public class ModelValidatorImpl implements ModelValidator
this.tenantAdminService = tenantAdminService;
}
public void setDictionaryService(DictionaryService dictionaryService)
{
this.dictionaryService = dictionaryService;
}
private void checkCustomModelNamespace(M2Model model, String tenantDomain)
{
if(tenantDomain != null && !tenantDomain.equals("") && enforceTenantInNamespace)
@@ -477,8 +485,37 @@ public class ModelValidatorImpl implements ModelValidator
{
throw new AlfrescoRuntimeException("Failed to validate model update - found non-incrementally updated " + modelDiff.getElementType() + " '" + modelDiff.getElementName() + "'");
}
if(modelDiff.getDiffType().equals(M2ModelDiff.DIFF_CREATED))
{
if (modelDiff.getElementType().equals(M2ModelDiff.TYPE_NAMESPACE))
{
ModelDefinition importedModel = dictionaryService.getModelByNamespaceUri(modelDiff.getNamespaceDefinition().getUri());
if(importedModel != null && !model.getNamespaces().isEmpty())
{
checkCircularDependency(importedModel, model, importedModel.getName().getLocalName());
}
}
}
}
// TODO validate that any deleted constraints are not being referenced - else currently will become anon - or push down into model compilation (check backwards compatibility ...)
}
private void checkCircularDependency(ModelDefinition model, M2Model existingModel, String parentPrefixedName) throws AlfrescoRuntimeException
{
for (NamespaceDefinition importedNamespace : model.getImportedNamespaces())
{
ModelDefinition md = null;
if ((md = dictionaryService.getModelByNamespaceUri(importedNamespace.getUri())) != null)
{
if (existingModel.getNamespace(importedNamespace.getUri()) != null)
{
throw new AlfrescoRuntimeException("Failed to validate model update - found circular dependency. You can't set parent " + parentPrefixedName + " as it's model already depends on " + existingModel.getName());
}
checkCircularDependency(md, existingModel, parentPrefixedName);
}
}
}
}

View File

@@ -1145,7 +1145,86 @@ public class DictionaryModelTypeTest extends BaseSpringTest
}
});
}
public void testCircularDependencyDetected() throws Exception
{
// Create modelA
txn = transactionService.getUserTransaction();
txn.begin();
final NodeRef modelANode = createActiveModel("dictionary/modelA.xml");
txn.commit();
//Create modelB
txn = transactionService.getUserTransaction();
txn.begin();
final NodeRef modelBNode = createActiveModel("dictionary/modelB.xml");
txn.commit();
// update model A to introduce circular dependency
try
{
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Void>() {
public Void execute() throws Exception {
ContentWriter contentWriter = DictionaryModelTypeTest.this.contentService.getWriter(modelANode, ContentModel.PROP_CONTENT, true);
contentWriter.putContent(Thread.currentThread().getContextClassLoader().getResourceAsStream("dictionary/modelAupdated.xml"));
return null;
}
});
fail("Validation should fail as a circular dependency was introduced");
} catch(AlfrescoRuntimeException e)
{
assertTrue(e.getMessage().contains("Failed to validate model update - found circular dependency. You can't set parent model-B as it's model already depends on prefixA:model-A"));
}
// update model A to introduce circular dependency - also add a second namespace
try
{
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Void>() {
public Void execute() throws Exception {
ContentWriter contentWriter = DictionaryModelTypeTest.this.contentService.getWriter(modelANode, ContentModel.PROP_CONTENT, true);
contentWriter.putContent(Thread.currentThread().getContextClassLoader().getResourceAsStream("dictionary/modelAupdatedWithSecondNamespace.xml"));
return null;
}
});
fail("Validation should fail as a circular dependency was introduced");
} catch(AlfrescoRuntimeException e)
{
assertTrue(e.getMessage().contains("Failed to validate model update - found circular dependency. You can't set parent model-B as it's model already depends on prefixA:model-A"));
}
//delete both created models
finally {
transactionService.getRetryingTransactionHelper().doInTransaction(
new RetryingTransactionCallback<Object>() {
public Object execute() throws Exception {
// Delete the model
DictionaryModelTypeTest.this.nodeService.deleteNode(modelANode);
DictionaryModelTypeTest.this.nodeService.deleteNode(modelBNode);
return null;
}
});
}
}
private NodeRef createActiveModel(String contentFile)
{
PropertyMap properties = new PropertyMap(1);
properties.put(ContentModel.PROP_MODEL_ACTIVE, true);
NodeRef modelNode = this.nodeService.createNode(
this.rootNodeRef,
ContentModel.ASSOC_CHILDREN,
QName.createQName(NamespaceService.ALFRESCO_URI, "dictionaryModels"),
ContentModel.TYPE_DICTIONARY_MODEL,
properties).getChildRef();
assertNotNull(modelNode);
ContentWriter contentWriter = this.contentService.getWriter(modelNode, ContentModel.PROP_CONTENT, true);
contentWriter.setEncoding("UTF-8");
contentWriter.setMimetype(MimetypeMap.MIMETYPE_XML);
contentWriter.putContent(Thread.currentThread().getContextClassLoader().getResourceAsStream(contentFile));
return modelNode;
}
}
/* MNT-15345 test */
public void testImportingSameNamespaceFailsWithinSingleModel() throws Exception

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<model xmlns="http://www.alfresco.org/model/dictionary/1.0" name="prefixA:model-A">
<author>Administrator</author>
<imports>
<import uri="http://www.alfresco.org/model/content/1.0" prefix="cm"/>
</imports>
<namespaces>
<namespace uri="http://www.mycompany.com/model/modelAnamespace/1.0" prefix="prefixA"/>
</namespaces>
<data-types/>
<constraints/>
<types>
<type name="prefixA:TypeA">
<title>Model-A Type A</title>
<parent>cm:content</parent>
<properties/>
<associations/>
<overrides/>
<mandatory-aspects/>
</type>
</types>
<aspects/>
</model>

View File

@@ -0,0 +1,31 @@
<model xmlns="http://www.alfresco.org/model/dictionary/1.0" name="prefixA:model-A">
<author>Administrator</author>
<imports>
<import uri="http://www.alfresco.org/model/content/1.0" prefix="cm"/>
<import uri="http://www.mycompany.com/model/modelBnamespace/1.0" prefix="prefixB"/>
</imports>
<namespaces>
<namespace uri="http://www.mycompany.com/model/modelAnamespace/1.0" prefix="prefixA"/>
</namespaces>
<data-types/>
<constraints/>
<types>
<type name="prefixA:TypeA">
<title>Model-A Type A</title>
<parent>cm:content</parent>
<properties/>
<associations/>
<overrides/>
<mandatory-aspects/>
</type>
<type name="prefixA:modelATypeB">
<title>Model-A Type B</title>
<parent>prefixB:TypeB</parent>
<properties/>
<associations/>
<overrides/>
<mandatory-aspects/>
</type>
</types>
<aspects/>
</model>

View File

@@ -0,0 +1,40 @@
<model xmlns="http://www.alfresco.org/model/dictionary/1.0" name="prefixA:model-A">
<author>Administrator</author>
<imports>
<import uri="http://www.alfresco.org/model/content/1.0" prefix="cm"/>
<import uri="http://www.mycompany.com/model/modelBnamespace/1.0" prefix="prefixB"/>
</imports>
<namespaces>
<namespace uri="http://www.mycompany.com/model/modelDnamespace/1.0" prefix="prefixD"/>
<namespace uri="http://www.mycompany.com/model/modelAnamespace/1.0" prefix="prefixA"/>
</namespaces>
<data-types/>
<constraints/>
<types>
<type name="prefixD:TypeD">
<title>Model-A Type A</title>
<parent>cm:content</parent>
<properties/>
<associations/>
<overrides/>
<mandatory-aspects/>
</type>
<type name="prefixA:TypeA">
<title>Model-A Type A</title>
<parent>cm:content</parent>
<properties/>
<associations/>
<overrides/>
<mandatory-aspects/>
</type>
<type name="prefixA:modelATypeB">
<title>Model-A Type B</title>
<parent>prefixB:TypeB</parent>
<properties/>
<associations/>
<overrides/>
<mandatory-aspects/>
</type>
</types>
<aspects/>
</model>

View File

@@ -0,0 +1,22 @@
<model xmlns="http://www.alfresco.org/model/dictionary/1.0" name="prefixB:model-B">
<author>Administrator</author>
<imports>
<import uri="http://www.mycompany.com/model/modelAnamespace/1.0" prefix="prefixA"/>
</imports>
<namespaces>
<namespace uri="http://www.mycompany.com/model/modelBnamespace/1.0" prefix="prefixB"/>
</namespaces>
<data-types/>
<constraints/>
<types>
<type name="prefixB:TypeB">
<title>Model-B TypeB</title>
<parent>prefixA:TypeA</parent>
<properties/>
<associations/>
<overrides/>
<mandatory-aspects/>
</type>
</types>
<aspects/>
</model>