mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-10-08 14:51:49 +00:00
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:
@@ -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"/>
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1145,6 +1145,85 @@ 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 */
|
||||
|
24
source/test-resources/dictionary/modelA.xml
Normal file
24
source/test-resources/dictionary/modelA.xml
Normal 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>
|
31
source/test-resources/dictionary/modelAupdated.xml
Normal file
31
source/test-resources/dictionary/modelAupdated.xml
Normal 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>
|
@@ -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>
|
22
source/test-resources/dictionary/modelB.xml
Normal file
22
source/test-resources/dictionary/modelB.xml
Normal 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>
|
Reference in New Issue
Block a user