mirror of
				https://github.com/Alfresco/alfresco-community-repo.git
				synced 2025-10-29 15:21:53 +00:00 
			
		
		
		
	132578 jkaabimofrad: APPSREPO-59: Modified the model validator to ignore the TYPE_NAMESPACE when there is a DIFF_DELETED type. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@132589 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
		
			
				
	
	
		
			530 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			530 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| /*
 | |
|  * #%L
 | |
|  * Alfresco Repository
 | |
|  * %%
 | |
|  * Copyright (C) 2005 - 2016 Alfresco Software Limited
 | |
|  * %%
 | |
|  * This file is part of the Alfresco software. 
 | |
|  * If the software was purchased under a paid Alfresco license, the terms of 
 | |
|  * the paid license agreement will prevail.  Otherwise, the software is 
 | |
|  * provided under the following open source license terms:
 | |
|  * 
 | |
|  * Alfresco is free software: you can redistribute it and/or modify
 | |
|  * it under the terms of the GNU Lesser General Public License as published by
 | |
|  * the Free Software Foundation, either version 3 of the License, or
 | |
|  * (at your option) any later version.
 | |
|  * 
 | |
|  * Alfresco is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|  * GNU Lesser General Public License for more details.
 | |
|  * 
 | |
|  * You should have received a copy of the GNU Lesser General Public License
 | |
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 | |
|  * #L%
 | |
|  */
 | |
| package org.alfresco.repo.dictionary;
 | |
| 
 | |
| import java.util.Collection;
 | |
| import java.util.HashSet;
 | |
| import java.util.List;
 | |
| import java.util.Set;
 | |
| 
 | |
| import org.alfresco.error.AlfrescoRuntimeException;
 | |
| import org.alfresco.repo.domain.qname.QNameDAO;
 | |
| import org.alfresco.repo.security.authentication.AuthenticationUtil;
 | |
| import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
 | |
| import org.alfresco.repo.tenant.Tenant;
 | |
| import org.alfresco.repo.tenant.TenantAdminService;
 | |
| import org.alfresco.repo.tenant.TenantService;
 | |
| import org.alfresco.repo.tenant.TenantUtil;
 | |
| import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork;
 | |
| import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
 | |
| import org.alfresco.repo.workflow.BPMEngineRegistry;
 | |
| 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;
 | |
| import org.alfresco.service.cmr.dictionary.TypeDefinition;
 | |
| import org.alfresco.service.cmr.workflow.WorkflowDefinition;
 | |
| import org.alfresco.service.cmr.workflow.WorkflowService;
 | |
| import org.alfresco.service.cmr.workflow.WorkflowTaskDefinition;
 | |
| import org.alfresco.service.namespace.NamespaceException;
 | |
| import org.alfresco.service.namespace.NamespaceService;
 | |
| import org.alfresco.service.namespace.QName;
 | |
| import org.alfresco.service.transaction.TransactionService;
 | |
| import org.apache.commons.logging.Log;
 | |
| import org.apache.commons.logging.LogFactory;
 | |
| import org.springframework.dao.DataIntegrityViolationException;
 | |
| 
 | |
| /**
 | |
|  * Model change validation covering model deletes, model constituent changes e.g. property deletes,
 | |
|  * additions, etc.
 | |
|  *  
 | |
|  * @author sglover
 | |
|  */
 | |
| 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;
 | |
|     private WorkflowService workflowService;
 | |
|     private TenantService tenantService;
 | |
|     private TenantAdminService tenantAdminService;
 | |
|     private boolean enforceTenantInNamespace = false;
 | |
| 
 | |
|     public void setEnforceTenantInNamespace(boolean enforceTenantInNamespace)
 | |
|     {
 | |
|         this.enforceTenantInNamespace = enforceTenantInNamespace;
 | |
|     }
 | |
| 
 | |
|     public void setTransactionService(TransactionService transactionService)
 | |
|     {
 | |
|         this.transactionService = transactionService;
 | |
|     }
 | |
| 
 | |
|     public void setQnameDAO(QNameDAO qnameDAO)
 | |
|     {
 | |
|         this.qnameDAO = qnameDAO;
 | |
|     }
 | |
| 
 | |
|     public void setDictionaryDAO(DictionaryDAO dictionaryDAO)
 | |
|     {
 | |
|         this.dictionaryDAO = dictionaryDAO;
 | |
|     }
 | |
| 
 | |
|     public void setNamespaceService(NamespaceService namespaceService)
 | |
|     {
 | |
|         this.namespaceService = namespaceService;
 | |
|     }
 | |
| 
 | |
|     public void setWorkflowService(WorkflowService workflowService)
 | |
|     {
 | |
|         this.workflowService = workflowService;
 | |
|     }
 | |
| 
 | |
|     public void setTenantService(TenantService tenantService)
 | |
|     {
 | |
|         this.tenantService = tenantService;
 | |
|     }
 | |
| 
 | |
|     public void setTenantAdminService(TenantAdminService tenantAdminService)
 | |
|     {
 | |
|         this.tenantAdminService = tenantAdminService;
 | |
|     }
 | |
| 
 | |
|     public void setDictionaryService(DictionaryService dictionaryService)
 | |
|     {
 | |
|         this.dictionaryService = dictionaryService;
 | |
|     }
 | |
| 
 | |
|     private void checkCustomModelNamespace(M2Model model, String tenantDomain)
 | |
|     {
 | |
|         if(tenantDomain != null && !tenantDomain.equals("") && enforceTenantInNamespace)
 | |
|         {
 | |
|             // check only for "real" tenants
 | |
|             for(M2Namespace namespace : model.getNamespaces())
 | |
|             {
 | |
|                 String namespaceURI = namespace.getUri();
 | |
|                 if(namespaceURI.indexOf(tenantDomain) == -1)
 | |
|                 {
 | |
|                     throw new DictionaryException("Namespace " + namespaceURI + " does not contain the tenant " + tenantDomain);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     private boolean canDeleteModel(Collection<NamespaceDefinition> namespaceDefs, Collection<TypeDefinition> typeDefs,
 | |
|             Collection<AspectDefinition> aspectDefs, Tenant tenant)
 | |
|     {
 | |
|         boolean canDelete = true;
 | |
| 
 | |
|         String tenantDomain = "for tenant ["
 | |
|               + (tenant == null ? TenantService.DEFAULT_DOMAIN : tenant.getTenantDomain()) + "]";
 | |
| 
 | |
|         List<WorkflowDefinition> workflowDefs = workflowService.getDefinitions();
 | |
|         
 | |
|         if (workflowDefs.size() > 0)
 | |
|         {
 | |
|             if (namespaceDefs.size() > 0)
 | |
|             {
 | |
|                 // check workflow namespace usage
 | |
|                 for (WorkflowDefinition workflowDef : workflowDefs)
 | |
|                 {
 | |
|                     String workflowDefName = workflowDef.getName();
 | |
|                     
 | |
|                     String workflowNamespaceURI = null;
 | |
|                     try
 | |
|                     {
 | |
|                         workflowNamespaceURI = QName.createQName(BPMEngineRegistry.getLocalId(workflowDefName), namespaceService).getNamespaceURI();
 | |
|                     }
 | |
|                     catch (NamespaceException ne)
 | |
|                     {
 | |
|                         logger.warn("Skipped workflow when validating model delete - unknown namespace: "+ne);
 | |
|                         continue;
 | |
|                     }
 | |
|                     
 | |
|                     for (NamespaceDefinition namespaceDef : namespaceDefs)
 | |
|                     {
 | |
|                         if (workflowNamespaceURI.equals(namespaceDef.getUri()))
 | |
|                         {
 | |
|                             logger.warn("Failed to validate model delete" + tenantDomain + " - found workflow process definition "
 | |
|                                     + workflowDefName + " using model namespace '" + namespaceDef.getUri() + "'");
 | |
|                             canDelete = false;
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // check for type usages
 | |
|         outer:
 | |
|         for (TypeDefinition type : typeDefs)
 | |
|         {
 | |
|             try
 | |
|             {
 | |
|                 validateDeleteClass(tenant, type);
 | |
|             }
 | |
|             catch(ModelInUseException e)
 | |
|             {
 | |
|                 canDelete = false;
 | |
|                 break outer;
 | |
|             }
 | |
|             catch(ModelNotInUseException e)
 | |
|             {
 | |
|                 // ok, continue
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // check for aspect usages
 | |
|         outer:
 | |
|         for (AspectDefinition aspect : aspectDefs)
 | |
|         {
 | |
|             try
 | |
|             {
 | |
|                 validateDeleteClass(tenant, aspect);
 | |
|             } catch(ModelInUseException e)
 | |
|             {
 | |
|                 canDelete = false;
 | |
|                 break outer;
 | |
|             }
 | |
|             catch(ModelNotInUseException e)
 | |
|             {
 | |
|                 // ok, continue
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return canDelete;
 | |
|     }
 | |
| 
 | |
|     private void validateDeleteClass(final Tenant tenant, final ClassDefinition classDef)
 | |
|     {
 | |
|         final String classType = "TYPE";
 | |
|         final QName className = classDef.getName();
 | |
| 
 | |
|         String tenantDomain = "for tenant ["
 | |
|                 + (tenant == null ? TenantService.DEFAULT_DOMAIN : tenant.getTenantDomain()) + "]";
 | |
| 
 | |
|         // We need a separate transaction to do the qname delete "check"
 | |
|         transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Void>()
 | |
|         {
 | |
|             @Override
 | |
|             public Void execute() throws Throwable
 | |
|             {
 | |
|                 try
 | |
|                 {
 | |
|                     // The class QName may not have been created in the database if no
 | |
|                     // properties have been created that use it, so check first and then
 | |
|                     // try to delete it.
 | |
|                     if(qnameDAO.getQName(className) != null)
 | |
|                     {
 | |
|                         qnameDAO.deleteQName(className);
 | |
|                     }
 | |
|                     throw new ModelNotInUseException("Class " + className + " not in use");
 | |
|                 }
 | |
|                 catch(DataIntegrityViolationException e)
 | |
|                 {
 | |
|                     // catch data integrity violation e.g. foreign key constraint exception
 | |
|                     logger.debug(e);
 | |
|                     throw new ModelInUseException("Cannot delete model, class "
 | |
|                             + className + " is in use");
 | |
|                 }
 | |
|             }
 | |
|         }, false, true);
 | |
| 
 | |
| 
 | |
|         // check against workflow task usage
 | |
|         for (WorkflowDefinition workflowDef : workflowService.getDefinitions())
 | |
|         {
 | |
|             for (WorkflowTaskDefinition workflowTaskDef : workflowService.getTaskDefinitions(workflowDef.getId()))
 | |
|             {
 | |
|                 TypeDefinition workflowTypeDef = workflowTaskDef.metadata;
 | |
|                 if (workflowTypeDef.getName().equals(className))
 | |
|                 {
 | |
|                     throw new AlfrescoRuntimeException("Failed to validate model delete" + tenantDomain + " - found task definition in workflow " 
 | |
|                             + workflowDef.getName() + " with " + classType + " '" + className + "'");
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     private void validateDeleteProperty(QName modelName, QName propertyQName, boolean sharedModel)
 | |
|     {
 | |
|         String tenantDomain = TenantService.DEFAULT_DOMAIN;
 | |
|         if (sharedModel)
 | |
|         {
 | |
|             tenantDomain = " for tenant [" + tenantService.getCurrentUserDomain() + "]";
 | |
|         }
 | |
| 
 | |
|         PropertyDefinition prop = dictionaryDAO.getProperty(propertyQName);
 | |
|         if(prop != null && prop.getName().equals(propertyQName) && prop.getModel().getName().equals(modelName))
 | |
|         {
 | |
|             validateDeleteProperty(tenantDomain, prop);
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             throw new AlfrescoRuntimeException("Cannot delete model " + modelName + " in tenant " + tenantDomain
 | |
|                     + " - property definition '" + propertyQName + "' not defined in model '" + modelName + "'");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     private void validateDeleteProperty(final String tenantDomain, final PropertyDefinition propDef)
 | |
|     {
 | |
|         final QName propName = propDef.getName();
 | |
| 
 | |
|         // We need a separate transaction to do the qname delete "check"
 | |
|         transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Void>()
 | |
|         {
 | |
|             @Override
 | |
|             public Void execute() throws Throwable
 | |
|             {
 | |
|                 return TenantUtil.runAsTenant(new TenantRunAsWork<Void>()
 | |
|                 {
 | |
|                     @Override
 | |
|                     public Void doWork() throws Exception
 | |
|                     {
 | |
|                         try
 | |
|                         {
 | |
|                             // The property QName may not have been created in the database if no
 | |
|                             // properties have been created that use it, so check first and then
 | |
|                             // try to delete it.
 | |
|                             if(qnameDAO.getQName(propName) != null)
 | |
|                             {
 | |
|                                 qnameDAO.deleteQName(propName);
 | |
|                             }
 | |
|                         }
 | |
|                         catch(DataIntegrityViolationException e)
 | |
|                         {
 | |
|                             // catch data integrity violation e.g. foreign key constraint exception
 | |
|                             logger.debug(e);
 | |
|                             throw new ModelInUseException("Failed to validate property delete, property " + propName + " is in use");
 | |
|                         }
 | |
| 
 | |
|                         return null;
 | |
|                     }
 | |
|                 }, tenantDomain);
 | |
|             }
 | |
|         }, false, true);
 | |
|     }
 | |
| 
 | |
|     // validate delete of a referencable constraint def
 | |
|     private void validateDeleteConstraint(CompiledModel compiledModel, QName constraintName, boolean sharedModel)
 | |
|     {
 | |
|         String tenantDomain = TenantService.DEFAULT_DOMAIN;
 | |
|         if (sharedModel)
 | |
|         {
 | |
|             tenantDomain = " for tenant [" + tenantService.getCurrentUserDomain() + "]";
 | |
|         }
 | |
|         
 | |
|         Set<QName> referencedBy = new HashSet<QName>(0);
 | |
|         
 | |
|         // check for references to constraint definition
 | |
|         // note: could be anon prop constraint (if no referenceable constraint)
 | |
|         Collection<QName> allModels = dictionaryDAO.getModels();
 | |
|         for (QName model : allModels)
 | |
|         {
 | |
|             Collection<PropertyDefinition> propDefs = null;
 | |
|             if (compiledModel.getModelDefinition().getName().equals(model))
 | |
|             {
 | |
|                 // TODO deal with multiple pending model updates
 | |
|                 propDefs = compiledModel.getProperties();
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 propDefs = dictionaryDAO.getProperties(model);
 | |
|             }
 | |
|             
 | |
|             for (PropertyDefinition propDef : propDefs)
 | |
|             {
 | |
|                 for (ConstraintDefinition conDef : propDef.getConstraints())
 | |
|                 {
 | |
|                     if (constraintName.equals(conDef.getRef()))
 | |
|                     {
 | |
|                         referencedBy.add(conDef.getName());
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         if (referencedBy.size() == 1)
 | |
|         {
 | |
|             throw new AlfrescoRuntimeException("Failed to validate constraint delete" + tenantDomain + " - constraint definition '" + constraintName + "' is being referenced by '" + referencedBy.toArray()[0] + "' property constraint");
 | |
|         }
 | |
|         else if (referencedBy.size() > 1)
 | |
|         {
 | |
|             throw new AlfrescoRuntimeException("Failed to validate constraint delete" + tenantDomain + " - constraint definition '" + constraintName + "' is being referenced by " + referencedBy.size() + " property constraints");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * {@inheritDoc}
 | |
|      */
 | |
|     @Override
 | |
|     public boolean canDeleteModel(final QName modelName)
 | |
|     {
 | |
|         boolean canDeleteModel = true;
 | |
| 
 | |
|         // TODO add model locking during delete (would need to be tenant-aware & cluster-aware) to avoid potential 
 | |
|         //      for concurrent addition of new content/workflow as model is being deleted
 | |
|         
 | |
|         final Collection<NamespaceDefinition> namespaceDefs;
 | |
|         final Collection<TypeDefinition> typeDefs;
 | |
|         final Collection<AspectDefinition> aspectDefs;
 | |
|         
 | |
|         try
 | |
|         {
 | |
|             namespaceDefs = dictionaryDAO.getNamespaces(modelName);
 | |
|             typeDefs = dictionaryDAO.getTypes(modelName);
 | |
|             aspectDefs = dictionaryDAO.getAspects(modelName);
 | |
| 
 | |
|             // TODO - in case of MT we do not currently allow deletion of an overridden model (with usages) ... but could allow if (re-)inherited model is equivalent to an incremental update only ?
 | |
|             canDeleteModel &= canDeleteModel(namespaceDefs, typeDefs, aspectDefs, null);
 | |
|             if(canDeleteModel)
 | |
|             {
 | |
|                 if (tenantService.isEnabled() && tenantService.isTenantUser() == false)
 | |
|                 {
 | |
|                     // TODO should fix this up - won't scale
 | |
|                     // shared model - need to check all tenants (whether enabled or disabled) unless they have overridden
 | |
|                     List<Tenant> tenants = tenantAdminService.getAllTenants();
 | |
|                     for (final Tenant tenant : tenants)
 | |
|                     {
 | |
|                         // validate model delete within context of tenant domain
 | |
|                         canDeleteModel &= AuthenticationUtil.runAs(new RunAsWork<Boolean>()
 | |
|                         {
 | |
|                             public Boolean doWork()
 | |
|                             {
 | |
|                                 boolean canDelete = canDeleteModel(namespaceDefs, typeDefs, aspectDefs, tenant);
 | |
|                                 return canDelete;
 | |
|                             }
 | |
|                         }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenant.getTenantDomain()));
 | |
| 
 | |
|                         if(!canDeleteModel)
 | |
|                         {
 | |
|                             break;
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         catch (DictionaryException e)
 | |
|         {
 | |
|             if (logger.isDebugEnabled())
 | |
|             {
 | |
|                 logger.debug("Dictionary model '" + modelName + "' does not exist ... skip delete validation : " + e);
 | |
|             }
 | |
|             // we must return true here - there is no model 
 | |
|             canDeleteModel = true;
 | |
|         }
 | |
| 
 | |
|         return canDeleteModel;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * {@inheritDoc}
 | |
|      */
 | |
|     @Override
 | |
|     public void validateModel(CompiledModel compiledModel)
 | |
|     {
 | |
|         ModelDefinition modelDef = compiledModel.getModelDefinition();
 | |
|         QName modelName = modelDef.getName();
 | |
|         M2Model model = compiledModel.getM2Model();
 | |
| 
 | |
|         checkCustomModelNamespace(model, TenantUtil.getCurrentDomain());
 | |
| 
 | |
|         List<M2ModelDiff> modelDiffs = dictionaryDAO.diffModel(model);
 | |
|         
 | |
|         for (M2ModelDiff modelDiff : modelDiffs)
 | |
|         {
 | |
|             if (modelDiff.getDiffType().equals(M2ModelDiff.DIFF_DELETED))
 | |
|             {
 | |
|                 // TODO - check tenants if model is shared / inherited
 | |
|                 if (modelDiff.getElementType().equals(M2ModelDiff.TYPE_PROPERTY))
 | |
|                 {
 | |
|                     validateDeleteProperty(modelName, modelDiff.getElementName(), false);
 | |
|                 }
 | |
|                 else if (modelDiff.getElementType().equals(M2ModelDiff.TYPE_CONSTRAINT))
 | |
|                 {
 | |
|                     validateDeleteConstraint(compiledModel, modelDiff.getElementName(), false);
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     /*
 | |
|                      * As the M2Model#compile method will detect and throw exception for any missing namespace which
 | |
|                      * is required to define any Type, Aspect or Property, we can safely add this extra check.
 | |
|                      * See APPSREPO-59 comment for details.
 | |
|                      */
 | |
|                     if (!modelDiff.getElementType().equals(M2ModelDiff.TYPE_NAMESPACE))
 | |
|                     {
 | |
|                         throw new AlfrescoRuntimeException("Failed to validate model update - found deleted " + modelDiff.getElementType() + " '" + modelDiff
 | |
|                                                 .getElementName() + "'");
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             if (modelDiff.getDiffType().equals(M2ModelDiff.DIFF_UPDATED))
 | |
|             {
 | |
|                 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);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
| }
 |