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">
|
<bean id="modelValidator" class="org.alfresco.repo.dictionary.ModelValidatorImpl">
|
||||||
<property name="dictionaryDAO" ref="dictionaryDAO"/>
|
<property name="dictionaryDAO" ref="dictionaryDAO"/>
|
||||||
|
<property name="dictionaryService" ref="dictionaryService"/>
|
||||||
<property name="namespaceService" ref="namespaceService"/>
|
<property name="namespaceService" ref="namespaceService"/>
|
||||||
<property name="workflowService" ref="WorkflowService"/>
|
<property name="workflowService" ref="WorkflowService"/>
|
||||||
<property name="tenantService" ref="tenantService"/>
|
<property name="tenantService" ref="tenantService"/>
|
||||||
|
@@ -25,6 +25,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.repo.dictionary;
|
package org.alfresco.repo.dictionary;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
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.ClassDefinition;
|
||||||
import org.alfresco.service.cmr.dictionary.ConstraintDefinition;
|
import org.alfresco.service.cmr.dictionary.ConstraintDefinition;
|
||||||
import org.alfresco.service.cmr.dictionary.DictionaryException;
|
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.ModelDefinition;
|
||||||
import org.alfresco.service.cmr.dictionary.NamespaceDefinition;
|
import org.alfresco.service.cmr.dictionary.NamespaceDefinition;
|
||||||
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
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 static final Log logger = LogFactory.getLog(ModelValidatorImpl.class);
|
||||||
|
|
||||||
private DictionaryDAO dictionaryDAO;
|
private DictionaryDAO dictionaryDAO;
|
||||||
|
private DictionaryService dictionaryService;
|
||||||
private QNameDAO qnameDAO;
|
private QNameDAO qnameDAO;
|
||||||
private NamespaceService namespaceService;
|
private NamespaceService namespaceService;
|
||||||
private TransactionService transactionService;
|
private TransactionService transactionService;
|
||||||
@@ -119,6 +122,11 @@ public class ModelValidatorImpl implements ModelValidator
|
|||||||
this.tenantAdminService = tenantAdminService;
|
this.tenantAdminService = tenantAdminService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setDictionaryService(DictionaryService dictionaryService)
|
||||||
|
{
|
||||||
|
this.dictionaryService = dictionaryService;
|
||||||
|
}
|
||||||
|
|
||||||
private void checkCustomModelNamespace(M2Model model, String tenantDomain)
|
private void checkCustomModelNamespace(M2Model model, String tenantDomain)
|
||||||
{
|
{
|
||||||
if(tenantDomain != null && !tenantDomain.equals("") && enforceTenantInNamespace)
|
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() + "'");
|
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 ...)
|
// 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 */
|
/* 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