diff --git a/config/alfresco/node-services-context.xml b/config/alfresco/node-services-context.xml
index 90ce0c9dcc..714bbdeecb 100644
--- a/config/alfresco/node-services-context.xml
+++ b/config/alfresco/node-services-context.xml
@@ -84,7 +84,7 @@
@@ -228,11 +228,19 @@
-
+
${system.cascadeDeleteInTransaction}
+
+
+
+
+
+ ${spaces.archive.store}
+
+
diff --git a/source/java/org/alfresco/repo/admin/RepoAdminServiceImplTest.java b/source/java/org/alfresco/repo/admin/RepoAdminServiceImplTest.java
index 1758285dcf..f35f3ddea2 100644
--- a/source/java/org/alfresco/repo/admin/RepoAdminServiceImplTest.java
+++ b/source/java/org/alfresco/repo/admin/RepoAdminServiceImplTest.java
@@ -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 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 contentProps = new HashMap();
+ 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 titledProps = new HashMap();
+ 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
diff --git a/source/java/org/alfresco/repo/dictionary/DictionaryBootstrap.java b/source/java/org/alfresco/repo/dictionary/DictionaryBootstrap.java
index f9578a4208..c42fb4e6e2 100644
--- a/source/java/org/alfresco/repo/dictionary/DictionaryBootstrap.java
+++ b/source/java/org/alfresco/repo/dictionary/DictionaryBootstrap.java
@@ -123,6 +123,11 @@ public class DictionaryBootstrap implements DictionaryListener
{
long startTime = System.currentTimeMillis();
+ if (logger.isTraceEnabled())
+ {
+ logger.trace("onDictionaryInit: ["+Thread.currentThread()+"]");
+ }
+
Collection 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()+"]");
}
}
}
diff --git a/source/java/org/alfresco/repo/dictionary/DictionaryDAOImpl.java b/source/java/org/alfresco/repo/dictionary/DictionaryDAOImpl.java
index 7c8f297fd4..cf6eda34ae 100644
--- a/source/java/org/alfresco/repo/dictionary/DictionaryDAOImpl.java
+++ b/source/java/org/alfresco/repo/dictionary/DictionaryDAOImpl.java
@@ -162,8 +162,8 @@ public class DictionaryDAOImpl implements DictionaryDAO
}
destroy();
- init();
-
+ init();
+
if (logger.isDebugEnabled())
{
logger.debug("... resetting dictionary completed");
@@ -175,17 +175,19 @@ 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()
{
public DictionaryRegistry doWork()
- {
+ {
try
{
- // create threadlocal, if needed
- createDataDictionaryLocal(tenantDomain);
-
DictionaryRegistry dictionaryRegistry = initDictionaryRegistry(tenantDomain);
if (dictionaryRegistry == null)
@@ -221,24 +223,32 @@ public class DictionaryDAOImpl implements DictionaryDAO
readLock.unlock();
}
}
- }
+ }
}, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain));
}
finally
{
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());
- getDictionaryRegistry(tenantDomain).setUriToModels(new HashMap>());
+ // create threadlocal, if needed
+ DictionaryRegistry dictionaryRegistry = createDataDictionaryLocal(tenantDomain);
- // initialise empty dictionary & namespaces
+ dictionaryRegistry.setCompiledModels(new HashMap());
+ dictionaryRegistry.setUriToModels(new HashMap>());
+
+ 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
diff --git a/source/java/org/alfresco/repo/dictionary/DictionaryModelType.java b/source/java/org/alfresco/repo/dictionary/DictionaryModelType.java
index c425e8cae4..45d6df003c 100644
--- a/source/java/org/alfresco/repo/dictionary/DictionaryModelType.java
+++ b/source/java/org/alfresco/repo/dictionary/DictionaryModelType.java
@@ -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;
@@ -235,37 +232,37 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
// Register interest in the onContentUpdate policy for the dictionary model type
policyComponent.bindClassBehaviour(
ContentServicePolicies.ON_CONTENT_UPDATE,
- ContentModel.TYPE_DICTIONARY_MODEL,
+ ContentModel.TYPE_DICTIONARY_MODEL,
new JavaBehaviour(this, "onContentUpdate"));
// Register interest in the onUpdateProperties policy for the dictionary model type
policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"),
- ContentModel.TYPE_DICTIONARY_MODEL,
+ ContentModel.TYPE_DICTIONARY_MODEL,
new JavaBehaviour(this, "onUpdateProperties"));
// Register interest in the beforeDeleteNode policy for the dictionary model type
policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"),
- ContentModel.TYPE_DICTIONARY_MODEL,
+ ContentModel.TYPE_DICTIONARY_MODEL,
new JavaBehaviour(this, "beforeDeleteNode"));
// Register interest in the onDeleteNode policy for the dictionary model type
policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onDeleteNode"),
- ContentModel.TYPE_DICTIONARY_MODEL,
+ ContentModel.TYPE_DICTIONARY_MODEL,
new JavaBehaviour(this, "onDeleteNode"));
// Register interest in the onRemoveAspect policy
policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onRemoveAspect"),
- this,
+ this,
new JavaBehaviour(this, "onRemoveAspect"));
// Register interest in the onCreateNode policy
policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateNode"),
- this,
+ this,
new JavaBehaviour(this, "onCreateNode"));
// Create the transaction listener
@@ -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)
{
diff --git a/source/java/org/alfresco/repo/dictionary/DictionaryRepositoryBootstrap.java b/source/java/org/alfresco/repo/dictionary/DictionaryRepositoryBootstrap.java
index ce172e3ae2..73ba849f82 100644
--- a/source/java/org/alfresco/repo/dictionary/DictionaryRepositoryBootstrap.java
+++ b/source/java/org/alfresco/repo/dictionary/DictionaryRepositoryBootstrap.java
@@ -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 modelsBefore = dictionaryDAO.getModels();
int modelsBeforeCnt = (modelsBefore != null ? modelsBefore.size() : 0);
@@ -220,6 +224,11 @@ public class DictionaryRepositoryBootstrap extends AbstractLifecycleBean impleme
{
Map> modelMap = new HashMap>();
+ 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+")"));
}
}
}
diff --git a/source/java/org/alfresco/repo/dictionary/NamespaceDAO.java b/source/java/org/alfresco/repo/dictionary/NamespaceDAO.java
index a4ffa75c66..3e6b07c902 100644
--- a/source/java/org/alfresco/repo/dictionary/NamespaceDAO.java
+++ b/source/java/org/alfresco/repo/dictionary/NamespaceDAO.java
@@ -64,6 +64,8 @@ public interface NamespaceDAO extends NamespacePrefixResolver
*/
public void init();
+ public void afterDictionaryInit();
+
/**
* Destroy Namespaces
*/
diff --git a/source/java/org/alfresco/repo/dictionary/NamespaceDAOImpl.java b/source/java/org/alfresco/repo/dictionary/NamespaceDAOImpl.java
index c9a4356ba9..27dbfade67 100644
--- a/source/java/org/alfresco/repo/dictionary/NamespaceDAOImpl.java
+++ b/source/java/org/alfresco/repo/dictionary/NamespaceDAOImpl.java
@@ -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,15 +74,78 @@ 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();
+ }
+ }
+ }
+
+
/**
* Initialise empty namespaces
*/
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());
- getNamespaceRegistry(tenantDomain).setPrefixesCache(new HashMap());
+ // create threadlocal, if needed
+ NamespaceRegistry namespaceRegistry = createNamespaceLocal(tenantDomain);
+
+ namespaceRegistry.setUrisCache(new ArrayList());
+ namespaceRegistry.setPrefixesCache(new HashMap());
if (logger.isTraceEnabled())
{
- logger.trace("Empty namespaces initialised");
+ logger.trace("Empty namespaces initialised: "+namespaceRegistry+" - "+namespaceRegistryThreadLocal+" ["+Thread.currentThread()+"]");
}
- return getNamespaceRegistryLocal(tenantDomain);
+ return namespaceRegistry;
}
@@ -242,7 +265,7 @@ public class NamespaceDAOImpl implements NamespaceDAO
// overridden, hence skip this default prefix
continue;
}
- prefixesFiltered.add(prefix);
+ prefixesFiltered.add(prefix);
}
// default (non-overridden) + tenant-specific
@@ -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
@@ -466,7 +489,7 @@ public class NamespaceDAOImpl implements NamespaceDAO
if ((namespaceRegistry != null) && (tenantDomain.equals(namespaceRegistry.getTenantDomain())))
{
return namespaceRegistry; // return threadlocal, if set
- }
+ }
return null;
}
diff --git a/source/java/org/alfresco/repo/node/AbstractNodeServiceImpl.java b/source/java/org/alfresco/repo/node/AbstractNodeServiceImpl.java
index 4fad0bc583..0eb0d66420 100644
--- a/source/java/org/alfresco/repo/node/AbstractNodeServiceImpl.java
+++ b/source/java/org/alfresco/repo/node/AbstractNodeServiceImpl.java
@@ -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 storesToIgnorePolicies = new ArrayList(0);
/*
* Policy delegates
@@ -144,6 +148,16 @@ public abstract class AbstractNodeServiceImpl implements NodeService
{
this.transactionService = transactionService;
}
+
+ public void setTenantService(TenantService tenantService)
+ {
+ this.tenantService = tenantService;
+ }
+
+ public void setStoresToIgnorePolicies(List storesToIgnorePolicies)
+ {
+ this.storesToIgnorePolicies = storesToIgnorePolicies;
+ }
/**
* Checks equality by type and uuid
@@ -203,13 +217,28 @@ public abstract class AbstractNodeServiceImpl implements NodeService
onCreateAssociationDelegate = policyComponent.registerAssociationPolicy(NodeServicePolicies.OnCreateAssociationPolicy.class);
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 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 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 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 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 qnames = getTypeAndAspectQNames(nodeRef);
// execute policy for node type and aspects
@@ -295,6 +356,10 @@ public abstract class AbstractNodeServiceImpl implements NodeService
Map before,
Map 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 qnames = getTypeAndAspectQNames(nodeRef);
// execute policy for node type and aspects
@@ -356,14 +426,30 @@ public abstract class AbstractNodeServiceImpl implements NodeService
*/
protected void invokeOnDeleteNode(ChildAssociationRef childAssocRef, QName childNodeTypeQName, Set childAspectQnames, boolean isArchivedNode)
{
- // get qnames to invoke against
- Set qnames = new HashSet(childAspectQnames.size() + 1);
- qnames.addAll(childAspectQnames);
- qnames.add(childNodeTypeQName);
+ NodeRef childNodeRef = childAssocRef.getChildRef();
- // execute policy for node type and aspects
- NodeServicePolicies.OnDeleteNodePolicy policy = onDeleteNodeDelegate.get(childAssocRef.getChildRef(), qnames);
- policy.onDeleteNode(childAssocRef, isArchivedNode);
+ Set qnames = null;
+
+ if (ignorePolicy(childNodeRef))
+ {
+ // special case
+ qnames = new HashSet(1);
+ qnames.add(ContentModel.ASPECT_VERSIONABLE);
+ }
+ else
+ {
+ // get qnames to invoke against
+ qnames = new HashSet(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);
+ }
}
/**
@@ -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 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 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 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 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 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 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 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 qnames = getTypeAndAspectQNames(sourceNodeRef);
diff --git a/source/java/org/alfresco/repo/tenant/MultiTDemoTest.java b/source/java/org/alfresco/repo/tenant/MultiTDemoTest.java
index 0e58f22b79..5bd1938930 100644
--- a/source/java/org/alfresco/repo/tenant/MultiTDemoTest.java
+++ b/source/java/org/alfresco/repo/tenant/MultiTDemoTest.java
@@ -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 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