/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco 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 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Alfresco. If not, see .
*/
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.springframework.extensions.config.ConfigDeployment;
import org.springframework.extensions.config.ConfigImpl;
import org.springframework.extensions.config.ConfigSection;
import org.springframework.extensions.config.ConfigSource;
import org.springframework.extensions.config.evaluator.Evaluator;
import org.springframework.extensions.config.xml.XMLConfigService;
import org.springframework.extensions.config.xml.elementreader.ConfigElementReader;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.repo.security.authentication.AuthenticationContext;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.tenant.TenantAdminService;
import org.alfresco.repo.tenant.TenantDeployer;
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 AuthenticationContext authenticationContext;
private TenantAdminService tenantAdminService;
// Internal cache (clusterable)
private SimpleCache configDataCache;
// used to reset the cache
private ThreadLocal configDataThreadLocal = new ThreadLocal();
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
public void setAuthenticationContext(AuthenticationContext authenticationContext)
{
this.authenticationContext = authenticationContext;
}
public void setTenantAdminService(TenantAdminService tenantAdminService)
{
this.tenantAdminService = tenantAdminService;
}
public void setConfigDataCache(SimpleCache configDataCache)
{
this.configDataCache = configDataCache;
}
/**
* Constructs an XMLConfigService using the given config source
*
* @param configSource
* A ConfigSource
*/
public RepoXMLConfigService(ConfigSource configSource)
{
super(configSource);
}
public List initConfig()
{
return resetRepoConfig().getConfigDeployments();
}
private ConfigData initRepoConfig(String tenantDomain)
{
ConfigData configData = null;
// can be null e.g. initial login, after fresh bootstrap
String currentUser = authenticationContext.getCurrentUserName();
if (currentUser == null)
{
authenticationContext.setSystemUserAsCurrentUser();
}
UserTransaction userTransaction = transactionService.getUserTransaction();
try
{
userTransaction.begin();
// parse config
List configDeployments = super.initConfig();
configData = getConfigDataLocal(tenantDomain);
if (configData != null)
{
configData.setConfigDeployments(configDeployments);
}
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)
{
authenticationContext.clearCurrentSecurityContext();
}
}
return configData;
}
public void destroy()
{
super.destroy();
logger.info("Config destroyed");
}
/**
* Resets the config service
*/
public void reset()
{
resetRepoConfig();
}
/**
* Resets the config service
*/
private ConfigData resetRepoConfig()
{
if (logger.isDebugEnabled())
{
logger.debug("Resetting repo config service");
}
String tenantDomain = getTenantDomain();
try
{
destroy();
// create threadlocal, if needed
ConfigData configData = getConfigDataLocal(tenantDomain);
if (configData == null)
{
configData = new ConfigData(tenantDomain);
this.configDataThreadLocal.set(configData);
}
configData = initRepoConfig(tenantDomain);
if (configData == null)
{
// unexpected
throw new AlfrescoRuntimeException("Failed to reset configData " + tenantDomain);
}
try
{
writeLock.lock();
configDataCache.put(tenantDomain, configData);
}
finally
{
writeLock.unlock();
}
return configData;
}
finally
{
try
{
readLock.lock();
if (configDataCache.get(tenantDomain) != null)
{
this.configDataThreadLocal.set(null); // it's in the cache, clear the threadlocal
}
}
finally
{
readLock.unlock();
}
}
}
@Override
protected void onBootstrap(ApplicationEvent event)
{
// run as System on bootstrap
AuthenticationUtil.runAs(new RunAsWork