diff --git a/config/alfresco/ehcache-default.xml b/config/alfresco/ehcache-default.xml
index 99a645e731..e2955195cd 100644
--- a/config/alfresco/ehcache-default.xml
+++ b/config/alfresco/ehcache-default.xml
@@ -292,5 +292,43 @@
eternal="true"
overflowToDisk="true"
/>
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/config/alfresco/extension/ehcache-custom.xml.sample.cluster b/config/alfresco/extension/ehcache-custom.xml.sample.cluster
index 4467734168..290add6a39 100644
--- a/config/alfresco/extension/ehcache-custom.xml.sample.cluster
+++ b/config/alfresco/extension/ehcache-custom.xml.sample.cluster
@@ -517,6 +517,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/config/alfresco/tenant-single-context.xml b/config/alfresco/tenant-single-context.xml
index af6d58b828..8bbdecc72f 100644
--- a/config/alfresco/tenant-single-context.xml
+++ b/config/alfresco/tenant-single-context.xml
@@ -8,5 +8,7 @@
+
+
diff --git a/source/java/org/alfresco/repo/config/source/RepoUrlConfigSource.java b/source/java/org/alfresco/repo/config/source/RepoUrlConfigSource.java
new file mode 100644
index 0000000000..e82a8528a6
--- /dev/null
+++ b/source/java/org/alfresco/repo/config/source/RepoUrlConfigSource.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2005-2007 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
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ * As a special exception to the terms and conditions of version 2.0 of
+ * the GPL, you may redistribute this Program in connection with Free/Libre
+ * and Open Source Software ("FLOSS") applications as described in Alfresco's
+ * FLOSS exception. You should have recieved a copy of the text describing
+ * the FLOSS exception, and it is also available here:
+ * http://www.alfresco.com/legal/licensing"
+ */
+package org.alfresco.repo.config.source;
+
+import java.io.InputStream;
+import java.util.List;
+
+import org.alfresco.config.ConfigException;
+import org.alfresco.config.source.UrlConfigSource;
+import org.alfresco.model.ContentModel;
+import org.alfresco.repo.tenant.TenantService;
+import org.alfresco.service.cmr.repository.ContentReader;
+import org.alfresco.service.cmr.repository.ContentService;
+import org.alfresco.service.cmr.repository.InvalidStoreRefException;
+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;
+
+/**
+ * ConfigSource that looks for a prefix to determine where to look for the config.
+ * Valid prefixes are:
+ *
+ *
:// the location provided is a path to a repository file
+ *
+ * as well as those defined in the core (UrlConfigSource)
+ *
+ * Example store URLs
+ * workspace://SpacesStore/${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.webclient_extension.childname}/cm:web-client-config-custom.xml
+ * workspace://SpacesStore/app:company_home/app:dictionary/cm:webclient_extension/cm:web-client-config-custom.xml
+ */
+public class RepoUrlConfigSource extends UrlConfigSource
+{
+ private TenantService tenantService;
+ private SearchService searchService;
+ private ContentService contentService;
+ private NamespaceService namespaceService;
+ private NodeService nodeService;
+
+
+ public void setTenantService(TenantService tenantService)
+ {
+ this.tenantService = tenantService;
+ }
+
+ public void setSearchService(SearchService searchService)
+ {
+ this.searchService = searchService;
+ }
+
+ public void setContentService(ContentService contentService)
+ {
+ this.contentService = contentService;
+ }
+
+ public void setNamespaceService(NamespaceService namespaceService)
+ {
+ this.namespaceService = namespaceService;
+ }
+
+ public void setNodeService(NodeService nodeService)
+ {
+ this.nodeService = nodeService;
+ }
+
+
+ public RepoUrlConfigSource(String sourceLocation)
+ {
+ super(sourceLocation);
+ }
+
+ public RepoUrlConfigSource(List sourceLocations)
+ {
+ super(sourceLocations);
+ }
+
+
+ public InputStream getInputStream(String sourceUrl)
+ {
+ // determine the config source
+ try
+ {
+ return super.getInputStream(sourceUrl);
+ }
+ catch (ConfigException ce)
+ {
+ int idx = sourceUrl.indexOf(StoreRef.URI_FILLER);
+ if (idx != -1)
+ {
+ // assume this is a repository location
+ int idx2 = sourceUrl.indexOf("/", idx+3);
+
+ String store = sourceUrl.substring(0, idx2);
+ String path = sourceUrl.substring(idx2);
+
+ StoreRef storeRef = tenantService.getName(new StoreRef(store));
+ NodeRef rootNode = null;
+
+ try
+ {
+ rootNode = nodeService.getRootNode(storeRef);
+ }
+ catch (InvalidStoreRefException e)
+ {
+ throw ce;
+ }
+
+ List nodeRefs = searchService.selectNodes(rootNode, path, null, namespaceService, false);
+
+ if (nodeRefs.size() == 0)
+ {
+ // if none found, then simply skip
+ return null;
+ }
+ else if (nodeRefs.size() > 1)
+ {
+ // unexpected
+ throw new ConfigException("Found duplicate config sources in the repository " + sourceUrl);
+ }
+
+ NodeRef nodeRef = nodeRefs.get(0);
+
+ ContentReader cr = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT);
+
+ return cr.getContentInputStream();
+ }
+ else
+ {
+ // not a repository url
+ throw ce;
+ }
+ }
+ }
+}
diff --git a/source/java/org/alfresco/repo/config/xml/RepoXMLConfigService.java b/source/java/org/alfresco/repo/config/xml/RepoXMLConfigService.java
new file mode 100644
index 0000000000..a3169129e3
--- /dev/null
+++ b/source/java/org/alfresco/repo/config/xml/RepoXMLConfigService.java
@@ -0,0 +1,594 @@
+/*
+ * Copyright (C) 2005-2007 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
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ * As a special exception to the terms and conditions of version 2.0 of
+ * the GPL, you may redistribute this Program in connection with Free/Libre
+ * and Open Source Software ("FLOSS") applications as described in Alfresco's
+ * FLOSS exception. You should have recieved a copy of the text describing
+ * the FLOSS exception, and it is also available here:
+ * http://www.alfresco.com/legal/licensing"
+ */
+package org.alfresco.repo.config.xml;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import javax.transaction.UserTransaction;
+
+import org.alfresco.config.ConfigImpl;
+import org.alfresco.config.ConfigSection;
+import org.alfresco.config.ConfigSource;
+import org.alfresco.config.evaluator.Evaluator;
+import org.alfresco.config.xml.XMLConfigService;
+import org.alfresco.config.xml.elementreader.ConfigElementReader;
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.repo.cache.SimpleCache;
+import org.alfresco.repo.security.authentication.AuthenticationComponent;
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
+import org.alfresco.repo.tenant.TenantDeployer;
+import org.alfresco.repo.tenant.TenantDeployerService;
+import org.alfresco.repo.tenant.TenantService;
+import org.alfresco.service.transaction.TransactionService;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * XML-based configuration service which can optionally read config from the Repository
+ *
+ */
+public class RepoXMLConfigService extends XMLConfigService implements TenantDeployer
+{
+ private static final Log logger = LogFactory.getLog(RepoXMLConfigService.class);
+
+ /**
+ * Lock objects
+ */
+ private ReadWriteLock lock = new ReentrantReadWriteLock();
+ private Lock readLock = lock.readLock();
+ private Lock writeLock = lock.writeLock();
+
+ // Dependencies
+ private TransactionService transactionService;
+ private AuthenticationComponent authenticationComponent;
+ private TenantDeployerService tenantDeployerService;
+ private TenantService tenantService;
+
+ // Internal caches that are clusterable
+ private SimpleCache globalConfigCache;
+ private SimpleCache> evaluatorsCache;
+ private SimpleCache>> sectionsByAreaCache;
+ private SimpleCache> sectionsCache;
+ private SimpleCache> elementReadersCache;
+
+
+ public void setTransactionService(TransactionService transactionService)
+ {
+ this.transactionService = transactionService;
+ }
+
+ public void setAuthenticationComponent(AuthenticationComponent authenticationComponent)
+ {
+ this.authenticationComponent = authenticationComponent;
+ }
+
+ public void setTenantDeployerService(TenantDeployerService tenantDeployerService)
+ {
+ this.tenantDeployerService = tenantDeployerService;
+ }
+
+ public void setTenantService(TenantService tenantService)
+ {
+ this.tenantService = tenantService;
+ }
+
+
+ public void setGlobalConfigCache(SimpleCache globalConfigCache)
+ {
+ this.globalConfigCache = globalConfigCache;
+ }
+
+ public void setEvaluatorsCache(SimpleCache> evaluatorsCache)
+ {
+ this.evaluatorsCache = evaluatorsCache;
+ }
+
+ public void setSectionsByAreaCache(SimpleCache>> sectionsByAreaCache)
+ {
+ this.sectionsByAreaCache = sectionsByAreaCache;
+ }
+
+ public void setSectionsCache(SimpleCache> sectionsCache)
+ {
+ this.sectionsCache = sectionsCache;
+ }
+
+ public void setElementReadersCache(SimpleCache> elementReadersCache)
+ {
+ this.elementReadersCache = elementReadersCache;
+ }
+
+
+ /**
+ * Constructs an XMLConfigService using the given config source
+ *
+ * @param configSource
+ * A ConfigSource
+ */
+ public RepoXMLConfigService(ConfigSource configSource)
+ {
+ super(configSource);
+ }
+
+ public void init()
+ {
+ // can be null e.g. initial login, after fresh bootstrap
+ String currentUser = authenticationComponent.getCurrentUserName();
+ if (currentUser == null)
+ {
+ authenticationComponent.setCurrentUser(authenticationComponent.getSystemUserName());
+ }
+
+ UserTransaction userTransaction = transactionService.getUserTransaction();
+
+ try
+ {
+ userTransaction.begin();
+
+ super.init(); // parse config and initialise caches
+
+ userTransaction.commit();
+
+ logger.info("Config initialised");
+ }
+ catch(Throwable e)
+ {
+ try { userTransaction.rollback(); } catch (Exception ex) {}
+ throw new AlfrescoRuntimeException("Failed to initialise config service", e);
+ }
+ finally
+ {
+ if (currentUser == null)
+ {
+ authenticationComponent.clearCurrentSecurityContext();
+ }
+ }
+ }
+
+ public void destroy()
+ {
+ super.destroy();
+
+ logger.info("Config destroyed");
+ }
+
+
+ @Override
+ protected void onBootstrap(ApplicationEvent event)
+ {
+ // run as System on bootstrap
+ AuthenticationUtil.runAs(new RunAsWork