mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-06-16 17:55:15 +00:00
Fixes for dynamic model management: fix delete model validation (in MT case) & fix model restore from archive
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@6984 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
parent
5d0a520f25
commit
714c7a2b14
@ -88,7 +88,8 @@
|
||||
<property name="workflowService" ref="WorkflowService"/>
|
||||
<property name="searchService" ref="SearchService"/>
|
||||
<property name="namespaceService" ref="namespaceService"/>
|
||||
<property name="tenantService" ref="tenantService"/>
|
||||
<property name="tenantService" ref="tenantService"/>
|
||||
<property name="tenantDeployerService" ref="tenantAdminService"/>
|
||||
|
||||
<property name="storeUrls">
|
||||
<list>
|
||||
|
@ -172,4 +172,7 @@ public interface DictionaryDAO extends ModelQuery
|
||||
* Destroy the Dictionary
|
||||
*/
|
||||
public void destroy();
|
||||
|
||||
// MT-specific
|
||||
public boolean isModelInherited(QName name);
|
||||
}
|
||||
|
@ -704,23 +704,54 @@ public class DictionaryDAOImpl implements DictionaryDAO
|
||||
*/
|
||||
public Collection<QName> getModels()
|
||||
{
|
||||
// get all models - including inherited models, if applicable
|
||||
return getCompiledModels().keySet();
|
||||
}
|
||||
|
||||
// return all tenant-specific models and all shared (non-overridden) models
|
||||
// MT-specific
|
||||
public boolean isModelInherited(QName modelName)
|
||||
{
|
||||
String tenantDomain = tenantService.getCurrentUserDomain();
|
||||
if (tenantDomain != "")
|
||||
{
|
||||
// get tenant-specific model (if any)
|
||||
CompiledModel model = getCompiledModels(tenantDomain).get(modelName);
|
||||
if (model != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// else drop down to check for shared (core/system) models ...
|
||||
}
|
||||
|
||||
// get non-tenant model (if any)
|
||||
CompiledModel model = getCompiledModels("").get(modelName);
|
||||
if (model == null)
|
||||
{
|
||||
throw new DictionaryException("d_dictionary.model.err.no_model", modelName);
|
||||
}
|
||||
|
||||
if (tenantDomain != "")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private Map<QName,CompiledModel> getCompiledModels()
|
||||
{
|
||||
String tenantDomain = tenantService.getCurrentUserDomain();
|
||||
if (tenantDomain != "")
|
||||
{
|
||||
// return all tenant-specific models and all shared (non-overridden) models
|
||||
// return all tenant-specific models and all inherited (non-overridden) models
|
||||
Map<QName, CompiledModel> filteredModels = new HashMap<QName, CompiledModel>();
|
||||
|
||||
// get tenant models (if any)
|
||||
Map<QName,CompiledModel> tenantModels = getCompiledModels(tenantDomain);
|
||||
|
||||
// get non-tenant models (if any)
|
||||
// note: these will be shared, if not overridden - could be core/system model or additional custom model shared between tenants
|
||||
// get non-tenant models - these will include core/system models and any additional custom models (which are implicitly available to all tenants)
|
||||
Map<QName,CompiledModel> nontenantModels = getCompiledModels("");
|
||||
|
||||
// check for overrides
|
||||
@ -744,15 +775,10 @@ public class DictionaryDAOImpl implements DictionaryDAO
|
||||
}
|
||||
else
|
||||
{
|
||||
// return all (non-tenant) models
|
||||
return getCompiledModels("");
|
||||
}
|
||||
}
|
||||
|
||||
// used for clean-up, e.g. when deleting a tenant
|
||||
protected Collection<QName> getNonSharedModels()
|
||||
{
|
||||
return getCompiledModels(tenantService.getCurrentUserDomain()).keySet();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.dictionary.impl.DictionaryDAO#getModel(org.alfresco.repo.ref.QName)
|
||||
|
@ -39,6 +39,8 @@ import org.alfresco.repo.policy.JavaBehaviour;
|
||||
import org.alfresco.repo.policy.PolicyComponent;
|
||||
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.TenantDeployerService;
|
||||
import org.alfresco.repo.tenant.TenantService;
|
||||
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
||||
import org.alfresco.repo.transaction.TransactionListener;
|
||||
@ -49,6 +51,7 @@ import org.alfresco.service.cmr.dictionary.DictionaryException;
|
||||
import org.alfresco.service.cmr.dictionary.ModelDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.NamespaceDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.TypeDefinition;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.ContentReader;
|
||||
import org.alfresco.service.cmr.repository.ContentService;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
@ -73,6 +76,7 @@ import org.apache.commons.logging.LogFactory;
|
||||
public class DictionaryModelType implements ContentServicePolicies.OnContentUpdatePolicy,
|
||||
NodeServicePolicies.OnUpdatePropertiesPolicy,
|
||||
NodeServicePolicies.BeforeDeleteNodePolicy,
|
||||
NodeServicePolicies.OnCreateNodePolicy,
|
||||
NodeServicePolicies.OnRemoveAspectPolicy
|
||||
{
|
||||
// Logger
|
||||
@ -81,9 +85,12 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
|
||||
/** Key to the pending models */
|
||||
private static final String KEY_PENDING_MODELS = "dictionaryModelType.pendingModels";
|
||||
|
||||
/** Key to the removed aspect */
|
||||
/** Key to the removed "workingcopy" aspect */
|
||||
private static final String KEY_WORKING_COPY = "dictionaryModelType.workingCopy";
|
||||
|
||||
/** Key to the removed "archived" aspect */
|
||||
private static final String KEY_ARCHIVED = "dictionaryModelType.archived";
|
||||
|
||||
/** The dictionary DAO */
|
||||
private DictionaryDAO dictionaryDAO;
|
||||
|
||||
@ -111,6 +118,9 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
|
||||
/** The tenant service */
|
||||
private TenantService tenantService;
|
||||
|
||||
/** The tenant deployer service */
|
||||
private TenantDeployerService tenantDeployerService;
|
||||
|
||||
/** Transaction listener */
|
||||
private DictionaryModelTypeTransactionListener transactionListener;
|
||||
|
||||
@ -207,6 +217,15 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
|
||||
this.tenantService = tenantService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the tenant admin service
|
||||
*
|
||||
* @param tenantAdminService the tenant admin service
|
||||
*/
|
||||
public void setTenantDeployerService(TenantDeployerService tenantDeployerService)
|
||||
{
|
||||
this.tenantDeployerService = tenantDeployerService;
|
||||
}
|
||||
|
||||
public void setStoreUrls(List<String> storeUrls)
|
||||
{
|
||||
@ -243,6 +262,12 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
|
||||
this,
|
||||
new JavaBehaviour(this, "onRemoveAspect"));
|
||||
|
||||
// Register interest in the onCreateNode policy for the dictionary model type
|
||||
policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateNode"),
|
||||
ContentModel.TYPE_DICTIONARY_MODEL,
|
||||
new JavaBehaviour(this, "onCreateNode"));
|
||||
|
||||
// Create the transaction listener
|
||||
this.transactionListener = new DictionaryModelTypeTransactionListener(this.nodeService, this.contentService);
|
||||
}
|
||||
@ -303,26 +328,37 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
|
||||
|
||||
public void onRemoveAspect(NodeRef nodeRef, QName aspect)
|
||||
{
|
||||
// undo/cancel checkout removes the aspect prior to deleting the node - hence need to track here
|
||||
// undo/cancel checkout removes the "workingcopy" aspect prior to deleting the node - hence need to track here
|
||||
if (aspect.equals(ContentModel.ASPECT_WORKING_COPY))
|
||||
{
|
||||
AlfrescoTransactionSupport.bindResource(KEY_WORKING_COPY, nodeRef);
|
||||
}
|
||||
|
||||
// restore removes the "archived" aspect prior to restoring (via delete/move) the node - hence need to track here
|
||||
if (aspect.equals(ContentModel.ASPECT_ARCHIVED))
|
||||
{
|
||||
AlfrescoTransactionSupport.bindResource(KEY_ARCHIVED, nodeRef);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void beforeDeleteNode(NodeRef nodeRef)
|
||||
{
|
||||
boolean workingCopy = nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY);
|
||||
|
||||
NodeRef wcNodeRef = (NodeRef)AlfrescoTransactionSupport.getResource(KEY_WORKING_COPY);
|
||||
if ((wcNodeRef != null) && (wcNodeRef.equals(nodeRef)))
|
||||
{
|
||||
workingCopy = true;
|
||||
}
|
||||
|
||||
boolean archived = nodeService.hasAspect(nodeRef, ContentModel.ASPECT_ARCHIVED);
|
||||
NodeRef aNodeRef = (NodeRef)AlfrescoTransactionSupport.getResource(KEY_ARCHIVED);
|
||||
if ((aNodeRef != null) && (aNodeRef.equals(nodeRef)))
|
||||
{
|
||||
archived = true;
|
||||
}
|
||||
|
||||
// Ignore if the node is a working copy or archived
|
||||
if ((workingCopy == false) && (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_ARCHIVED) == false))
|
||||
if (! (workingCopy || archived))
|
||||
{
|
||||
QName modelName = (QName)this.nodeService.getProperty(nodeRef, ContentModel.PROP_MODEL_NAME);
|
||||
if (modelName != null)
|
||||
@ -336,6 +372,16 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
|
||||
}
|
||||
}
|
||||
|
||||
public void onCreateNode(ChildAssociationRef childAssocRef)
|
||||
{
|
||||
NodeRef nodeRef = childAssocRef.getChildRef();
|
||||
Boolean value = (Boolean)nodeService.getProperty(nodeRef, ContentModel.PROP_MODEL_ACTIVE);
|
||||
if ((value != null) && (value == true))
|
||||
{
|
||||
queueModel(nodeRef);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dictionary model type transaction listener class.
|
||||
*/
|
||||
@ -372,11 +418,13 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
|
||||
|
||||
if (pendingModels != null)
|
||||
{
|
||||
for (final NodeRef nodeRef : pendingModels)
|
||||
for (NodeRef pendingNodeRef : pendingModels)
|
||||
{
|
||||
String tenantDomain = tenantService.getDomain(nodeRef.getStoreRef().getIdentifier());
|
||||
String tenantDomain = tenantService.getDomain(pendingNodeRef.getStoreRef().getIdentifier());
|
||||
String tenantAdminUserName = tenantService.getDomainUser(TenantService.ADMIN_BASENAME, tenantDomain);
|
||||
|
||||
final NodeRef nodeRef = tenantService.getBaseName(pendingNodeRef);
|
||||
|
||||
AuthenticationUtil.runAs(new RunAsWork<Object>()
|
||||
{
|
||||
public Object doWork()
|
||||
@ -390,8 +438,7 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
|
||||
}
|
||||
|
||||
// Ignore if the node is a working copy or archived
|
||||
if ((nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY) == false) &&
|
||||
(nodeService.hasAspect(nodeRef, ContentModel.ASPECT_ARCHIVED) == false))
|
||||
if (! (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY) || nodeService.hasAspect(nodeRef, ContentModel.ASPECT_ARCHIVED)))
|
||||
{
|
||||
if (isActive == true)
|
||||
{
|
||||
@ -476,7 +523,6 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
|
||||
/**
|
||||
* @see org.alfresco.repo.transaction.TransactionListener#afterRollback()
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void afterRollback()
|
||||
{
|
||||
}
|
||||
@ -508,7 +554,7 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
|
||||
*
|
||||
* @param modelName
|
||||
*/
|
||||
private void validateModelDelete(QName modelName)
|
||||
private void validateModelDelete(final QName modelName)
|
||||
{
|
||||
// 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
|
||||
@ -522,7 +568,45 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
|
||||
logger.warn("Model ' + modelName + ' does not exist ... skip delete validation : " + e);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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 ?
|
||||
validateModelDelete(modelName, false);
|
||||
|
||||
if (tenantService.isEnabled() && tenantService.isTenantUser() == false)
|
||||
{
|
||||
// shared model - need to check all tenants (whether enabled or disabled) unless they have overridden
|
||||
List<Tenant> tenants = tenantDeployerService.getAllTenants();
|
||||
|
||||
if (tenants != null)
|
||||
{
|
||||
for (Tenant tenant : tenants)
|
||||
{
|
||||
// switch to admin in order to validate model delete within context of tenant domain
|
||||
// assumes each tenant has default "admin" user
|
||||
AuthenticationUtil.runAs(new RunAsWork<Object>()
|
||||
{
|
||||
public Object doWork()
|
||||
{
|
||||
if (dictionaryDAO.isModelInherited(modelName))
|
||||
{
|
||||
validateModelDelete(modelName, true);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}, tenantService.getDomainUser(TenantService.ADMIN_BASENAME, tenant.getTenantDomain()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validateModelDelete(QName modelName, boolean sharedModel)
|
||||
{
|
||||
String tenantDomainCtx = "";
|
||||
if (sharedModel)
|
||||
{
|
||||
tenantDomainCtx = " for tenant [" + tenantService.getCurrentUserDomain() + "]";
|
||||
}
|
||||
|
||||
// check workflow namespace usage
|
||||
for (WorkflowDefinition workflowDef : workflowService.getDefinitions())
|
||||
{
|
||||
@ -532,7 +616,7 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
|
||||
{
|
||||
if (workflowNamespaceURI.equals(namespace.getUri()))
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Failed to validate model delete - found workflow process definition " + workflowDefName + " using model namespace '" + namespace.getUri() + "'");
|
||||
throw new AlfrescoRuntimeException("Failed to validate model delete" + tenantDomainCtx + " - found workflow process definition " + workflowDefName + " using model namespace '" + namespace.getUri() + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -540,17 +624,17 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
|
||||
// check for type usages
|
||||
for (TypeDefinition type : dictionaryDAO.getTypes(modelName))
|
||||
{
|
||||
validateClass(type);
|
||||
validateClass(tenantDomainCtx, type);
|
||||
}
|
||||
|
||||
// check for aspect usages
|
||||
for (AspectDefinition aspect : dictionaryDAO.getAspects(modelName))
|
||||
{
|
||||
validateClass(aspect);
|
||||
validateClass(tenantDomainCtx, aspect);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateClass(ClassDefinition classDef)
|
||||
private void validateClass(String tenantDomainCtx, ClassDefinition classDef)
|
||||
{
|
||||
QName className = classDef.getName();
|
||||
|
||||
@ -568,7 +652,7 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
|
||||
ResultSet rs = searchService.query(store, SearchService.LANGUAGE_LUCENE, classType+":\""+className+"\"");
|
||||
if (rs.length() > 0)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Failed to validate model delete - found " + rs.length() + " nodes in store " + store + " with " + classType + " '" + className + "'");
|
||||
throw new AlfrescoRuntimeException("Failed to validate model delete" + tenantDomainCtx + " - found " + rs.length() + " nodes in store " + store + " with " + classType + " '" + className + "'" );
|
||||
}
|
||||
}
|
||||
|
||||
@ -580,7 +664,7 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
|
||||
TypeDefinition workflowTypeDef = workflowTaskDef.metadata;
|
||||
if (workflowTypeDef.getName().toString().equals(className))
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Failed to validate model delete - found task definition in workflow " + workflowDef.getName() + " with " + classType + " '" + className + "'");
|
||||
throw new AlfrescoRuntimeException("Failed to validate model delete" + tenantDomainCtx + " - found task definition in workflow " + workflowDef.getName() + " with " + classType + " '" + className + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1938,8 +1938,10 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC),
|
||||
properties.get(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC));
|
||||
PropertyValue originalOwnerProperty = properties.get(ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER);
|
||||
// remove the aspect archived aspect
|
||||
aspects.remove(ContentModel.ASPECT_ARCHIVED);
|
||||
|
||||
// remove the archived aspect
|
||||
removeAspect(archivedNodeRef, ContentModel.ASPECT_ARCHIVED); // allow policy to fire, e.g. for DictionaryModelType
|
||||
|
||||
properties.remove(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC);
|
||||
properties.remove(ContentModel.PROP_ARCHIVED_BY);
|
||||
properties.remove(ContentModel.PROP_ARCHIVED_DATE);
|
||||
|
@ -24,6 +24,8 @@
|
||||
*/
|
||||
package org.alfresco.repo.tenant;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
/**
|
||||
@ -51,4 +53,9 @@ public class SingleTDeployerServiceImpl implements TenantDeployerService
|
||||
{
|
||||
// NOOP
|
||||
}
|
||||
|
||||
public List<Tenant> getAllTenants()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
@ -24,6 +24,8 @@
|
||||
*/
|
||||
package org.alfresco.repo.tenant;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
|
||||
@ -44,4 +46,6 @@ public interface TenantDeployerService
|
||||
public void register(TenantDeployer tenantDeployer);
|
||||
|
||||
public void unregister(TenantDeployer tenantDeployer);
|
||||
|
||||
public List<Tenant> getAllTenants();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user