Merged V3.2 to HEAD

19507: ALF-955: deletion of dynamic custom model
   19553: Follow-on to r19507 (ALF-955) - fix testAutoRemovalOfVersionHistory


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@19569 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Jan Vonka
2010-03-25 09:30:11 +00:00
parent fcffbe3af8
commit f3b8452415
10 changed files with 563 additions and 169 deletions

View File

@@ -84,7 +84,7 @@
<bean id="storeArchiveMap" class="org.alfresco.repo.node.StoreArchiveMap">
<property name="archiveMap">
<map>
<entry key="workspace://SpacesStore"><value>archive://SpacesStore</value></entry>
<entry key="workspace://SpacesStore"><value>${spaces.archive.store}</value></entry>
</map>
</property>
<property name="tenantService">
@@ -233,6 +233,14 @@
<property name="cascadeInTransaction">
<value>${system.cascadeDeleteInTransaction}</value>
</property>
<property name="tenantService">
<ref bean="tenantService"/>
</property>
<property name="storesToIgnorePolicies">
<list>
<value>${spaces.archive.store}</value>
</list>
</property>
</bean>
<bean id="nodeAuditor" class="org.alfresco.repo.node.NodeAuditor">

View File

@@ -27,18 +27,31 @@ package org.alfresco.repo.admin;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import junit.framework.TestCase;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.service.cmr.admin.RepoAdminService;
import org.alfresco.service.cmr.dictionary.ClassDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper;
@@ -61,6 +74,11 @@ public class RepoAdminServiceImplTest extends TestCase
private DictionaryService dictionaryService;
private TransactionService transactionService;
private NodeService nodeService;
private ContentService contentService;
private SearchService searchService;
private NamespaceService namespaceService;
final String modelPrefix = "model-";
final static String MKR = "{MKR}";
@@ -107,6 +125,11 @@ public class RepoAdminServiceImplTest extends TestCase
dictionaryService = (DictionaryService) ctx.getBean("DictionaryService");
transactionService = (TransactionService) ctx.getBean("TransactionService");
nodeService = (NodeService) ctx.getBean("NodeService");
contentService = (ContentService) ctx.getBean("ContentService");
searchService = (SearchService) ctx.getBean("SearchService");
namespaceService = (NamespaceService) ctx.getBean("NamespaceService");
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
}
@@ -125,9 +148,140 @@ public class RepoAdminServiceImplTest extends TestCase
// Test custom model management
//
public void testSimpleDynamicModel() throws Exception
public void testSimpleDynamicModelViaNodeService() throws Exception
{
final int X = 0;
final String X = "A";
final String modelFileName = modelPrefix+X+".xml";
final QName typeName = QName.createQName("{http://www.alfresco.org/test/testmodel"+X+"/1.0}base");
final QName modelName = QName.createQName("{http://www.alfresco.org/test/testmodel"+X+"/1.0}testModel"+X);
try
{
if (isModelDeployed(modelFileName))
{
// undeploy model
repoAdminService.undeployModel(modelFileName);
}
StoreRef storeRef = StoreRef.STORE_REF_WORKSPACE_SPACESSTORE;
NodeRef rootNodeRef = nodeService.getRootNode(storeRef);
assertNull(dictionaryService.getClass(typeName));
final int defaultModelCnt = dictionaryService.getAllModels().size();
// deploy custom model
String model = MODEL_MKR_XML.replace(MKR, X+"");
InputStream modelStream = new ByteArrayInputStream(model.getBytes("UTF-8"));
List<NodeRef> nodeRefs = searchService.selectNodes(rootNodeRef, "/app:company_home/app:dictionary/app:models", null, namespaceService, false);
assertEquals(1, nodeRefs.size());
NodeRef modelsNodeRef = nodeRefs.get(0);
// create model node
Map<QName, Serializable> contentProps = new HashMap<QName, Serializable>();
contentProps.put(ContentModel.PROP_NAME, modelFileName);
NodeRef model1 = nodeService.createNode(
modelsNodeRef,
ContentModel.ASSOC_CONTAINS,
modelName,
ContentModel.TYPE_DICTIONARY_MODEL,
contentProps).getChildRef();
// add titled aspect (for Web Client display)
Map<QName, Serializable> titledProps = new HashMap<QName, Serializable>();
titledProps.put(ContentModel.PROP_TITLE, modelFileName);
titledProps.put(ContentModel.PROP_DESCRIPTION, modelFileName);
nodeService.addAspect(model1, ContentModel.ASPECT_TITLED, titledProps);
ContentWriter writer = contentService.getWriter(model1, ContentModel.PROP_CONTENT, true);
writer.setMimetype(MimetypeMap.MIMETYPE_XML);
writer.setEncoding("UTF-8");
writer.putContent(modelStream); // also invokes policies for DictionaryModelType - e.g. onContentUpdate
modelStream.close();
// activate the model
nodeService.setProperty(model1, ContentModel.PROP_MODEL_ACTIVE, new Boolean(true));
assertEquals(defaultModelCnt+1, dictionaryService.getAllModels().size());
ClassDefinition myType = dictionaryService.getClass(typeName);
assertNotNull(myType);
assertEquals(modelName, myType.getModel().getName());
// create node with custom type
NodeRef node1 = nodeService.createNode(
rootNodeRef,
ContentModel.ASSOC_CHILDREN,
QName.createQName("http://www.alfresco.org/model/system/1.0", "node1"),
typeName,
null).getChildRef();
// try to delete the model
try
{
nodeService.deleteNode(model1);
fail();
}
catch (AlfrescoRuntimeException are)
{
// expected
assertTrue(are.getMessage().contains("Failed to validate model delete"));
}
nodeService.deleteNode(node1);
assertFalse(nodeService.exists(node1));
NodeRef archiveRootNode = nodeService.getStoreArchiveNode(storeRef);
NodeRef archiveNode1 = new NodeRef(archiveRootNode.getStoreRef(), node1.getId());
assertTrue(nodeService.exists(archiveNode1));
// try to delete the model
try
{
nodeService.deleteNode(model1);
fail();
}
catch (AlfrescoRuntimeException are)
{
// expected
assertTrue(are.getMessage().contains("Failed to validate model delete"));
}
nodeService.deleteNode(archiveNode1);
assertFalse(nodeService.exists(archiveNode1));
// delete model
nodeService.deleteNode(model1);
assertEquals(defaultModelCnt, dictionaryService.getAllModels().size());
assertNull(dictionaryService.getClass(typeName));
NodeRef archiveModel1 = new NodeRef(archiveRootNode.getStoreRef(), model1.getId());
// restore model
nodeService.restoreNode(archiveModel1, null, null, null);
assertEquals(defaultModelCnt+1, dictionaryService.getAllModels().size());
assertNotNull(dictionaryService.getClass(typeName));
// delete for good
nodeService.deleteNode(model1);
nodeService.deleteNode(archiveModel1);
}
finally
{
// NOOP
}
}
public void testSimpleDynamicModelViaRepoAdminService() throws Exception
{
final String X = "B";
final String modelFileName = modelPrefix+X+".xml";
final QName typeName = QName.createQName("{http://www.alfresco.org/test/testmodel"+X+"/1.0}base");
final QName modelName = QName.createQName("{http://www.alfresco.org/test/testmodel"+X+"/1.0}testModel"+X);
@@ -180,6 +334,7 @@ public class RepoAdminServiceImplTest extends TestCase
{
// expected
assertTrue(are.getMessage().contains("Model deactivation failed"));
assertTrue(are.getCause().getMessage().contains("is already deactivated"));
}
// re-activate model
@@ -202,9 +357,57 @@ public class RepoAdminServiceImplTest extends TestCase
{
// expected
assertTrue(are.getMessage().contains("Model activation failed"));
assertTrue(are.getCause().getMessage().contains("is already activated"));
}
// undeploy model
StoreRef storeRef = StoreRef.STORE_REF_WORKSPACE_SPACESSTORE;
NodeRef rootNodeRef = nodeService.getRootNode(storeRef);
// create node with custom type
NodeRef node1 = nodeService.createNode(
rootNodeRef,
ContentModel.ASSOC_CHILDREN,
QName.createQName("http://www.alfresco.org/model/system/1.0", "node1"),
typeName,
null).getChildRef();
// try to undeploy model
try
{
repoAdminService.undeployModel(modelFileName);
fail();
}
catch (AlfrescoRuntimeException are)
{
// expected
assertTrue(are.getMessage().contains("Model undeployment failed"));
assertTrue(are.getCause().getMessage().contains("Failed to validate model delete"));
}
nodeService.deleteNode(node1);
assertFalse(nodeService.exists(node1));
NodeRef archiveRootNode = nodeService.getStoreArchiveNode(storeRef);
NodeRef archiveNode1 = new NodeRef(archiveRootNode.getStoreRef(), node1.getId());
assertTrue(nodeService.exists(archiveNode1));
// try to undeploy model
try
{
repoAdminService.undeployModel(modelFileName);
fail();
}
catch (AlfrescoRuntimeException are)
{
// expected
assertTrue(are.getMessage().contains("Model undeployment failed"));
assertTrue(are.getCause().getMessage().contains("Failed to validate model delete"));
}
nodeService.deleteNode(archiveNode1);
assertFalse(nodeService.exists(archiveNode1));
// undeploy
repoAdminService.undeployModel(modelFileName);
assertFalse(isModelDeployed(modelFileName));
@@ -221,6 +424,7 @@ public class RepoAdminServiceImplTest extends TestCase
{
// expected
assertTrue(are.getMessage().contains("Model undeployment failed"));
assertTrue(are.getCause().getMessage().contains("Could not find custom model"));
}
}
finally

View File

@@ -123,6 +123,11 @@ public class DictionaryBootstrap implements DictionaryListener
{
long startTime = System.currentTimeMillis();
if (logger.isTraceEnabled())
{
logger.trace("onDictionaryInit: ["+Thread.currentThread()+"]");
}
Collection<QName> modelsBefore = dictionaryDAO.getModels(); // note: on first bootstrap will init empty dictionary
int modelsBeforeCnt = (modelsBefore != null ? modelsBefore.size() : 0);
@@ -158,7 +163,7 @@ public class DictionaryBootstrap implements DictionaryListener
if (logger.isDebugEnabled())
{
logger.debug("Model count: before="+modelsBeforeCnt+", load="+models.size()+", after="+modelsAfterCnt+" in "+(System.currentTimeMillis()-startTime)+" msecs");
logger.debug("Model count: before="+modelsBeforeCnt+", load="+models.size()+", after="+modelsAfterCnt+" in "+(System.currentTimeMillis()-startTime)+" msecs ["+Thread.currentThread()+"]");
}
}
}

View File

@@ -175,6 +175,11 @@ public class DictionaryDAOImpl implements DictionaryDAO
{
long startTime = System.currentTimeMillis();
if (logger.isDebugEnabled())
{
logger.debug("Init Dictionary: ["+Thread.currentThread()+"] "+(tenantDomain.equals(TenantService.DEFAULT_DOMAIN) ? "" : " (Tenant: "+tenantDomain+")"));
}
try
{
return AuthenticationUtil.runAs(new RunAsWork<DictionaryRegistry>()
@@ -183,9 +188,6 @@ public class DictionaryDAOImpl implements DictionaryDAO
{
try
{
// create threadlocal, if needed
createDataDictionaryLocal(tenantDomain);
DictionaryRegistry dictionaryRegistry = initDictionaryRegistry(tenantDomain);
if (dictionaryRegistry == null)
@@ -228,17 +230,25 @@ public class DictionaryDAOImpl implements DictionaryDAO
{
if (logger.isInfoEnabled())
{
logger.info("Init Dictionary: model count = "+(getModels() != null ? getModels().size() : 0) +" in "+(System.currentTimeMillis()-startTime)+" msecs "+(tenantDomain.equals(TenantService.DEFAULT_DOMAIN) ? "" : " (Tenant: "+tenantDomain+")"));
logger.info("Init Dictionary: model count = "+(getModels() != null ? getModels().size() : 0) +" in "+(System.currentTimeMillis()-startTime)+" msecs ["+Thread.currentThread()+"] "+(tenantDomain.equals(TenantService.DEFAULT_DOMAIN) ? "" : " (Tenant: "+tenantDomain+")"));
}
}
}
private DictionaryRegistry initDictionaryRegistry(String tenantDomain)
{
getDictionaryRegistry(tenantDomain).setCompiledModels(new HashMap<QName,CompiledModel>());
getDictionaryRegistry(tenantDomain).setUriToModels(new HashMap<String, List<CompiledModel>>());
// create threadlocal, if needed
DictionaryRegistry dictionaryRegistry = createDataDictionaryLocal(tenantDomain);
// initialise empty dictionary & namespaces
dictionaryRegistry.setCompiledModels(new HashMap<QName,CompiledModel>());
dictionaryRegistry.setUriToModels(new HashMap<String, List<CompiledModel>>());
if (logger.isTraceEnabled())
{
logger.trace("Empty dictionary initialised: "+dictionaryRegistry+" - "+defaultDictionaryRegistryThreadLocal+" ["+Thread.currentThread()+"]");
}
// initialise empty namespaces
namespaceDAO.init();
// populate the dictionary based on registered sources
@@ -253,7 +263,7 @@ public class DictionaryDAOImpl implements DictionaryDAO
dictionaryListener.afterDictionaryInit();
}
return getDictionaryRegistryLocal(tenantDomain);
return dictionaryRegistry;
}
/* (non-Javadoc)
@@ -1018,7 +1028,7 @@ public class DictionaryDAOImpl implements DictionaryDAO
}
// create threadlocal
private void createDataDictionaryLocal(String tenantDomain)
private DictionaryRegistry createDataDictionaryLocal(String tenantDomain)
{
// create threadlocal, if needed
DictionaryRegistry dictionaryRegistry = getDictionaryRegistryLocal(tenantDomain);
@@ -1035,6 +1045,8 @@ public class DictionaryDAOImpl implements DictionaryDAO
dictionaryRegistryThreadLocal.set(dictionaryRegistry);
}
}
return dictionaryRegistry;
}
// get threadlocal

View File

@@ -92,9 +92,6 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
/** 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;
@@ -349,12 +346,6 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
{
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")
@@ -367,17 +358,10 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
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;
}
boolean isVersionNode = nodeRef.getStoreRef().getIdentifier().equals(Version2Model.STORE_ID);
// Ignore if the node is a working copy, archived or version node
if (! (workingCopy || archived || isVersionNode))
// Ignore if the node is a working copy or version node
if (! (workingCopy || isVersionNode))
{
QName modelName = (QName)this.nodeService.getProperty(nodeRef, ContentModel.PROP_MODEL_NAME);
@@ -407,7 +391,7 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
{
if (logger.isTraceEnabled())
{
logger.trace("beforeDeleteNode: nodeRef="+nodeRef+ " ignored ("+(workingCopy ? " workingCopy " : "")+(archived ? " archived " : "")+(isVersionNode ? " isVersionNode " : "")+") ["+AlfrescoTransactionSupport.getTransactionId()+"]");
logger.trace("beforeDeleteNode: nodeRef="+nodeRef+ " ignored ("+(workingCopy ? " workingCopy " : "")+(isVersionNode ? " isVersionNode " : "")+") ["+AlfrescoTransactionSupport.getTransactionId()+"]");
}
}
}
@@ -606,8 +590,8 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
isActive = value.booleanValue();
}
// Ignore if the node is a working copy or archived
if (! (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY) || nodeService.hasAspect(nodeRef, ContentModel.ASPECT_ARCHIVED)))
// Ignore if the node is a working copy
if (! (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY)))
{
if (isActive == true)
{

View File

@@ -33,7 +33,6 @@ import org.alfresco.repo.i18n.MessageService;
import org.alfresco.repo.tenant.TenantAdminService;
import org.alfresco.repo.tenant.TenantDeployer;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentReader;
@@ -211,6 +210,11 @@ public class DictionaryRepositoryBootstrap extends AbstractLifecycleBean impleme
{
long startTime = System.currentTimeMillis();
if (logger.isTraceEnabled())
{
logger.trace("onDictionaryInit: ["+Thread.currentThread()+"]");
}
Collection<QName> modelsBefore = dictionaryDAO.getModels();
int modelsBeforeCnt = (modelsBefore != null ? modelsBefore.size() : 0);
@@ -220,6 +224,11 @@ public class DictionaryRepositoryBootstrap extends AbstractLifecycleBean impleme
{
Map<String, Pair<RepositoryLocation, M2Model>> modelMap = new HashMap<String, Pair<RepositoryLocation, M2Model>>();
if (logger.isTraceEnabled())
{
logger.trace("onDictionaryInit: locations="+this.repositoryModelsLocations);
}
// Register the models found in the repository
for (RepositoryLocation repositoryLocation : this.repositoryModelsLocations)
@@ -289,14 +298,14 @@ public class DictionaryRepositoryBootstrap extends AbstractLifecycleBean impleme
if (modelsAfterCnt != (modelsBeforeCnt + loadedModels.size()))
{
String tenantDomain = tenantAdminService.getCurrentUserDomain();
logger.warn("Model count: before="+modelsBeforeCnt+", load="+loadedModels.size()+", after="+modelsAfterCnt+" ["+AlfrescoTransactionSupport.getTransactionId()+"] in "+(System.currentTimeMillis()-startTime)+" msecs "+(tenantDomain.equals(TenantService.DEFAULT_DOMAIN) ? "" : " (Tenant: "+tenantDomain+")"));
logger.warn("Model count: before="+modelsBeforeCnt+", load="+loadedModels.size()+", after="+modelsAfterCnt+" in "+(System.currentTimeMillis()-startTime)+" msecs ["+Thread.currentThread()+"] "+(tenantDomain.equals(TenantService.DEFAULT_DOMAIN) ? "" : " (Tenant: "+tenantDomain+")"));
}
else
{
if (logger.isDebugEnabled())
{
String tenantDomain = tenantAdminService.getCurrentUserDomain();
logger.debug("Model count: before="+modelsBeforeCnt+", load="+loadedModels.size()+", after="+modelsAfterCnt+" ["+AlfrescoTransactionSupport.getTransactionId()+"] in "+(System.currentTimeMillis()-startTime)+" msecs "+(tenantDomain.equals(TenantService.DEFAULT_DOMAIN) ? "" : " (Tenant: "+tenantDomain+")"));
logger.debug("Model count: before="+modelsBeforeCnt+", load="+loadedModels.size()+", after="+modelsAfterCnt+" in "+(System.currentTimeMillis()-startTime)+" msecs ["+Thread.currentThread()+"] "+(tenantDomain.equals(TenantService.DEFAULT_DOMAIN) ? "" : " (Tenant: "+tenantDomain+")"));
}
}
}

View File

@@ -64,6 +64,8 @@ public interface NamespaceDAO extends NamespacePrefixResolver
*/
public void init();
public void afterDictionaryInit();
/**
* Destroy Namespaces
*/

View File

@@ -38,7 +38,7 @@ import org.apache.commons.logging.LogFactory;
/**
* Simple in-memory namespace DAO
*/
public class NamespaceDAOImpl implements NamespaceDAO
public class NamespaceDAOImpl implements NamespaceDAO, DictionaryListener
{
private static final Log logger = LogFactory.getLog(NamespaceDAOImpl.class);
@@ -74,6 +74,62 @@ public class NamespaceDAOImpl implements NamespaceDAO
public void registerDictionary(DictionaryDAO dictionaryDAO)
{
this.dictionaryDAO = dictionaryDAO;
this.dictionaryDAO.register(this);
}
public void afterDictionaryDestroy()
{
// TODO Auto-generated method stub
}
public void onDictionaryInit()
{
// TODO Auto-generated method stub
}
/**
* Complete the initialisation
*/
public void afterDictionaryInit()
{
String tenantDomain = getTenantDomain();
try
{
NamespaceRegistry namespaceRegistry = getNamespaceRegistryLocal(tenantDomain);
if (namespaceRegistry == null)
{
// unexpected
throw new AlfrescoRuntimeException("Failed to init namespaceRegistry " + tenantDomain);
}
try
{
writeLock.lock();
namespaceRegistryCache.put(tenantDomain, namespaceRegistry);
}
finally
{
writeLock.unlock();
}
}
finally
{
try
{
readLock.lock();
if (namespaceRegistryCache.get(tenantDomain) != null)
{
removeNamespaceLocal(tenantDomain);
}
}
finally
{
readLock.unlock();
}
}
}
@@ -82,7 +138,14 @@ public class NamespaceDAOImpl implements NamespaceDAO
*/
public void init()
{
initNamespace(getTenantDomain());
String tenantDomain = getTenantDomain();
NamespaceRegistry namespaceRegistry = initNamespaceRegistry(tenantDomain);
if (namespaceRegistry == null)
{
// unexpected
throw new AlfrescoRuntimeException("Failed to init namespaceRegistry " + tenantDomain);
}
}
/**
@@ -128,60 +191,20 @@ public class NamespaceDAOImpl implements NamespaceDAO
return namespaceRegistry;
}
private NamespaceRegistry initNamespace(String tenantDomain)
{
try
{
createNamespaceLocal(tenantDomain);
NamespaceRegistry namespaceRegistry = initNamespaceRegistry(tenantDomain);
if (namespaceRegistry == null)
{
// unexpected
throw new AlfrescoRuntimeException("Failed to init namespaceRegistry " + tenantDomain);
}
try
{
writeLock.lock();
namespaceRegistryCache.put(tenantDomain, namespaceRegistry);
}
finally
{
writeLock.unlock();
}
return namespaceRegistry;
}
finally
{
try
{
readLock.lock();
if (namespaceRegistryCache.get(tenantDomain) != null)
{
removeNamespaceLocal(tenantDomain);
}
}
finally
{
readLock.unlock();
}
}
}
private NamespaceRegistry initNamespaceRegistry(String tenantDomain)
{
getNamespaceRegistry(tenantDomain).setUrisCache(new ArrayList<String>());
getNamespaceRegistry(tenantDomain).setPrefixesCache(new HashMap<String, String>());
// create threadlocal, if needed
NamespaceRegistry namespaceRegistry = createNamespaceLocal(tenantDomain);
namespaceRegistry.setUrisCache(new ArrayList<String>());
namespaceRegistry.setPrefixesCache(new HashMap<String, String>());
if (logger.isTraceEnabled())
{
logger.trace("Empty namespaces initialised");
logger.trace("Empty namespaces initialised: "+namespaceRegistry+" - "+namespaceRegistryThreadLocal+" ["+Thread.currentThread()+"]");
}
return getNamespaceRegistryLocal(tenantDomain);
return namespaceRegistry;
}
@@ -427,7 +450,7 @@ public class NamespaceDAOImpl implements NamespaceDAO
}
// create threadlocal
private void createNamespaceLocal(String tenantDomain)
private NamespaceRegistry createNamespaceLocal(String tenantDomain)
{
// create threadlocal, if needed
NamespaceRegistry namespaceRegistry = getNamespaceRegistryLocal(tenantDomain);
@@ -435,17 +458,17 @@ public class NamespaceDAOImpl implements NamespaceDAO
{
namespaceRegistry = new NamespaceRegistry(tenantDomain);
if (! tenantDomain.equals(TenantService.DEFAULT_DOMAIN))
if (tenantDomain.equals(TenantService.DEFAULT_DOMAIN))
{
defaultNamespaceRegistryThreadLocal.set(namespaceRegistry);
}
else
{
namespaceRegistryThreadLocal.set(namespaceRegistry);
}
}
if (defaultNamespaceRegistryThreadLocal.get() == null)
{
namespaceRegistry = new NamespaceRegistry(TenantService.DEFAULT_DOMAIN);
defaultNamespaceRegistryThreadLocal.set(namespaceRegistry);
}
}
return namespaceRegistry;
}
// get threadlocal

View File

@@ -19,6 +19,7 @@
package org.alfresco.repo.node;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -52,6 +53,7 @@ import org.alfresco.repo.policy.AssociationPolicyDelegate;
import org.alfresco.repo.policy.ClassPolicyDelegate;
import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.repo.search.Indexer;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.service.cmr.dictionary.ClassDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
@@ -95,6 +97,8 @@ public abstract class AbstractNodeServiceImpl implements NodeService
private PolicyComponent policyComponent;
protected DictionaryService dictionaryService;
protected TransactionService transactionService;
protected TenantService tenantService;
protected List<String> storesToIgnorePolicies = new ArrayList<String>(0);
/*
* Policy delegates
@@ -145,6 +149,16 @@ public abstract class AbstractNodeServiceImpl implements NodeService
this.transactionService = transactionService;
}
public void setTenantService(TenantService tenantService)
{
this.tenantService = tenantService;
}
public void setStoresToIgnorePolicies(List<String> storesToIgnorePolicies)
{
this.storesToIgnorePolicies = storesToIgnorePolicies;
}
/**
* Checks equality by type and uuid
*/
@@ -204,12 +218,27 @@ public abstract class AbstractNodeServiceImpl implements NodeService
onDeleteAssociationDelegate = policyComponent.registerAssociationPolicy(NodeServicePolicies.OnDeleteAssociationPolicy.class);
}
private boolean ignorePolicy(StoreRef storeRef)
{
return (storesToIgnorePolicies.contains(tenantService.getBaseName(storeRef).toString()));
}
private boolean ignorePolicy(NodeRef nodeRef)
{
return (storesToIgnorePolicies.contains(tenantService.getBaseName(nodeRef.getStoreRef()).toString()));
}
/**
* @see NodeServicePolicies.BeforeCreateStorePolicy#beforeCreateStore(QName,
* StoreRef)
*/
protected void invokeBeforeCreateStore(QName nodeTypeQName, StoreRef storeRef)
{
if (ignorePolicy(storeRef))
{
return;
}
NodeServicePolicies.BeforeCreateStorePolicy policy = this.beforeCreateStoreDelegate.get(nodeTypeQName);
policy.beforeCreateStore(nodeTypeQName, storeRef);
}
@@ -219,6 +248,11 @@ public abstract class AbstractNodeServiceImpl implements NodeService
*/
protected void invokeOnCreateStore(NodeRef rootNodeRef)
{
if (ignorePolicy(rootNodeRef))
{
return;
}
// get qnames to invoke against
Set<QName> qnames = getTypeAndAspectQNames(rootNodeRef);
// execute policy for node type and aspects
@@ -232,6 +266,11 @@ public abstract class AbstractNodeServiceImpl implements NodeService
*/
protected void invokeBeforeCreateNode(NodeRef parentNodeRef, QName assocTypeQName, QName assocQName, QName childNodeTypeQName)
{
if (ignorePolicy(parentNodeRef))
{
return;
}
// execute policy for node type
NodeServicePolicies.BeforeCreateNodePolicy policy = beforeCreateNodeDelegate.get(childNodeTypeQName);
policy.beforeCreateNode(parentNodeRef, assocTypeQName, assocQName, childNodeTypeQName);
@@ -243,6 +282,12 @@ public abstract class AbstractNodeServiceImpl implements NodeService
protected void invokeOnCreateNode(ChildAssociationRef childAssocRef)
{
NodeRef childNodeRef = childAssocRef.getChildRef();
if (ignorePolicy(childNodeRef))
{
return;
}
// get qnames to invoke against
Set<QName> qnames = getTypeAndAspectQNames(childNodeRef);
// execute policy for node type and aspects
@@ -256,6 +301,12 @@ public abstract class AbstractNodeServiceImpl implements NodeService
protected void invokeOnMoveNode(ChildAssociationRef oldChildAssocRef, ChildAssociationRef newChildAssocRef)
{
NodeRef childNodeRef = newChildAssocRef.getChildRef();
if (ignorePolicy(childNodeRef))
{
return;
}
// get qnames to invoke against
Set<QName> qnames = getTypeAndAspectQNames(childNodeRef);
// execute policy for node type and aspects
@@ -268,6 +319,11 @@ public abstract class AbstractNodeServiceImpl implements NodeService
*/
protected void invokeBeforeUpdateNode(NodeRef nodeRef)
{
if (ignorePolicy(nodeRef))
{
return;
}
// get qnames to invoke against
Set<QName> qnames = getTypeAndAspectQNames(nodeRef);
// execute policy for node type and aspects
@@ -280,6 +336,11 @@ public abstract class AbstractNodeServiceImpl implements NodeService
*/
protected void invokeOnUpdateNode(NodeRef nodeRef)
{
if (ignorePolicy(nodeRef))
{
return;
}
// get qnames to invoke against
Set<QName> qnames = getTypeAndAspectQNames(nodeRef);
// execute policy for node type and aspects
@@ -295,6 +356,10 @@ public abstract class AbstractNodeServiceImpl implements NodeService
Map<QName, Serializable> before,
Map<QName, Serializable> after)
{
if (ignorePolicy(nodeRef))
{
return;
}
// Some logging so we can see which properties have been modified
if (logger.isDebugEnabled() == true)
@@ -344,6 +409,11 @@ public abstract class AbstractNodeServiceImpl implements NodeService
*/
protected void invokeBeforeDeleteNode(NodeRef nodeRef)
{
if (ignorePolicy(nodeRef))
{
return;
}
// get qnames to invoke against
Set<QName> qnames = getTypeAndAspectQNames(nodeRef);
// execute policy for node type and aspects
@@ -355,16 +425,32 @@ public abstract class AbstractNodeServiceImpl implements NodeService
* @see NodeServicePolicies.OnDeleteNodePolicy#onDeleteNode(ChildAssociationRef)
*/
protected void invokeOnDeleteNode(ChildAssociationRef childAssocRef, QName childNodeTypeQName, Set<QName> childAspectQnames, boolean isArchivedNode)
{
NodeRef childNodeRef = childAssocRef.getChildRef();
Set<QName> qnames = null;
if (ignorePolicy(childNodeRef))
{
// special case
qnames = new HashSet<QName>(1);
qnames.add(ContentModel.ASPECT_VERSIONABLE);
}
else
{
// get qnames to invoke against
Set<QName> qnames = new HashSet<QName>(childAspectQnames.size() + 1);
qnames = new HashSet<QName>(childAspectQnames.size() + 1);
qnames.addAll(childAspectQnames);
qnames.add(childNodeTypeQName);
}
if (qnames != null)
{
// execute policy for node type and aspects
NodeServicePolicies.OnDeleteNodePolicy policy = onDeleteNodeDelegate.get(childAssocRef.getChildRef(), qnames);
policy.onDeleteNode(childAssocRef, isArchivedNode);
}
}
/**
* @see NodeServicePolicies.BeforeAddAspectPolicy#beforeAddAspect(NodeRef,
@@ -372,6 +458,11 @@ public abstract class AbstractNodeServiceImpl implements NodeService
*/
protected void invokeBeforeAddAspect(NodeRef nodeRef, QName aspectTypeQName)
{
if (ignorePolicy(nodeRef))
{
return;
}
NodeServicePolicies.BeforeAddAspectPolicy policy = beforeAddAspectDelegate.get(nodeRef, aspectTypeQName);
policy.beforeAddAspect(nodeRef, aspectTypeQName);
}
@@ -381,6 +472,11 @@ public abstract class AbstractNodeServiceImpl implements NodeService
*/
protected void invokeOnAddAspect(NodeRef nodeRef, QName aspectTypeQName)
{
if (ignorePolicy(nodeRef))
{
return;
}
NodeServicePolicies.OnAddAspectPolicy policy = onAddAspectDelegate.get(nodeRef, aspectTypeQName);
policy.onAddAspect(nodeRef, aspectTypeQName);
}
@@ -391,6 +487,11 @@ public abstract class AbstractNodeServiceImpl implements NodeService
*/
protected void invokeBeforeRemoveAspect(NodeRef nodeRef, QName aspectTypeQName)
{
if (ignorePolicy(nodeRef))
{
return;
}
NodeServicePolicies.BeforeRemoveAspectPolicy policy = beforeRemoveAspectDelegate.get(nodeRef, aspectTypeQName);
policy.beforeRemoveAspect(nodeRef, aspectTypeQName);
}
@@ -401,6 +502,11 @@ public abstract class AbstractNodeServiceImpl implements NodeService
*/
protected void invokeOnRemoveAspect(NodeRef nodeRef, QName aspectTypeQName)
{
if (ignorePolicy(nodeRef))
{
return;
}
NodeServicePolicies.OnRemoveAspectPolicy policy = onRemoveAspectDelegate.get(nodeRef, aspectTypeQName);
policy.onRemoveAspect(nodeRef, aspectTypeQName);
}
@@ -411,6 +517,11 @@ public abstract class AbstractNodeServiceImpl implements NodeService
*/
protected void invokeBeforeCreateNodeAssociation(NodeRef parentNodeRef, QName assocTypeQName, QName assocQName)
{
if (ignorePolicy(parentNodeRef))
{
return;
}
// get qnames to invoke against
Set<QName> qnames = getTypeAndAspectQNames(parentNodeRef);
// execute policy for node type
@@ -425,6 +536,12 @@ public abstract class AbstractNodeServiceImpl implements NodeService
{
// Get the parent reference and the assoc type qName
NodeRef parentNodeRef = childAssocRef.getParentRef();
if (ignorePolicy(parentNodeRef))
{
return;
}
QName assocTypeQName = childAssocRef.getTypeQName();
// get qnames to invoke against
Set<QName> qnames = getTypeAndAspectQNames(parentNodeRef);
@@ -439,6 +556,11 @@ public abstract class AbstractNodeServiceImpl implements NodeService
*/
protected void invokeBeforeCreateChildAssociation(NodeRef parentNodeRef, NodeRef childNodeRef, QName assocTypeQName, QName assocQName, boolean isNewNode)
{
if (ignorePolicy(parentNodeRef))
{
return;
}
// get qnames to invoke against
Set<QName> qnames = getTypeAndAspectQNames(parentNodeRef);
// execute policy for node type
@@ -453,6 +575,12 @@ public abstract class AbstractNodeServiceImpl implements NodeService
{
// Get the parent reference and the assoc type qName
NodeRef parentNodeRef = childAssocRef.getParentRef();
if (ignorePolicy(parentNodeRef))
{
return;
}
QName assocTypeQName = childAssocRef.getTypeQName();
// get qnames to invoke against
Set<QName> qnames = getTypeAndAspectQNames(parentNodeRef);
@@ -467,6 +595,12 @@ public abstract class AbstractNodeServiceImpl implements NodeService
protected void invokeBeforeDeleteChildAssociation(ChildAssociationRef childAssocRef)
{
NodeRef parentNodeRef = childAssocRef.getParentRef();
if (ignorePolicy(parentNodeRef))
{
return;
}
QName assocTypeQName = childAssocRef.getTypeQName();
// get qnames to invoke against
Set<QName> qnames = getTypeAndAspectQNames(parentNodeRef);
@@ -481,6 +615,12 @@ public abstract class AbstractNodeServiceImpl implements NodeService
protected void invokeOnDeleteChildAssociation(ChildAssociationRef childAssocRef)
{
NodeRef parentNodeRef = childAssocRef.getParentRef();
if (ignorePolicy(parentNodeRef))
{
return;
}
QName assocTypeQName = childAssocRef.getTypeQName();
// get qnames to invoke against
Set<QName> qnames = getTypeAndAspectQNames(parentNodeRef);
@@ -495,6 +635,12 @@ public abstract class AbstractNodeServiceImpl implements NodeService
protected void invokeOnCreateAssociation(AssociationRef nodeAssocRef)
{
NodeRef sourceNodeRef = nodeAssocRef.getSourceRef();
if (ignorePolicy(sourceNodeRef))
{
return;
}
QName assocTypeQName = nodeAssocRef.getTypeQName();
// get qnames to invoke against
Set<QName> qnames = getTypeAndAspectQNames(sourceNodeRef);
@@ -509,6 +655,12 @@ public abstract class AbstractNodeServiceImpl implements NodeService
protected void invokeOnDeleteAssociation(AssociationRef nodeAssocRef)
{
NodeRef sourceNodeRef = nodeAssocRef.getSourceRef();
if (ignorePolicy(sourceNodeRef))
{
return;
}
QName assocTypeQName = nodeAssocRef.getTypeQName();
// get qnames to invoke against
Set<QName> qnames = getTypeAndAspectQNames(sourceNodeRef);

View File

@@ -149,6 +149,8 @@ public class MultiTDemoTest extends TestCase
repoAdminService = (RepoAdminService) ctx.getBean("RepoAdminService");
dictionaryService = (DictionaryService) ctx.getBean("DictionaryService");
usageService = (UsageService) ctx.getBean("usageService");
createTenants();
}
@Override
@@ -157,6 +159,51 @@ public class MultiTDemoTest extends TestCase
super.tearDown();
}
private void createTenants()
{
for (final String tenantDomain : tenants)
{
createTenant(tenantDomain);
}
}
public void testCreateTenants() throws Throwable
{
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); // authenticate as super-admin
logger.info("Create tenants");
Set<NodeRef> personRefs = personService.getAllPeople();
//assertEquals(2, personRefs.size()); // super-tenant: admin, guest (note: checking for 2 assumes that this test is run in a fresh bootstrap env)
for (NodeRef personRef : personRefs)
{
String userName = (String)nodeService.getProperty(personRef, ContentModel.PROP_USERNAME);
for (final String tenantDomain : tenants)
{
assertFalse("Unexpected (tenant) user: "+userName, userName.endsWith(tenantDomain));
}
}
}
private void createTenant(final String tenantDomain)
{
// create tenants (if not already created)
AuthenticationUtil.runAs(new RunAsWork<Object>()
{
public Object doWork() throws Exception
{
if (! tenantAdminService.existsTenant(tenantDomain))
{
//tenantAdminService.createTenant(tenantDomain, DEFAULT_ADMIN_PW.toCharArray(), ROOT_DIR + "/" + tenantDomain);
tenantAdminService.createTenant(tenantDomain, (DEFAULT_ADMIN_PW+" "+tenantDomain).toCharArray(), null); // use default root dir
logger.info("Created tenant " + tenantDomain);
}
return null;
}
}, AuthenticationUtil.getSystemUserName());
}
public void test_ETHREEOH_2015()
{
@@ -192,58 +239,6 @@ public class MultiTDemoTest extends TestCase
usageService.deleteDeltas(personNodeRef);
}
public void testCreateTenants() throws Throwable
{
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); // authenticate as super-admin
logger.info("Create tenants");
Set<NodeRef> personRefs = personService.getAllPeople();
//assertEquals(2, personRefs.size()); // super-tenant: admin, guest (note: checking for 2 assumes that this test is run in a fresh bootstrap env)
for (NodeRef personRef : personRefs)
{
String userName = (String)nodeService.getProperty(personRef, ContentModel.PROP_USERNAME);
for (final String tenantDomain : tenants)
{
assertFalse("Unexpected (tenant) user: "+userName, userName.endsWith(tenantDomain));
}
}
try
{
for (final String tenantDomain : tenants)
{
createTenant(tenantDomain);
}
}
catch (Throwable t)
{
StringWriter stackTrace = new StringWriter();
t.printStackTrace(new PrintWriter(stackTrace));
System.err.println(stackTrace.toString());
throw t;
}
}
private void createTenant(final String tenantDomain)
{
// create tenants (if not already created)
AuthenticationUtil.runAs(new RunAsWork<Object>()
{
public Object doWork() throws Exception
{
if (! tenantAdminService.existsTenant(tenantDomain))
{
//tenantAdminService.createTenant(tenantDomain, DEFAULT_ADMIN_PW.toCharArray(), ROOT_DIR + "/" + tenantDomain);
tenantAdminService.createTenant(tenantDomain, (DEFAULT_ADMIN_PW+" "+tenantDomain).toCharArray(), null); // use default root dir
logger.info("Created tenant " + tenantDomain);
}
return null;
}
}, AuthenticationUtil.getSystemUserName());
}
public void testCreateUsers() throws Throwable
{