/* * 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.ConfigDeployment; 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.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; // 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 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 List initConfig() { List configDeployments = null; // 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(); // parse config and initialise caches configDeployments = super.initConfig(); 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(); } } return configDeployments; } public void destroy() { super.destroy(); logger.info("Config destroyed"); } @Override protected void onBootstrap(ApplicationEvent event) { // run as System on bootstrap AuthenticationUtil.runAs(new RunAsWork() { public Object doWork() { initConfig(); return null; } }, AuthenticationUtil.getSystemUserName()); if ((tenantDeployerService != null) && (tenantDeployerService.isEnabled())) { tenantDeployerService.deployTenants(this, logger); tenantDeployerService.register(this); } } @Override protected void onShutdown(ApplicationEvent event) { // run as System on shutdown AuthenticationUtil.runAs(new RunAsWork() { public Object doWork() { destroy(); return null; } }, AuthenticationUtil.getSystemUserName()); if ((tenantDeployerService != null) && (tenantDeployerService.isEnabled())) { tenantDeployerService.undeployTenants(this, logger); tenantDeployerService.unregister(this); } } public void onEnableTenant() { initConfig(); // will be called in context of tenant } public void onDisableTenant() { destroy(); // will be called in context of tenant } @Override protected ConfigImpl getGlobalConfigImpl() { String tenantDomain = getTenantDomain(); ConfigImpl globalConfig = null; try { readLock.lock(); globalConfig = globalConfigCache.get(tenantDomain); } finally { readLock.unlock(); } if (globalConfig == null) { reset(); // reset caches - may have been invalidated (e.g. in a cluster) try { readLock.lock(); globalConfig = globalConfigCache.get(tenantDomain); } finally { readLock.unlock(); } if (globalConfig == null) { // unexpected throw new AlfrescoRuntimeException("Failed to reset caches (globalConfigCache) " + tenantDomain); } } return globalConfig; } @Override protected void putGlobalConfig(ConfigImpl globalConfig) { try { writeLock.lock(); globalConfigCache.put(getTenantDomain(), globalConfig); } finally { writeLock.unlock(); } } @Override protected void removeGlobalConfig() { try { writeLock.lock(); String tenantDomain = getTenantDomain(); if (globalConfigCache.get(tenantDomain) != null) { globalConfigCache.remove(tenantDomain); } } finally { writeLock.unlock(); } } @Override protected Map getEvaluators() { String tenantDomain = getTenantDomain(); Map evaluators = null; try { readLock.lock(); evaluators = evaluatorsCache.get(tenantDomain); } finally { readLock.unlock(); } if (evaluators == null) { reset(); // reset caches - may have been invalidated (e.g. in a cluster) try { readLock.lock(); evaluators = evaluatorsCache.get(tenantDomain); } finally { readLock.unlock(); } if (evaluators == null) { // unexpected throw new AlfrescoRuntimeException("Failed to reset caches (evaluatorsCache) " + tenantDomain); } } return evaluators; } @Override protected void putEvaluators(Map evaluators) { try { writeLock.lock(); evaluatorsCache.put(getTenantDomain(), evaluators); } finally { writeLock.unlock(); } } @Override protected void removeEvaluators() { try { writeLock.lock(); String tenantDomain = getTenantDomain(); if (evaluatorsCache.get(tenantDomain) != null) { evaluatorsCache.get(tenantDomain).clear(); evaluatorsCache.remove(tenantDomain); } } finally { writeLock.unlock(); } } @Override protected Map> getSectionsByArea() { String tenantDomain = getTenantDomain(); Map> sectionsByArea = null; try { readLock.lock(); sectionsByArea = sectionsByAreaCache.get(tenantDomain); } finally { readLock.unlock(); } if (sectionsByArea == null) { reset(); // reset caches - may have been invalidated (e.g. in a cluster) try { readLock.lock(); sectionsByArea = sectionsByAreaCache.get(tenantDomain); } finally { readLock.unlock(); } if (sectionsByArea == null) { // unexpected throw new AlfrescoRuntimeException("Failed to reset caches (sectionsByAreaCache) " + tenantDomain); } } return sectionsByArea; } @Override protected void putSectionsByArea(Map> sectionsByArea) { try { writeLock.lock(); sectionsByAreaCache.put(getTenantDomain(), sectionsByArea); } finally { writeLock.unlock(); } } @Override protected void removeSectionsByArea() { try { writeLock.lock(); String tenantDomain = getTenantDomain(); if (sectionsByAreaCache.get(tenantDomain) != null) { sectionsByAreaCache.get(tenantDomain).clear(); sectionsByAreaCache.remove(tenantDomain); } } finally { writeLock.unlock(); } } @Override protected List getSections() { String tenantDomain = getTenantDomain(); List sections = null; try { readLock.lock(); sections = sectionsCache.get(tenantDomain); } finally { readLock.unlock(); } if (sections == null) { reset(); // reset caches - may have been invalidated (e.g. in a cluster) try { readLock.lock(); sections = sectionsCache.get(tenantDomain); } finally { readLock.unlock(); } if (sections == null) { // unexpected throw new AlfrescoRuntimeException("Failed to reset caches (sectionsCache) " + tenantDomain); } } return sections; } @Override protected void putSections(List sections) { try { writeLock.lock(); sectionsCache.put(getTenantDomain(), sections); } finally { writeLock.unlock(); } } @Override protected void removeSections() { try { writeLock.lock(); String tenantDomain = getTenantDomain(); if (sectionsCache.get(tenantDomain) != null) { sectionsCache.get(tenantDomain).clear(); sectionsCache.remove(tenantDomain); } } finally { writeLock.unlock(); } } @Override protected Map getElementReaders() { String tenantDomain = getTenantDomain(); Map elementReaders = null; try { readLock.lock(); elementReaders = elementReadersCache.get(tenantDomain); } finally { readLock.unlock(); } if (elementReaders == null) { reset(); // reset caches - may have been invalidated (e.g. in a cluster) try { readLock.lock(); elementReaders = elementReadersCache.get(tenantDomain); } finally { readLock.unlock(); } if (elementReaders == null) { // unexpected throw new AlfrescoRuntimeException("Failed to reset caches (elementReadersCache) " + tenantDomain); } } return elementReaders; } @Override protected void putElementReaders(Map elementReader) { try { writeLock.lock(); elementReadersCache.put(getTenantDomain(), elementReader); } finally { writeLock.unlock(); } } @Override protected void removeElementReaders() { try { writeLock.lock(); String tenantDomain = getTenantDomain(); if (elementReadersCache.get(tenantDomain) != null) { elementReadersCache.get(tenantDomain).clear(); elementReadersCache.remove(tenantDomain); } } finally { writeLock.unlock(); } } // local helper - returns tenant domain (or empty string if default non-tenant) private String getTenantDomain() { return tenantDeployerService.getCurrentUserDomain(); } }