diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml index ab11034e75..28c52c5791 100644 --- a/config/alfresco/bootstrap-context.xml +++ b/config/alfresco/bootstrap-context.xml @@ -147,11 +147,6 @@ - - - - - @@ -277,7 +272,12 @@ - + + + + + + diff --git a/config/alfresco/cache-context.xml b/config/alfresco/cache-context.xml index 6218260b5d..4d72df3e65 100644 --- a/config/alfresco/cache-context.xml +++ b/config/alfresco/cache-context.xml @@ -636,39 +636,7 @@ - - - - - - - - - - org.alfresco.cache.uriToModelsCache - - - - - - - - - - - - - - - - org.alfresco.uriToModelsTransactionalCache - - - 100 - - - - + @@ -683,7 +651,7 @@ - + @@ -700,40 +668,8 @@ - - - - - - - - - - org.alfresco.cache.urisCache - - - - - - - - - - - - - - - - org.alfresco.urisTransactionalCache - - - 100 - - - - - + + @@ -747,7 +683,7 @@ - + diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index 3c0449d925..d16fe3cf04 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -863,7 +863,7 @@ - + @@ -889,11 +889,8 @@ - - - - + diff --git a/config/alfresco/ehcache-default.xml b/config/alfresco/ehcache-default.xml index a5b4647338..2f3dfa53af 100644 --- a/config/alfresco/ehcache-default.xml +++ b/config/alfresco/ehcache-default.xml @@ -380,13 +380,6 @@ - - - - - - - - - + - + - - + + dictionaryRegistryCache; - // Map of Namespace URI usages to Models - private SimpleCache>> uriToModelsCache; - - // Map of model name to compiled model - private SimpleCache> compiledModelsCache; + // used to reset the cache + private ThreadLocal dictionaryRegistryThreadLocal = new ThreadLocal(); + private ThreadLocal defaultDictionaryRegistryThreadLocal = new ThreadLocal(); // Static list of registered dictionary listeners private List dictionaryListeners = new ArrayList(); @@ -104,14 +98,9 @@ public class DictionaryDAOImpl implements DictionaryDAO this.tenantService = tenantService; } - public void setUriToModelsCache(SimpleCache>> uriToModelsCache) + public void setDictionaryRegistryCache(SimpleCache dictionaryRegistryCache) { - this.uriToModelsCache = uriToModelsCache; - } - - public void setCompiledModelsCache(SimpleCache> compiledModelsCache) - { - this.compiledModelsCache = compiledModelsCache; + this.dictionaryRegistryCache = dictionaryRegistryCache; } /** @@ -133,6 +122,7 @@ public class DictionaryDAOImpl implements DictionaryDAO { if (! dictionaryListeners.contains(dictionaryDeployer)) { + destroy(); // force reload on next get dictionaryListeners.add(dictionaryDeployer); } } @@ -142,27 +132,7 @@ public class DictionaryDAOImpl implements DictionaryDAO */ public void init() { - String tenantDomain = tenantService.getCurrentUserDomain(); - - // initialise empty dictionary & namespaces - putCompiledModels(tenantDomain, new HashMap()); - putUriToModels(tenantDomain, new HashMap>()); - - namespaceDAO.init(); - - // populate the dictionary - for (DictionaryListener dictionaryListener : dictionaryListeners) - { - dictionaryListener.onDictionaryInit(); - } - - // notify registered listeners that dictionary has been initialised - for (DictionaryListener dictionaryListener : dictionaryListeners) - { - dictionaryListener.afterDictionaryInit(); - } - - logger.info("Dictionary initialised"); + initDictionary(tenantService.getCurrentUserDomain()); } /** @@ -172,8 +142,7 @@ public class DictionaryDAOImpl implements DictionaryDAO { String tenantDomain = tenantService.getCurrentUserDomain(); - removeCompiledModels(tenantDomain); - removeUriToModels(tenantDomain); + removeDictionaryRegistry(tenantDomain); namespaceDAO.destroy(); @@ -183,7 +152,10 @@ public class DictionaryDAOImpl implements DictionaryDAO dictionaryDeployer.afterDictionaryDestroy(); } - logger.info("Dictionary destroyed"); + if (logger.isDebugEnabled()) + { + logger.debug("Dictionary destroyed"); + } } /** @@ -191,31 +163,104 @@ public class DictionaryDAOImpl implements DictionaryDAO */ public void reset() { - reset(tenantService.getCurrentUserDomain()); + if (logger.isDebugEnabled()) + { + logger.debug("Resetting dictionary ..."); + } + + destroy(); + init(); + + if (logger.isDebugEnabled()) + { + logger.debug("... resetting dictionary completed"); + } } - private void reset(String tenantDomain) + // load dictionary (models and namespaces) + private DictionaryRegistry initDictionary(final String tenantDomain) { - if (logger.isDebugEnabled()) - { - logger.debug("Resetting dictionary ..."); - } + long startTime = System.currentTimeMillis(); - AuthenticationUtil.runAs(new RunAsWork() - { - public Object doWork() - { - destroy(); - init(); - - return null; - } - }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); - - if (logger.isDebugEnabled()) - { - logger.debug("... resetting dictionary completed"); - } + try + { + return AuthenticationUtil.runAs(new RunAsWork() + { + public DictionaryRegistry doWork() + { + try + { + // create threadlocal, if needed + createDataDictionaryLocal(tenantDomain); + + DictionaryRegistry dictionaryRegistry = initDictionaryRegistry(tenantDomain); + + if (dictionaryRegistry == null) + { + // unexpected + throw new AlfrescoRuntimeException("Failed to init dictionaryRegistry " + tenantDomain); + } + + try + { + writeLock.lock(); + dictionaryRegistryCache.put(tenantDomain, dictionaryRegistry); + } + finally + { + writeLock.unlock(); + } + + return dictionaryRegistry; + } + finally + { + try + { + readLock.lock(); + if (dictionaryRegistryCache.get(tenantDomain) != null) + { + removeDataDictionaryLocal(tenantDomain); + } + } + finally + { + 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+")")); + } + } + } + + private DictionaryRegistry initDictionaryRegistry(String tenantDomain) + { + getDictionaryRegistry(tenantDomain).setCompiledModels(new HashMap()); + getDictionaryRegistry(tenantDomain).setUriToModels(new HashMap>()); + + // initialise empty dictionary & namespaces + namespaceDAO.init(); + + // populate the dictionary based on registered sources + for (DictionaryListener dictionaryDeployer : dictionaryListeners) + { + dictionaryDeployer.onDictionaryInit(); + } + + // notify registered listeners that dictionary has been initialised (population is complete) + for (DictionaryListener dictionaryListener : dictionaryListeners) + { + dictionaryListener.afterDictionaryInit(); + } + + return getDictionaryRegistryLocal(tenantDomain); } /* (non-Javadoc) @@ -228,7 +273,7 @@ public class DictionaryDAOImpl implements DictionaryDAO // Compile model definition CompiledModel compiledModel = model.compile(this, namespaceDAO); QName modelName = compiledModel.getModelDefinition().getName(); - + // Remove namespace definitions for previous model, if it exists CompiledModel previousVersion = getCompiledModels(tenantDomain).get(modelName); if (previousVersion != null) @@ -260,19 +305,18 @@ public class DictionaryDAOImpl implements DictionaryDAO // Publish new Model Definition getCompiledModels(tenantDomain).put(modelName, compiledModel); - if (logger.isInfoEnabled()) + if (logger.isDebugEnabled()) { - logger.info("Registered model " + modelName.toPrefixString(namespaceDAO)); + logger.debug("Registered model " + modelName.toPrefixString(namespaceDAO)); for (M2Namespace namespace : model.getNamespaces()) { - logger.info("Registered namespace '" + namespace.getUri() + "' (prefix '" + namespace.getPrefix() + "')"); + logger.debug("Registered namespace '" + namespace.getUri() + "' (prefix '" + namespace.getPrefix() + "')"); } } - + return modelName; } - /** * @see org.alfresco.repo.dictionary.DictionaryDAO#removeModel(org.alfresco.service.namespace.QName) */ @@ -296,8 +340,7 @@ public class DictionaryDAOImpl implements DictionaryDAO getCompiledModels(tenantDomain).remove(modelName); } } - - + /** * Map Namespace URI to Model * @@ -318,7 +361,6 @@ public class DictionaryDAOImpl implements DictionaryDAO models.add(model); } } - /** * Unmap Namespace URI from Model @@ -378,14 +420,14 @@ public class DictionaryDAOImpl implements DictionaryDAO filteredModels.addAll(tenantModels); models = filteredModels; } - + if (models == null) { models = Collections.emptyList(); } return models; } - + List models = getUriToModels(TenantService.DEFAULT_DOMAIN).get(uri); if (models == null) { @@ -393,8 +435,7 @@ public class DictionaryDAOImpl implements DictionaryDAO } return models; } - - + /** * @param modelName the model name * @return the compiled model of the given name @@ -439,8 +480,7 @@ public class DictionaryDAOImpl implements DictionaryDAO } return null; } - - + /* (non-Javadoc) * @see org.alfresco.repo.dictionary.ModelQuery#getDataType(java.lang.Class) */ @@ -714,7 +754,7 @@ public class DictionaryDAOImpl implements DictionaryDAO public Collection getModels() { // get all models - including inherited models, if applicable - return getCompiledModels().keySet(); + return getCompiledModels().keySet(); } // MT-specific @@ -892,6 +932,120 @@ public class DictionaryDAOImpl implements DictionaryDAO return namespaces; } + // re-entrant (eg. via reset) + private DictionaryRegistry getDictionaryRegistry(String tenantDomain) + { + DictionaryRegistry dictionaryRegistry = null; + + try + { + // check cache first - return if set + readLock.lock(); + dictionaryRegistry = dictionaryRegistryCache.get(tenantDomain); + + if (dictionaryRegistry != null) + { + return dictionaryRegistry; // return cached config + } + } + finally + { + readLock.unlock(); + } + + // check threadlocal second - return if set + dictionaryRegistry = getDictionaryRegistryLocal(tenantDomain); + if (dictionaryRegistry != null) + { + return dictionaryRegistry; // return local dictionaryRegistry + } + + // reset caches - may have been invalidated (e.g. in a cluster) + dictionaryRegistry = initDictionary(tenantDomain); + + if (dictionaryRegistry == null) + { + // unexpected + throw new AlfrescoRuntimeException("Failed to get dictionaryRegistry " + tenantDomain); + } + + return dictionaryRegistry; + } + + // create threadlocal + private void createDataDictionaryLocal(String tenantDomain) + { + // create threadlocal, if needed + DictionaryRegistry dictionaryRegistry = getDictionaryRegistryLocal(tenantDomain); + if (dictionaryRegistry == null) + { + dictionaryRegistry = new DictionaryRegistry(tenantDomain); + + if (tenantDomain.equals(TenantService.DEFAULT_DOMAIN)) + { + defaultDictionaryRegistryThreadLocal.set(dictionaryRegistry); + } + else + { + dictionaryRegistryThreadLocal.set(dictionaryRegistry); + } + } + } + + // get threadlocal + private DictionaryRegistry getDictionaryRegistryLocal(String tenantDomain) + { + DictionaryRegistry dictionaryRegistry = null; + + if (tenantDomain.equals(TenantService.DEFAULT_DOMAIN)) + { + dictionaryRegistry = this.defaultDictionaryRegistryThreadLocal.get(); + } + else + { + dictionaryRegistry = this.dictionaryRegistryThreadLocal.get(); + } + + // check to see if domain switched + if ((dictionaryRegistry != null) && (tenantDomain.equals(dictionaryRegistry.getTenantDomain()))) + { + return dictionaryRegistry; // return threadlocal, if set + } + + return null; + } + + // remove threadlocal + private void removeDataDictionaryLocal(String tenantDomain) + { + if (tenantDomain.equals(TenantService.DEFAULT_DOMAIN)) + { + defaultDictionaryRegistryThreadLocal.set(null); // it's in the cache, clear the threadlocal + } + else + { + dictionaryRegistryThreadLocal.set(null); // it's in the cache, clear the threadlocal + } + } + + private void removeDictionaryRegistry(String tenantDomain) + { + try + { + writeLock.lock(); + if (dictionaryRegistryCache.get(tenantDomain) != null) + { + dictionaryRegistryCache.remove(tenantDomain); + } + + removeDataDictionaryLocal(tenantDomain); + } + finally + { + writeLock.unlock(); + } + } + /** * Get compiledModels from the cache (in the context of the given tenant domain) * @@ -899,80 +1053,7 @@ public class DictionaryDAOImpl implements DictionaryDAO */ private Map getCompiledModels(String tenantDomain) { - Map compiledModels = null; - try - { - readLock.lock(); - compiledModels = compiledModelsCache.get(tenantDomain); - } - finally - { - readLock.unlock(); - } - - - if (compiledModels == null) - { - reset(tenantDomain); // reset caches - may have been invalidated (e.g. in a cluster) - - try - { - readLock.lock(); - compiledModels = compiledModelsCache.get(tenantDomain); - } - finally - { - readLock.unlock(); - } - - if (compiledModels == null) - { - // unexpected - throw new AlfrescoRuntimeException("Failed to re-initialise compiledModelsCache " + tenantDomain); - } - } - - return compiledModels; - } - - /** - * Put compiledModels into the cache (in the context of the given tenant domain) - * - * @param tenantDomain - */ - private void putCompiledModels(String tenantDomain, Map compiledModels) - { - try - { - writeLock.lock(); - compiledModelsCache.put(tenantDomain, compiledModels); - } - finally - { - writeLock.unlock(); - } - } - - /** - * Remove compiledModels from the cache (in the context of the given tenant domain) - * - * @param tenantDomain - */ - private void removeCompiledModels(String tenantDomain) - { - try - { - writeLock.lock(); - if (compiledModelsCache.get(tenantDomain) != null) - { - compiledModelsCache.get(tenantDomain).clear(); - compiledModelsCache.remove(tenantDomain); - } - } - finally - { - writeLock.unlock(); - } + return getDictionaryRegistry(tenantDomain).getCompiledModels(); } /** @@ -982,80 +1063,8 @@ public class DictionaryDAOImpl implements DictionaryDAO */ private Map> getUriToModels(String tenantDomain) { - Map> uriToModels = null; - try - { - readLock.lock(); - uriToModels = uriToModelsCache.get(tenantDomain); - } - finally - { - readLock.unlock(); - } - - if (uriToModels == null) - { - reset(tenantDomain); // reset caches - may have been invalidated (e.g. in a cluster) - - try - { - readLock.lock(); - uriToModels = uriToModelsCache.get(tenantDomain); - } - finally - { - readLock.unlock(); - } - - if (uriToModels == null) - { - // unexpected - throw new AlfrescoRuntimeException("Failed to re-initialise uriToModelsCache " + tenantDomain); - } - } - - return uriToModels; - } - - /** - * Put uriToModels into the cache (in the context of the given tenant domain) - * - * @param tenantDomain - */ - private void putUriToModels(String tenantDomain, Map> uriToModels) - { - try - { - writeLock.lock(); - uriToModelsCache.put(tenantDomain, uriToModels); - } - finally - { - writeLock.unlock(); - } - } - - /** - * Remove uriToModels from the cache (in the context of the given tenant domain) - * - * @param tenantDomain - */ - private void removeUriToModels(String tenantDomain) - { - try - { - writeLock.lock(); - if (uriToModelsCache.get(tenantDomain) != null) - { - uriToModelsCache.get(tenantDomain).clear(); - uriToModelsCache.remove(tenantDomain); - } - } - finally - { - writeLock.unlock(); - } - } + return getDictionaryRegistry(tenantDomain).getUriToModels(); + } /** * Return diffs between input model and model in the Dictionary. @@ -1205,4 +1214,39 @@ public class DictionaryDAOImpl implements DictionaryDAO } } } + + /* package */ class DictionaryRegistry + { + private Map> uriToModels = new HashMap>(0); + private Map compiledModels = new HashMap(0); + + private String tenantDomain; + + public DictionaryRegistry(String tenantDomain) + { + this.tenantDomain = tenantDomain; + } + + public String getTenantDomain() + { + return tenantDomain; + } + + public Map> getUriToModels() + { + return uriToModels; + } + public void setUriToModels(Map> uriToModels) + { + this.uriToModels = uriToModels; + } + public Map getCompiledModels() + { + return compiledModels; + } + public void setCompiledModels(Map compiledModels) + { + this.compiledModels = compiledModels; + } + } } diff --git a/source/java/org/alfresco/repo/dictionary/DictionaryDAOTest.java b/source/java/org/alfresco/repo/dictionary/DictionaryDAOTest.java index 9cbe0186d1..e20669072b 100644 --- a/source/java/org/alfresco/repo/dictionary/DictionaryDAOTest.java +++ b/source/java/org/alfresco/repo/dictionary/DictionaryDAOTest.java @@ -39,7 +39,8 @@ import net.sf.ehcache.CacheManager; import org.alfresco.i18n.I18NUtil; import org.alfresco.model.ContentModel; import org.alfresco.repo.cache.EhCacheAdapter; -import org.alfresco.repo.dictionary.NamespaceDAOImpl.NamespaceData; +import org.alfresco.repo.dictionary.DictionaryDAOImpl.DictionaryRegistry; +import org.alfresco.repo.dictionary.NamespaceDAOImpl.NamespaceRegistry; import org.alfresco.repo.dictionary.constraint.RegexConstraint; import org.alfresco.repo.dictionary.constraint.StringLengthConstraint; import org.alfresco.repo.tenant.SingleTServiceImpl; @@ -107,19 +108,12 @@ public class DictionaryDAOTest extends TestCase { CacheManager cacheManager = new CacheManager(); - Cache uriToModelsEhCache = new Cache("uriToModelsCache", 50, false, true, 0L, 0L); - cacheManager.addCache(uriToModelsEhCache); - EhCacheAdapter>> uriToModelsCache = new EhCacheAdapter>>(); - uriToModelsCache.setCache(uriToModelsEhCache); + Cache dictionaryEhCache = new Cache("dictionaryCache", 50, false, true, 0L, 0L); + cacheManager.addCache(dictionaryEhCache); + EhCacheAdapter dictionaryCache = new EhCacheAdapter(); + dictionaryCache.setCache(dictionaryEhCache); - dictionaryDAO.setUriToModelsCache(uriToModelsCache); - - Cache compileModelsEhCache = new Cache("compiledModelsCache", 50, false, true, 0L, 0L); - cacheManager.addCache(compileModelsEhCache); - EhCacheAdapter> compileModelCache = new EhCacheAdapter>(); - compileModelCache.setCache(compileModelsEhCache); - - dictionaryDAO.setCompiledModelsCache(compileModelCache); + dictionaryDAO.setDictionaryRegistryCache(dictionaryCache); } private void initNamespaceCaches(NamespaceDAOImpl namespaceDAO) @@ -128,10 +122,10 @@ public class DictionaryDAOTest extends TestCase Cache namespaceEhCache = new Cache("namespaceCache", 50, false, true, 0L, 0L); cacheManager.addCache(namespaceEhCache); - EhCacheAdapter namespaceCache = new EhCacheAdapter(); + EhCacheAdapter namespaceCache = new EhCacheAdapter(); namespaceCache.setCache(namespaceEhCache); - namespaceDAO.setNamespaceDataCache(namespaceCache); + namespaceDAO.setNamespaceRegistryCache(namespaceCache); } diff --git a/source/java/org/alfresco/repo/dictionary/DictionaryModelType.java b/source/java/org/alfresco/repo/dictionary/DictionaryModelType.java index c941e3f304..a4604665e0 100644 --- a/source/java/org/alfresco/repo/dictionary/DictionaryModelType.java +++ b/source/java/org/alfresco/repo/dictionary/DictionaryModelType.java @@ -71,11 +71,12 @@ import org.apache.commons.logging.LogFactory; /** * Dictionary model type behaviour. * - * @author Roy Wetherall + * @author Roy Wetherall, janv */ public class DictionaryModelType implements ContentServicePolicies.OnContentUpdatePolicy, NodeServicePolicies.OnUpdatePropertiesPolicy, NodeServicePolicies.BeforeDeleteNodePolicy, + NodeServicePolicies.OnDeleteNodePolicy, NodeServicePolicies.OnCreateNodePolicy, NodeServicePolicies.OnRemoveAspectPolicy { @@ -85,6 +86,9 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda /** Key to the pending models */ private static final String KEY_PENDING_MODELS = "dictionaryModelType.pendingModels"; + /** Key to the pending deleted models */ + private static final String KEY_PENDING_DELETE_MODELS = "dictionaryModelType.pendingDeleteModels"; + /** Key to the removed "workingcopy" aspect */ private static final String KEY_WORKING_COPY = "dictionaryModelType.workingCopy"; @@ -214,7 +218,7 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda /** - * The initialise method + * The initialise method */ public void init() { @@ -224,25 +228,31 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda ContentModel.TYPE_DICTIONARY_MODEL, new JavaBehaviour(this, "onContentUpdate")); - // Register interest in the onPropertyUpdate policy for the dictionary model type + // Register interest in the onUpdateProperties policy for the dictionary model type policyComponent.bindClassBehaviour( QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"), ContentModel.TYPE_DICTIONARY_MODEL, new JavaBehaviour(this, "onUpdateProperties")); - // Register interest in the node delete policy + // Register interest in the beforeDeleteNode policy for the dictionary model type policyComponent.bindClassBehaviour( QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"), ContentModel.TYPE_DICTIONARY_MODEL, new JavaBehaviour(this, "beforeDeleteNode")); - // Register interest in the remove aspect policy + // Register interest in the onDeleteNode policy for the dictionary model type + policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onDeleteNode"), + ContentModel.TYPE_DICTIONARY_MODEL, + new JavaBehaviour(this, "onDeleteNode")); + + // Register interest in the onRemoveAspect policy policyComponent.bindClassBehaviour( QName.createQName(NamespaceService.ALFRESCO_URI, "onRemoveAspect"), this, new JavaBehaviour(this, "onRemoveAspect")); - // Register interest in the onCreateNode policy for the dictionary model type + // Register interest in the onCreateNode policy policyComponent.bindClassBehaviour( QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateNode"), this, @@ -321,6 +331,7 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda } } + @SuppressWarnings("unchecked") public void beforeDeleteNode(NodeRef nodeRef) { boolean workingCopy = nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY); @@ -336,18 +347,58 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda { archived = true; } - + // Ignore if the node is a working copy or archived if (! (workingCopy || archived)) { QName modelName = (QName)this.nodeService.getProperty(nodeRef, ContentModel.PROP_MODEL_NAME); if (modelName != null) { - // Validate model delete against usages - content and/or workflows - validateModelDelete(modelName); - - // Remove the model from the dictionary - dictionaryDAO.removeModel(modelName); + // Validate model delete against usages - content and/or workflows + validateModelDelete(modelName); + + if (logger.isDebugEnabled()) + { + logger.debug("beforeDeleteNode: modelName="+modelName+" ("+nodeRef+")"); + } + + Set pendingModelDeletes = (Set)AlfrescoTransactionSupport.getResource(KEY_PENDING_DELETE_MODELS); + if (pendingModelDeletes == null) + { + pendingModelDeletes = new HashSet(); + AlfrescoTransactionSupport.bindResource(KEY_PENDING_DELETE_MODELS, pendingModelDeletes); + } + pendingModelDeletes.add(tenantService.getName(nodeRef)); + + AlfrescoTransactionSupport.bindListener(this.transactionListener); + } + } + } + + @SuppressWarnings("unchecked") + public void onDeleteNode(ChildAssociationRef childAssocRef, boolean isNodeArchived) + { + NodeRef nodeRef = childAssocRef.getChildRef(); + + Set pendingDeleteModels = (Set)AlfrescoTransactionSupport.getResource(KEY_PENDING_DELETE_MODELS); + + if (pendingDeleteModels != null) + { + if (pendingDeleteModels.contains(nodeRef)) + { + String tenantDomain = tenantService.getDomain(nodeRef.getStoreRef().getIdentifier()); + String tenantSystemUserName = tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain); + + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() + { + // invalidate - to force lazy re-init + dictionaryDAO.destroy(); + + return null; + } + }, tenantSystemUserName); } } } @@ -395,6 +446,11 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda if (pendingModels != null) { + if (logger.isDebugEnabled()) + { + logger.debug("beforeCommit: pendingModelsCnt="+pendingModels.size()); + } + for (NodeRef pendingNodeRef : pendingModels) { String tenantDomain = tenantService.getDomain(pendingNodeRef.getStoreRef().getIdentifier()); @@ -405,7 +461,7 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda AuthenticationUtil.runAs(new RunAsWork() { public Object doWork() - { + { // Find out whether the model is active boolean isActive = false; Boolean value = (Boolean)nodeService.getProperty(nodeRef, ContentModel.PROP_MODEL_ACTIVE); @@ -464,8 +520,8 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda // Validate model against dictionary - could be new, unchanged or updated dictionaryDAO.validateModel(m2Model); - // Put the model - dictionaryDAO.putModel(m2Model); + // invalidate - to force lazy re-init + dictionaryDAO.destroy(); } } else @@ -476,19 +532,19 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda // Validate model delete against usages - content and/or workflows validateModelDelete(modelName); - // Remove the model from the dictionary - dictionaryDAO.removeModel(modelName); + // invalidate - to force lazy re-init + dictionaryDAO.destroy(); } } } - + return null; } }, tenantSystemUserName); } } } - + /** * @see java.lang.Object#equals(java.lang.Object) */ @@ -534,13 +590,13 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda // 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) + if (tenantService.isEnabled() && tenantService.isTenantUser() == false) { // shared model - need to check all tenants (whether enabled or disabled) unless they have overridden - List tenants = tenantAdminService.getAllTenants(); + List tenants = tenantAdminService.getAllTenants(); for (Tenant tenant : tenants) { - // validate model delete within context of tenant domain + // validate model delete within context of tenant domain AuthenticationUtil.runAs(new RunAsWork() { public Object doWork() @@ -550,7 +606,7 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda validateModelDelete(modelName, true); } return null; - } + } }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenant.getTenantDomain())); } } diff --git a/source/java/org/alfresco/repo/dictionary/DictionaryModelTypeTest.java b/source/java/org/alfresco/repo/dictionary/DictionaryModelTypeTest.java index edc1bcdcd0..989f4632cb 100644 --- a/source/java/org/alfresco/repo/dictionary/DictionaryModelTypeTest.java +++ b/source/java/org/alfresco/repo/dictionary/DictionaryModelTypeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2007 Alfresco Software Limited. + * Copyright (C) 2005-2009 Alfresco Software Limited. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -24,8 +24,13 @@ */ package org.alfresco.repo.dictionary; +import java.util.ArrayList; +import java.util.List; + import org.alfresco.model.ContentModel; import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.i18n.MessageService; +import org.alfresco.repo.tenant.TenantAdminService; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.coci.CheckOutCheckInService; import org.alfresco.service.cmr.dictionary.DictionaryException; @@ -33,6 +38,8 @@ import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.ModelDefinition; 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.search.SearchService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.util.BaseAlfrescoSpringTest; @@ -136,7 +143,35 @@ public class DictionaryModelTypeTest extends BaseAlfrescoSpringTest this.namespaceService = (NamespaceService)this.applicationContext.getBean("namespaceService"); this.cociService = (CheckOutCheckInService)this.applicationContext.getBean("checkOutCheckInService"); this.dictionaryDAO = (DictionaryDAO)this.applicationContext.getBean("dictionaryDAO"); + this.nodeService = (NodeService)this.applicationContext.getBean("NodeService"); + SearchService searchService = (SearchService)this.applicationContext.getBean("searchService"); + TenantAdminService tenantAdminService = (TenantAdminService)this.applicationContext.getBean("tenantAdminService"); + MessageService messageService = (MessageService)this.applicationContext.getBean("messageService"); + + DictionaryRepositoryBootstrap bootstrap = new DictionaryRepositoryBootstrap(); + bootstrap.setContentService(this.contentService); + bootstrap.setSearchService(searchService); + bootstrap.setDictionaryDAO(this.dictionaryDAO); + bootstrap.setTransactionService(this.transactionService); + bootstrap.setTenantAdminService(tenantAdminService); + bootstrap.setNodeService(this.nodeService); + bootstrap.setNamespaceService(this.namespaceService); + bootstrap.setMessageService(messageService); + + RepositoryLocation location = new RepositoryLocation(); + location.setStoreProtocol(this.storeRef.getProtocol()); + location.setStoreId(this.storeRef.getIdentifier()); + location.setQueryLanguage(SearchService.LANGUAGE_XPATH); + // NOTE: we are not setting the path for now .. in doing so we are searching the whole store + + List locations = new ArrayList(); + locations.add(location); + + bootstrap.setRepositoryModelsLocations(locations); + + // register with dictionary service + bootstrap.register(); } /** @@ -232,13 +267,20 @@ public class DictionaryModelTypeTest extends BaseAlfrescoSpringTest return null; } }); + + transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + public Object execute() throws Exception + { + DictionaryModelTypeTest.this.nodeService.deleteNode(modelNode); + return null; + } + }); } public void testIsActiveFlagAndDelete() { - this.dictionaryDAO.removeModel(TEST_MODEL_ONE); - try { // Check that the model has not yet been loaded into the dictionary diff --git a/source/java/org/alfresco/repo/dictionary/DictionaryRepositoryBootstrap.java b/source/java/org/alfresco/repo/dictionary/DictionaryRepositoryBootstrap.java index 50a72197b3..25a72a62da 100644 --- a/source/java/org/alfresco/repo/dictionary/DictionaryRepositoryBootstrap.java +++ b/source/java/org/alfresco/repo/dictionary/DictionaryRepositoryBootstrap.java @@ -251,8 +251,8 @@ public class DictionaryRepositoryBootstrap extends AbstractLifecycleBean impleme for (NodeRef dictionaryModel : nodeRefs) { - // Ignore if the node is a working copy or if its inactive - if (nodeService.hasAspect(dictionaryModel, ContentModel.ASPECT_WORKING_COPY) == false) + // Ignore if the node is a working copy or archived, or if its inactive + if (! (nodeService.hasAspect(dictionaryModel, ContentModel.ASPECT_WORKING_COPY) || nodeService.hasAspect(dictionaryModel, ContentModel.ASPECT_ARCHIVED))) { Boolean isActive = (Boolean)nodeService.getProperty(dictionaryModel, ContentModel.PROP_MODEL_ACTIVE); @@ -261,6 +261,11 @@ public class DictionaryRepositoryBootstrap extends AbstractLifecycleBean impleme M2Model model = createM2Model(dictionaryModel); if (model != null) { + if (logger.isTraceEnabled()) + { + logger.trace("onDictionaryInit: "+model.getName()+" ("+dictionaryModel+")"); + } + for (M2Namespace namespace : model.getNamespaces()) { modelMap.put(namespace.getUri(), model); @@ -407,22 +412,7 @@ public class DictionaryRepositoryBootstrap extends AbstractLifecycleBean impleme @Override protected void onBootstrap(ApplicationEvent event) { - // run as System on bootstrap - AuthenticationUtil.runAs(new RunAsWork() - { - public Object doWork() - { - init(); - return null; - } - }, AuthenticationUtil.getSystemUserName()); - - if (tenantAdminService.isEnabled()) - { - tenantAdminService.deployTenants(this, logger); - } - - register(); + register(); } @Override diff --git a/source/java/org/alfresco/repo/dictionary/DiffModelTest.java b/source/java/org/alfresco/repo/dictionary/DiffModelTest.java index 3bed6a386c..2d4491577d 100755 --- a/source/java/org/alfresco/repo/dictionary/DiffModelTest.java +++ b/source/java/org/alfresco/repo/dictionary/DiffModelTest.java @@ -27,7 +27,6 @@ package org.alfresco.repo.dictionary; import java.io.ByteArrayInputStream; import java.util.ArrayList; import java.util.List; -import java.util.Map; import junit.framework.TestCase; import net.sf.ehcache.Cache; @@ -35,7 +34,8 @@ import net.sf.ehcache.CacheManager; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.repo.cache.EhCacheAdapter; -import org.alfresco.repo.dictionary.NamespaceDAOImpl.NamespaceData; +import org.alfresco.repo.dictionary.DictionaryDAOImpl.DictionaryRegistry; +import org.alfresco.repo.dictionary.NamespaceDAOImpl.NamespaceRegistry; import org.alfresco.repo.tenant.SingleTServiceImpl; import org.alfresco.repo.tenant.TenantService; import org.alfresco.service.namespace.QName; @@ -789,19 +789,12 @@ public class DiffModelTest extends TestCase { CacheManager cacheManager = new CacheManager(); - Cache uriToModelsEhCache = new Cache("uriToModelsCache", 50, false, true, 0L, 0L); - cacheManager.addCache(uriToModelsEhCache); - EhCacheAdapter>> uriToModelsCache = new EhCacheAdapter>>(); - uriToModelsCache.setCache(uriToModelsEhCache); + Cache dictionaryEhCache = new Cache("dictionaryCache", 50, false, true, 0L, 0L); + cacheManager.addCache(dictionaryEhCache); + EhCacheAdapter dictionaryCache = new EhCacheAdapter(); + dictionaryCache.setCache(dictionaryEhCache); - dictionaryDAO.setUriToModelsCache(uriToModelsCache); - - Cache compileModelsEhCache = new Cache("compiledModelsCache", 50, false, true, 0L, 0L); - cacheManager.addCache(compileModelsEhCache); - EhCacheAdapter> compileModelCache = new EhCacheAdapter>(); - compileModelCache.setCache(compileModelsEhCache); - - dictionaryDAO.setCompiledModelsCache(compileModelCache); + dictionaryDAO.setDictionaryRegistryCache(dictionaryCache); } private void initNamespaceCaches(NamespaceDAOImpl namespaceDAO) @@ -810,10 +803,10 @@ public class DiffModelTest extends TestCase Cache namespaceEhCache = new Cache("namespaceCache", 50, false, true, 0L, 0L); cacheManager.addCache(namespaceEhCache); - EhCacheAdapter namespaceCache = new EhCacheAdapter(); + EhCacheAdapter namespaceCache = new EhCacheAdapter(); namespaceCache.setCache(namespaceEhCache); - namespaceDAO.setNamespaceDataCache(namespaceCache); + namespaceDAO.setNamespaceRegistryCache(namespaceCache); } public void testDeleteModel() diff --git a/source/java/org/alfresco/repo/dictionary/NamespaceDAOImpl.java b/source/java/org/alfresco/repo/dictionary/NamespaceDAOImpl.java index 2b327da2aa..9bf3285727 100644 --- a/source/java/org/alfresco/repo/dictionary/NamespaceDAOImpl.java +++ b/source/java/org/alfresco/repo/dictionary/NamespaceDAOImpl.java @@ -56,11 +56,11 @@ public class NamespaceDAOImpl implements NamespaceDAO private Lock writeLock = lock.writeLock(); // Internal cache (clusterable) - private SimpleCache namespaceDataCache; + private SimpleCache namespaceRegistryCache; // used to reset the cache - private ThreadLocal namespaceDataThreadLocal = new ThreadLocal(); - private ThreadLocal defaultNamespaceDataThreadLocal = new ThreadLocal(); + private ThreadLocal namespaceRegistryThreadLocal = new ThreadLocal(); + private ThreadLocal defaultNamespaceRegistryThreadLocal = new ThreadLocal(); // Dependencies private TenantService tenantService; @@ -72,9 +72,9 @@ public class NamespaceDAOImpl implements NamespaceDAO this.tenantService = tenantService; } - public void setNamespaceDataCache(SimpleCache namespaceDataCache) + public void setNamespaceRegistryCache(SimpleCache namespaceRegistryCache) { - this.namespaceDataCache = namespaceDataCache; + this.namespaceRegistryCache = namespaceRegistryCache; } public void registerDictionary(DictionaryDAO dictionaryDAO) @@ -98,7 +98,7 @@ public class NamespaceDAOImpl implements NamespaceDAO { String tenantDomain = getTenantDomain(); - removeNamespaceData(tenantDomain); + removeNamespaceRegistry(tenantDomain); if (logger.isDebugEnabled()) { @@ -109,7 +109,7 @@ public class NamespaceDAOImpl implements NamespaceDAO /** * Resets the namespaces (by re-initialising the dictionary) */ - private NamespaceData reset(String tenantDomain) + private NamespaceRegistry reset(String tenantDomain) { if (dictionaryDAO == null) { @@ -124,48 +124,48 @@ public class NamespaceDAOImpl implements NamespaceDAO dictionaryDAO.init(); - NamespaceData namespaceData = getNamespaceData(tenantDomain); + NamespaceRegistry namespaceRegistry = getNamespaceRegistry(tenantDomain); if (logger.isDebugEnabled()) { logger.debug("... resetting namespaces completed"); } - return namespaceData; + return namespaceRegistry; } - private NamespaceData initNamespace(String tenantDomain) + private NamespaceRegistry initNamespace(String tenantDomain) { try { createNamespaceLocal(tenantDomain); - NamespaceData namespaceData = initNamespaceData(tenantDomain); + NamespaceRegistry namespaceRegistry = initNamespaceRegistry(tenantDomain); - if (namespaceData == null) + if (namespaceRegistry == null) { // unexpected - throw new AlfrescoRuntimeException("Failed to init namespaceData " + tenantDomain); + throw new AlfrescoRuntimeException("Failed to init namespaceRegistry " + tenantDomain); } try { writeLock.lock(); - namespaceDataCache.put(tenantDomain, namespaceData); + namespaceRegistryCache.put(tenantDomain, namespaceRegistry); } finally { writeLock.unlock(); } - return namespaceData; + return namespaceRegistry; } finally { try { readLock.lock(); - if (namespaceDataCache.get(tenantDomain) != null) + if (namespaceRegistryCache.get(tenantDomain) != null) { removeNamespaceLocal(tenantDomain); } @@ -177,17 +177,17 @@ public class NamespaceDAOImpl implements NamespaceDAO } } - private NamespaceData initNamespaceData(String tenantDomain) + private NamespaceRegistry initNamespaceRegistry(String tenantDomain) { - getNamespaceData(tenantDomain).setUrisCache(new ArrayList()); - getNamespaceData(tenantDomain).setPrefixesCache(new HashMap()); + getNamespaceRegistry(tenantDomain).setUrisCache(new ArrayList()); + getNamespaceRegistry(tenantDomain).setPrefixesCache(new HashMap()); if (logger.isDebugEnabled()) { logger.debug("Empty namespaces initialised"); } - return getNamespaceDataLocal(tenantDomain); + return getNamespaceRegistryLocal(tenantDomain); } @@ -393,18 +393,18 @@ public class NamespaceDAOImpl implements NamespaceDAO // re-entrant (eg. via reset) - private NamespaceData getNamespaceData(String tenantDomain) + private NamespaceRegistry getNamespaceRegistry(String tenantDomain) { - NamespaceData namespaceData = null; + NamespaceRegistry namespaceRegistry = null; try { // check cache first - return if set readLock.lock(); - namespaceData = namespaceDataCache.get(tenantDomain); + namespaceRegistry = namespaceRegistryCache.get(tenantDomain); - if (namespaceData != null) + if (namespaceRegistry != null) { - return namespaceData; // return cached config + return namespaceRegistry; // return cached config } } finally @@ -413,62 +413,65 @@ public class NamespaceDAOImpl implements NamespaceDAO } // check threadlocal second - return if set - namespaceData = getNamespaceDataLocal(tenantDomain); - if (namespaceData != null) + namespaceRegistry = getNamespaceRegistryLocal(tenantDomain); + if (namespaceRegistry != null) { - return namespaceData; // return local namespaceData + return namespaceRegistry; // return local namespaceRegistry } // reset caches - may have been invalidated (e.g. in a cluster) - namespaceData = reset(tenantDomain); + namespaceRegistry = reset(tenantDomain); - if (namespaceData == null) + if (namespaceRegistry == null) { // unexpected - throw new AlfrescoRuntimeException("Failed to get namespaceData " + tenantDomain); + throw new AlfrescoRuntimeException("Failed to get namespaceRegistry " + tenantDomain); } - return namespaceData; + return namespaceRegistry; } // create threadlocal private void createNamespaceLocal(String tenantDomain) { // create threadlocal, if needed - NamespaceData namespaceData = getNamespaceDataLocal(tenantDomain); - if (namespaceData == null) + NamespaceRegistry namespaceRegistry = getNamespaceRegistryLocal(tenantDomain); + if (namespaceRegistry == null) { - namespaceData = new NamespaceData(tenantDomain); + namespaceRegistry = new NamespaceRegistry(tenantDomain); - if (tenantDomain.equals(TenantService.DEFAULT_DOMAIN)) + if (! tenantDomain.equals(TenantService.DEFAULT_DOMAIN)) { - defaultNamespaceDataThreadLocal.set(namespaceData); + namespaceRegistryThreadLocal.set(namespaceRegistry); + } - else + + if (defaultNamespaceRegistryThreadLocal.get() == null) { - namespaceDataThreadLocal.set(namespaceData); + namespaceRegistry = new NamespaceRegistry(TenantService.DEFAULT_DOMAIN); + defaultNamespaceRegistryThreadLocal.set(namespaceRegistry); } } } // get threadlocal - private NamespaceData getNamespaceDataLocal(String tenantDomain) + private NamespaceRegistry getNamespaceRegistryLocal(String tenantDomain) { - NamespaceData namespaceData = null; + NamespaceRegistry namespaceRegistry = null; if (tenantDomain.equals(TenantService.DEFAULT_DOMAIN)) { - namespaceData = this.defaultNamespaceDataThreadLocal.get(); + namespaceRegistry = this.defaultNamespaceRegistryThreadLocal.get(); } else { - namespaceData = this.namespaceDataThreadLocal.get(); + namespaceRegistry = this.namespaceRegistryThreadLocal.get(); } // check to see if domain switched (eg. during login) - if ((namespaceData != null) && (tenantDomain.equals(namespaceData.getTenantDomain()))) + if ((namespaceRegistry != null) && (tenantDomain.equals(namespaceRegistry.getTenantDomain()))) { - return namespaceData; // return threadlocal, if set + return namespaceRegistry; // return threadlocal, if set } return null; @@ -479,22 +482,22 @@ public class NamespaceDAOImpl implements NamespaceDAO { if (tenantDomain.equals(TenantService.DEFAULT_DOMAIN)) { - defaultNamespaceDataThreadLocal.set(null); // it's in the cache, clear the threadlocal + defaultNamespaceRegistryThreadLocal.set(null); // it's in the cache, clear the threadlocal } else { - namespaceDataThreadLocal.set(null); // it's in the cache, clear the threadlocal + namespaceRegistryThreadLocal.set(null); // it's in the cache, clear the threadlocal } } - private void removeNamespaceData(String tenantDomain) + private void removeNamespaceRegistry(String tenantDomain) { try { writeLock.lock(); - if (namespaceDataCache.get(tenantDomain) != null) + if (namespaceRegistryCache.get(tenantDomain) != null) { - namespaceDataCache.remove(tenantDomain); + namespaceRegistryCache.remove(tenantDomain); } removeNamespaceLocal(tenantDomain); @@ -523,7 +526,7 @@ public class NamespaceDAOImpl implements NamespaceDAO */ private List getUrisCtx(String tenantDomain) { - return getNamespaceData(tenantDomain).getUrisCache(); + return getNamespaceRegistry(tenantDomain).getUrisCache(); } /** @@ -544,7 +547,7 @@ public class NamespaceDAOImpl implements NamespaceDAO */ private Map getPrefixesCtx(String tenantDomain) { - return getNamespaceData(tenantDomain).getPrefixesCache(); + return getNamespaceRegistry(tenantDomain).getPrefixesCache(); } /** @@ -555,14 +558,14 @@ public class NamespaceDAOImpl implements NamespaceDAO return tenantService.getCurrentUserDomain(); } - /* package */ class NamespaceData + /* package */ class NamespaceRegistry { - private List urisCache; - private Map prefixesCache; + private List urisCache = new ArrayList(0); + private Map prefixesCache = new HashMap(0); private String tenantDomain; - public NamespaceData(String tenantDomain) + public NamespaceRegistry(String tenantDomain) { this.tenantDomain = tenantDomain; } diff --git a/source/java/org/alfresco/repo/dictionary/TestModel.java b/source/java/org/alfresco/repo/dictionary/TestModel.java index 84e3ea9886..3754a35c44 100644 --- a/source/java/org/alfresco/repo/dictionary/TestModel.java +++ b/source/java/org/alfresco/repo/dictionary/TestModel.java @@ -26,16 +26,15 @@ package org.alfresco.repo.dictionary; import java.util.ArrayList; import java.util.List; -import java.util.Map; import net.sf.ehcache.Cache; import net.sf.ehcache.CacheManager; import org.alfresco.repo.cache.EhCacheAdapter; -import org.alfresco.repo.dictionary.NamespaceDAOImpl.NamespaceData; +import org.alfresco.repo.dictionary.DictionaryDAOImpl.DictionaryRegistry; +import org.alfresco.repo.dictionary.NamespaceDAOImpl.NamespaceRegistry; import org.alfresco.repo.tenant.SingleTServiceImpl; import org.alfresco.repo.tenant.TenantService; -import org.alfresco.service.namespace.QName; /** @@ -114,19 +113,12 @@ public class TestModel { CacheManager cacheManager = new CacheManager(); - Cache uriToModelsEhCache = new Cache("uriToModelsCache", 50, false, true, 0L, 0L); - cacheManager.addCache(uriToModelsEhCache); - EhCacheAdapter>> uriToModelsCache = new EhCacheAdapter>>(); - uriToModelsCache.setCache(uriToModelsEhCache); + Cache dictionaryEhCache = new Cache("dictionaryCache", 50, false, true, 0L, 0L); + cacheManager.addCache(dictionaryEhCache); + EhCacheAdapter dictionaryCache = new EhCacheAdapter(); + dictionaryCache.setCache(dictionaryEhCache); - dictionaryDAO.setUriToModelsCache(uriToModelsCache); - - Cache compileModelsEhCache = new Cache("compiledModelsCache", 50, false, true, 0L, 0L); - cacheManager.addCache(compileModelsEhCache); - EhCacheAdapter> compileModelCache = new EhCacheAdapter>(); - compileModelCache.setCache(compileModelsEhCache); - - dictionaryDAO.setCompiledModelsCache(compileModelCache); + dictionaryDAO.setDictionaryRegistryCache(dictionaryCache); } private static void initNamespaceCaches(NamespaceDAOImpl namespaceDAO) @@ -135,9 +127,9 @@ public class TestModel Cache namespaceEhCache = new Cache("namespaceCache", 50, false, true, 0L, 0L); cacheManager.addCache(namespaceEhCache); - EhCacheAdapter namespaceCache = new EhCacheAdapter(); + EhCacheAdapter namespaceCache = new EhCacheAdapter(); namespaceCache.setCache(namespaceEhCache); - namespaceDAO.setNamespaceDataCache(namespaceCache); + namespaceDAO.setNamespaceRegistryCache(namespaceCache); } } \ No newline at end of file diff --git a/source/java/org/alfresco/repo/policy/PolicyComponentTest.java b/source/java/org/alfresco/repo/policy/PolicyComponentTest.java index 95ae4dbe0a..e468e659d8 100644 --- a/source/java/org/alfresco/repo/policy/PolicyComponentTest.java +++ b/source/java/org/alfresco/repo/policy/PolicyComponentTest.java @@ -29,10 +29,7 @@ import java.util.Collection; import java.util.List; import junit.framework.TestCase; -import net.sf.ehcache.Cache; -import net.sf.ehcache.CacheManager; -import org.alfresco.repo.cache.EhCacheAdapter; import org.alfresco.repo.cache.NullCache; import org.alfresco.repo.dictionary.DictionaryBootstrap; import org.alfresco.repo.dictionary.DictionaryComponent; @@ -93,30 +90,15 @@ public class PolicyComponentTest extends TestCase @SuppressWarnings("unchecked") private void initDictionaryCaches(DictionaryDAOImpl dictionaryDAO) { - CacheManager cacheManager = new CacheManager(); - - Cache uriToModelsEhCache = new Cache("uriToModelsCache", 50, false, true, 0L, 0L); - cacheManager.addCache(uriToModelsEhCache); - //EhCacheAdapter>> uriToModelsCache = new EhCacheAdapter>>(); - EhCacheAdapter uriToModelsCache = new EhCacheAdapter(); - uriToModelsCache.setCache(uriToModelsEhCache); - - dictionaryDAO.setUriToModelsCache(uriToModelsCache); - - Cache compileModelsEhCache = new Cache("compiledModelsCache", 50, false, true, 0L, 0L); - cacheManager.addCache(compileModelsEhCache); - //EhCacheAdapter> compileModelCache = new EhCacheAdapter>(); - EhCacheAdapter compileModelCache = new EhCacheAdapter(); - compileModelCache.setCache(compileModelsEhCache); - - dictionaryDAO.setCompiledModelsCache(compileModelCache); + // note: unit tested here with null cache + dictionaryDAO.setDictionaryRegistryCache(new NullCache()); } @SuppressWarnings("unchecked") private void initNamespaceCaches(NamespaceDAOImpl namespaceDAO) { // note: unit tested here with null cache - namespaceDAO.setNamespaceDataCache(new NullCache()); + namespaceDAO.setNamespaceRegistryCache(new NullCache()); } public void testJavaBehaviour() diff --git a/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneIndexerAndSearcherFactory.java b/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneIndexerAndSearcherFactory.java index 00f938a7fc..8713ee049b 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneIndexerAndSearcherFactory.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneIndexerAndSearcherFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2007 Alfresco Software Limited. + * Copyright (C) 2005-2009 Alfresco Software Limited. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -34,6 +34,7 @@ import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.ContentService; 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; /** @@ -134,6 +135,19 @@ public class ADMLuceneIndexerAndSearcherFactory extends AbstractLuceneIndexerAnd searcher.setDictionaryService(dictionaryService); return searcher; } + + protected SearchService getNodeSearcher() throws SearcherException + { + ADMLuceneSearcherImpl searcher = ADMLuceneSearcherImpl.getNodeSearcher(); + searcher.setNamespacePrefixResolver(nameSpaceService); + searcher.setNodeService(nodeService); + searcher.setTenantService(tenantService); + searcher.setDictionaryService(dictionaryService); + searcher.setQueryRegister(getQueryRegister()); + searcher.setQueryEngine(queryEngine); + searcher.setDictionaryService(dictionaryService); + return searcher; + } protected List getAllStores() diff --git a/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneSearcherImpl.java b/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneSearcherImpl.java index 512e6e815a..a25450bb46 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneSearcherImpl.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneSearcherImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2007 Alfresco Software Limited. + * Copyright (C) 2005-2009 Alfresco Software Limited. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -37,7 +37,6 @@ import java.util.Locale; import java.util.Map; import java.util.Set; -import org.alfresco.cmis.CMISQueryException; import org.alfresco.i18n.I18NUtil; import org.alfresco.repo.search.CannedQueryDef; import org.alfresco.repo.search.EmptyResultSet; @@ -53,11 +52,9 @@ import org.alfresco.repo.search.impl.querymodel.QueryEngine; import org.alfresco.repo.search.impl.querymodel.QueryEngineResults; import org.alfresco.repo.search.impl.querymodel.QueryModelFactory; import org.alfresco.repo.search.impl.querymodel.QueryOptions; -import org.alfresco.repo.search.impl.querymodel.Selector; import org.alfresco.repo.search.results.SortedResultSet; import org.alfresco.repo.tenant.TenantService; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; -import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.repository.InvalidNodeRefException; import org.alfresco.service.cmr.repository.NodeRef; @@ -161,7 +158,16 @@ public class ADMLuceneSearcherImpl extends AbstractLuceneBase implements LuceneS { return getSearcher(storeRef, null, config); } - + + /** + * Get a select-node-based searcher + * @return + */ + public static ADMLuceneSearcherImpl getNodeSearcher() + { + return new ADMLuceneSearcherImpl(); + } + public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver) { this.namespacePrefixResolver = namespacePrefixResolver; diff --git a/source/java/org/alfresco/repo/search/impl/lucene/AVMLuceneIndexerAndSearcherFactory.java b/source/java/org/alfresco/repo/search/impl/lucene/AVMLuceneIndexerAndSearcherFactory.java index d1463f4cd4..8593d53f9b 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/AVMLuceneIndexerAndSearcherFactory.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/AVMLuceneIndexerAndSearcherFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2007 Alfresco Software Limited. + * Copyright (C) 2005-2009 Alfresco Software Limited. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -40,9 +40,8 @@ import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.ContentService; 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.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** * Factory for AVM indexers and searchers @@ -52,7 +51,7 @@ import org.apache.commons.logging.LogFactory; */ public class AVMLuceneIndexerAndSearcherFactory extends AbstractLuceneIndexerAndSearcherFactory implements SupportsBackgroundIndexing { - private static Log s_logger = LogFactory.getLog(AVMLuceneIndexerAndSearcherFactory.class); + //private static Log s_logger = LogFactory.getLog(AVMLuceneIndexerAndSearcherFactory.class); private DictionaryService dictionaryService; @@ -190,6 +189,12 @@ public class AVMLuceneIndexerAndSearcherFactory extends AbstractLuceneIndexerAnd searcher.setDictionaryService(dictionaryService); return searcher; } + + @Override + protected SearchService getNodeSearcher() throws SearcherException + { + throw new UnsupportedOperationException(); + } /** * Register the full text searcher (done by the seracher bean to break cyclic bean defs) diff --git a/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerAndSearcherFactory.java b/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerAndSearcherFactory.java index e35f096a6c..1ddd4c25b8 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerAndSearcherFactory.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerAndSearcherFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2007 Alfresco Software Limited. + * Copyright (C) 2005-2009 Alfresco Software Limited. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -60,6 +60,7 @@ import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; 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.transaction.TransactionService; import org.alfresco.util.GUID; import org.apache.commons.logging.Log; @@ -464,6 +465,11 @@ public abstract class AbstractLuceneIndexerAndSearcherFactory implements LuceneI LuceneSearcher searcher = getSearcher(storeRef, indexer); return searcher; } + + /** + * Get node-based searcher (for "selectNodes / selectProperties") + */ + protected abstract SearchService getNodeSearcher() throws SearcherException; /** * Get a searcher over the index and the current delta diff --git a/source/java/org/alfresco/repo/tenant/MultiTDemoTest.java b/source/java/org/alfresco/repo/tenant/MultiTDemoTest.java index dd50f46bd2..6801dbd847 100644 --- a/source/java/org/alfresco/repo/tenant/MultiTDemoTest.java +++ b/source/java/org/alfresco/repo/tenant/MultiTDemoTest.java @@ -493,12 +493,13 @@ public class MultiTDemoTest extends TestCase { NodeRef homeSpaceRef = getHomeSpaceFolderNode(tenantUserName); - NodeRef contentRef = addTextContent(homeSpaceRef, tenantUserName+" quick brown fox.txt", "The quick brown fox jumps over the lazy dog (tenant " + tenantDomain + ")"); + NodeRef contentRef = addContent(homeSpaceRef, tenantUserName+" quick brown fox.txt", "The quick brown fox jumps over the lazy dog (tenant " + tenantDomain + ")", MimetypeMap.MIMETYPE_TEXT_PLAIN); nodeService.addAspect(contentRef, ContentModel.ASPECT_VERSIONABLE, null); if (tenantDomain.equals(TEST_TENANT_DOMAIN2)) { - contentRef = addTextContent(homeSpaceRef, tenantUserName+" quick brown fox ANO.txt", "The quick brown fox jumps over the lazy dog ANO (tenant " + tenantDomain + ")"); + contentRef = addContent(homeSpaceRef, tenantUserName+" quick brown fox ANO.txt", "The quick brown fox jumps over the lazy dog ANO (tenant " + tenantDomain + ")", MimetypeMap.MIMETYPE_TEXT_PLAIN); + nodeService.addAspect(contentRef, ContentModel.ASPECT_VERSIONABLE, null); } @@ -519,7 +520,12 @@ public class MultiTDemoTest extends TestCase { public Object doWork() throws Exception { - assertTrue("System: ", (nodeService.getStores().size() >= (DEFAULT_STORE_COUNT * (tenants.size()+1)))); + for (StoreRef storeRef : nodeService.getStores()) + { + System.out.println("StoreRef: "+storeRef); + } + + assertTrue("System: "+nodeService.getStores().size()+", "+(tenants.size()+1), (nodeService.getStores().size() >= (DEFAULT_STORE_COUNT * (tenants.size()+1)))); return null; } }, AuthenticationUtil.getSystemUserName()); @@ -529,7 +535,7 @@ public class MultiTDemoTest extends TestCase { public Object doWork() throws Exception { - assertTrue("Super admin: ", (nodeService.getStores().size() >= DEFAULT_STORE_COUNT)); + assertTrue("Super admin: "+nodeService.getStores().size(), (nodeService.getStores().size() >= DEFAULT_STORE_COUNT)); return null; } }, AuthenticationUtil.getAdminUserName()); @@ -644,8 +650,8 @@ public class MultiTDemoTest extends TestCase public Object doWork() throws Exception { NodeRef homeSpaceRef = getHomeSpaceFolderNode(tenantUserName); - NodeRef contentRef = addTextContent(homeSpaceRef, tenantUserName+" tqbfjotld.txt", "The quick brown fox jumps over the lazy dog (tenant " + tenantDomain + ")"); - + NodeRef contentRef = addContent(homeSpaceRef, tenantUserName+" tqbfjotld.txt", "The quick brown fox jumps over the lazy dog (tenant " + tenantDomain + ")", MimetypeMap.MIMETYPE_TEXT_PLAIN); + NodeRef storeArchiveNode = nodeService.getStoreArchiveNode(contentRef.getStoreRef()); nodeService.deleteNode(contentRef); @@ -662,9 +668,9 @@ public class MultiTDemoTest extends TestCase } } - public void testCustomDynamicModels() + public void testCustomModels() { - logger.info("test custom/dynamic models"); + logger.info("test custom models"); final int defaultModelCnt = dictionaryService.getAllModels().size(); @@ -715,6 +721,33 @@ public class MultiTDemoTest extends TestCase } } + public void testAddCustomWebClient() + { + // note: add as demo files - need to re-start Alfresco to see custom web client config / messages + logger.info("test add custom web client config"); + + for (final String tenantDomain : tenants) + { + final String tenantAdminName = tenantService.getDomainUser(AuthenticationUtil.getAdminUserName(), tenantDomain); + + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() throws Exception + { + NodeRef webClientExtFolder = getWebClientExtensionNodeRef(SPACES_STORE); + + InputStream is = getClass().getClassLoader().getResourceAsStream("tenant/webclient.properties"); + addContent(webClientExtFolder, "webclient.properties", is, MimetypeMap.MIMETYPE_TEXT_PLAIN); + + is = getClass().getClassLoader().getResourceAsStream("tenant/web-client-config-custom.xml"); + addContent(webClientExtFolder, "web-client-config-custom.xml", is, MimetypeMap.MIMETYPE_XML); + + return null; + } + }, tenantAdminName); + } + } + private void createGroup(String shortName, String parentShortName) { // create new Group using authority Service @@ -816,6 +849,12 @@ public class MultiTDemoTest extends TestCase return findFolderNodeRef(storeRef, "/app:company_home/app:user_homes"); } + private NodeRef getWebClientExtensionNodeRef(StoreRef storeRef) + { + // get the "Web Client Extensions" location + return findFolderNodeRef(storeRef, "/app:company_home/app:dictionary/app:webclient_extension"); + } + private NodeRef findFolderNodeRef(StoreRef storeRef, String folderXPath) { ResultSet rs = this.searchService.query(storeRef, SearchService.LANGUAGE_XPATH, folderXPath); @@ -926,7 +965,7 @@ public class MultiTDemoTest extends TestCase return (NodeRef)this.nodeService.getProperty(personService.getPerson(userName), ContentModel.PROP_HOMEFOLDER); } - private NodeRef addTextContent(NodeRef spaceRef, String name, String textData) + private NodeRef addContent(NodeRef spaceRef, String name, String textData, String mimeType) { Map contentProps = new HashMap(); contentProps.put(ContentModel.PROP_NAME, name); @@ -947,7 +986,7 @@ public class MultiTDemoTest extends TestCase ContentWriter writer = contentService.getWriter(content, ContentModel.PROP_CONTENT, true); - writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setMimetype(mimeType); writer.setEncoding("UTF-8"); writer.putContent(textData); @@ -955,6 +994,35 @@ public class MultiTDemoTest extends TestCase return content; } + private NodeRef addContent(NodeRef spaceRef, String name, InputStream is, String mimeType) + { + Map contentProps = new HashMap(); + contentProps.put(ContentModel.PROP_NAME, name); + + ChildAssociationRef association = nodeService.createNode(spaceRef, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name), + ContentModel.TYPE_CONTENT, + contentProps); + + NodeRef content = association.getChildRef(); + + // add titled aspect (for Web Client display) + Map titledProps = new HashMap(); + titledProps.put(ContentModel.PROP_TITLE, name); + titledProps.put(ContentModel.PROP_DESCRIPTION, name); + this.nodeService.addAspect(content, ContentModel.ASPECT_TITLED, titledProps); + + ContentWriter writer = contentService.getWriter(content, ContentModel.PROP_CONTENT, true); + + writer.setMimetype(mimeType); + writer.setEncoding("UTF-8"); + + writer.putContent(is); + + return content; + } + // comment-in to run from command line public static void main(String args[]) diff --git a/source/java/org/alfresco/repo/workflow/WorkflowDeployer.java b/source/java/org/alfresco/repo/workflow/WorkflowDeployer.java index 60c475a03b..b08deb7f94 100644 --- a/source/java/org/alfresco/repo/workflow/WorkflowDeployer.java +++ b/source/java/org/alfresco/repo/workflow/WorkflowDeployer.java @@ -256,7 +256,7 @@ public class WorkflowDeployer extends AbstractLifecycleBean userTransaction.begin(); // bootstrap the workflow models and static labels (from classpath) - if (models != null && resourceBundles != null) + if (models != null && resourceBundles != null && ((models.size() > 0) || (resourceBundles.size() > 0))) { DictionaryBootstrap dictionaryBootstrap = new DictionaryBootstrap(); dictionaryBootstrap.setDictionaryDAO(dictionaryDAO); diff --git a/source/test-resources/tenant/web-client-config-custom.xml b/source/test-resources/tenant/web-client-config-custom.xml new file mode 100755 index 0000000000..9004a67709 --- /dev/null +++ b/source/test-resources/tenant/web-client-config-custom.xml @@ -0,0 +1,121 @@ + + + + + + 2 + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/test-resources/tenant/webclient.properties b/source/test-resources/tenant/webclient.properties new file mode 100755 index 0000000000..6d45948495 --- /dev/null +++ b/source/test-resources/tenant/webclient.properties @@ -0,0 +1,15 @@ +# web client custom I18N message properties + +# example model +# see display-label-ids added to the web-client-config-custom.xml sample + +# my:sop +my.publishedDate= publié Published Date +my.authorisedBy=Authorised By +my.signOff= publié Sign Off +my.processSteps=Process Steps + +# my:imageClassification +my.width=Width +my.height=Height +my.resolution=Resolution \ No newline at end of file