mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-06-30 18:15:39 +00:00
76994: Merged PLATFORM1 (5.0/Cloud) to HEAD-BUG-FIX (5.0/Cloud) 70127: MNT-9882 - refactoring of caches git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@77833 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
304 lines
11 KiB
Java
304 lines
11 KiB
Java
/*
|
|
* Copyright (C) 2005-2013 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 Lesser 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 Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
package org.alfresco.repo.config;
|
|
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
import org.alfresco.repo.cache.AbstractMTAsynchronouslyRefreshedCache;
|
|
import org.alfresco.repo.config.ConfigDataCache.ConfigData;
|
|
import org.alfresco.repo.config.xml.RepoXMLConfigService;
|
|
import org.apache.commons.logging.Log;
|
|
import org.apache.commons.logging.LogFactory;
|
|
import org.springframework.extensions.config.ConfigDeployment;
|
|
import org.springframework.extensions.config.ConfigImpl;
|
|
import org.springframework.extensions.config.ConfigSection;
|
|
import org.springframework.extensions.config.ConfigService;
|
|
import org.springframework.extensions.config.evaluator.Evaluator;
|
|
import org.springframework.extensions.config.xml.elementreader.ConfigElementReader;
|
|
|
|
import com.sun.star.uno.RuntimeException;
|
|
|
|
/**
|
|
* An innder class that uses the {@link RepoXMLConfigService config service} to asynchronously
|
|
* refresh tenant config data.
|
|
* <p/>
|
|
* Cluster-wide invalidation messages mean that we have to be very careful about only making
|
|
* updates to caches when entries really change. However, the nature of the {@link ConfigService}
|
|
* hierarchy makes it very difficult to do this in a deterministic manner without performing
|
|
* write locks that run across tenants. By receiving asynchronous messages to refresh we no
|
|
* longer have to rely on a cluster-aware cache.
|
|
*
|
|
* @author Derek Hulley
|
|
* @author Andy Hind
|
|
* @since 4.1.5
|
|
*/
|
|
public class ConfigDataCache extends AbstractMTAsynchronouslyRefreshedCache<ConfigData>
|
|
{
|
|
private static Log logger = LogFactory.getLog(ConfigDataCache.class);
|
|
|
|
private RepoXMLConfigService repoXMLConfigService;
|
|
|
|
/**
|
|
* Set the config service. Depending on the order of injection, this might have to
|
|
* be done by code.
|
|
*/
|
|
public void setRepoXMLConfigService(RepoXMLConfigService repoXMLConfigService)
|
|
{
|
|
this.repoXMLConfigService = repoXMLConfigService;
|
|
}
|
|
|
|
@Override
|
|
protected ConfigData buildCache(String tenantId)
|
|
{
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
logger.debug("Received request to rebuild config data for tenant: " + tenantId);
|
|
}
|
|
ConfigData configData = repoXMLConfigService.getRepoConfig(tenantId);
|
|
if (!(configData instanceof ImmutableConfigData))
|
|
{
|
|
throw new RuntimeException("ConfigData must be immutable for the cache.");
|
|
}
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
logger.debug("Rebuilt config data for tenant: " + tenantId + " (" + configData + ").");
|
|
}
|
|
return configData;
|
|
}
|
|
|
|
/**
|
|
* Data containing all a tenant's required UI configuration
|
|
*
|
|
* @author various
|
|
*/
|
|
public static class ConfigData
|
|
{
|
|
private ConfigImpl globalConfig;
|
|
private Map<String, Evaluator> evaluators;
|
|
private Map<String, List<ConfigSection>> sectionsByArea;
|
|
private List<ConfigSection> sections;
|
|
private Map<String, ConfigElementReader> elementReaders;
|
|
|
|
private List<ConfigDeployment> configDeployments;
|
|
|
|
public ConfigData()
|
|
{
|
|
}
|
|
|
|
public ConfigImpl getGlobalConfig()
|
|
{
|
|
return globalConfig;
|
|
}
|
|
public void setGlobalConfig(ConfigImpl globalConfig)
|
|
{
|
|
this.globalConfig = globalConfig;
|
|
}
|
|
public Map<String, Evaluator> getEvaluators()
|
|
{
|
|
return evaluators;
|
|
}
|
|
public void setEvaluators(Map<String, Evaluator> evaluators)
|
|
{
|
|
this.evaluators = evaluators;
|
|
}
|
|
public Map<String, List<ConfigSection>> getSectionsByArea()
|
|
{
|
|
return sectionsByArea;
|
|
}
|
|
public void setSectionsByArea(Map<String, List<ConfigSection>> sectionsByArea)
|
|
{
|
|
this.sectionsByArea = sectionsByArea;
|
|
}
|
|
public List<ConfigSection> getSections()
|
|
{
|
|
return sections;
|
|
}
|
|
public void setSections(List<ConfigSection> sections)
|
|
{
|
|
this.sections = sections;
|
|
}
|
|
public Map<String, ConfigElementReader> getElementReaders()
|
|
{
|
|
return elementReaders;
|
|
}
|
|
public void setElementReaders(Map<String, ConfigElementReader> elementReaders)
|
|
{
|
|
this.elementReaders = elementReaders;
|
|
}
|
|
public List<ConfigDeployment> getConfigDeployments()
|
|
{
|
|
return configDeployments;
|
|
}
|
|
public void setConfigDeployments(List<ConfigDeployment> configDeployments)
|
|
{
|
|
this.configDeployments = configDeployments;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Immutable version of {@link ConfigData} to ensure cast-iron safety of data
|
|
* being put into the caches.
|
|
*
|
|
* @author Derek Hulley
|
|
* @since 4.1.5
|
|
*/
|
|
public static class ImmutableConfigData extends ConfigData
|
|
{
|
|
/**
|
|
* Local variable to allow setter use during construction
|
|
*/
|
|
private boolean locked = false;
|
|
|
|
/**
|
|
* Copy constructor that prevents any data from being changed.
|
|
*
|
|
* @param configData the config to copy
|
|
*/
|
|
public ImmutableConfigData(ConfigData configData)
|
|
{
|
|
/*
|
|
* Each member is copied or protected in some way to ensure immutability
|
|
*/
|
|
|
|
List<ConfigDeployment> configDeployments = configData.getConfigDeployments();
|
|
if (configDeployments != null)
|
|
{
|
|
List<ConfigDeployment> configDeploymentsLocked = Collections.unmodifiableList(configDeployments);
|
|
setConfigDeployments(configDeploymentsLocked);
|
|
}
|
|
else
|
|
{
|
|
setConfigDeployments(Collections.<ConfigDeployment>emptyList());
|
|
}
|
|
|
|
Map<String, ConfigElementReader> elementReaders = configData.getElementReaders();
|
|
if (elementReaders != null)
|
|
{
|
|
Map<String, ConfigElementReader> elementReadersLocked = Collections.unmodifiableMap(elementReaders);
|
|
setElementReaders(elementReadersLocked);
|
|
}
|
|
else
|
|
{
|
|
setElementReaders(Collections.<String, ConfigElementReader>emptyMap());
|
|
}
|
|
|
|
Map<String, Evaluator> evaluators = configData.getEvaluators();
|
|
if (evaluators != null)
|
|
{
|
|
Map<String, Evaluator> evaluatorsLocked = Collections.unmodifiableMap(evaluators);
|
|
setEvaluators(evaluatorsLocked);
|
|
}
|
|
else
|
|
{
|
|
setEvaluators(Collections.<String, Evaluator>emptyMap());
|
|
}
|
|
|
|
ConfigImpl globalConfig = configData.getGlobalConfig();
|
|
ImmutableConfig globalConfigLocked = new ImmutableConfig(globalConfig);
|
|
setGlobalConfig(globalConfigLocked);
|
|
|
|
List<ConfigSection> sections = configData.getSections();
|
|
if (sections != null)
|
|
{
|
|
List<ConfigSection> sectionsLocked = Collections.unmodifiableList(sections);
|
|
setSections(sectionsLocked);
|
|
}
|
|
else
|
|
{
|
|
setSections(Collections.<ConfigSection>emptyList());
|
|
}
|
|
|
|
Map<String, List<ConfigSection>> sectionsByArea = configData.getSectionsByArea();
|
|
if (sectionsByArea != null)
|
|
{
|
|
Map<String, List<ConfigSection>> sectionsByAreaLocked = Collections.unmodifiableMap(sectionsByArea);
|
|
setSectionsByArea(sectionsByAreaLocked);
|
|
}
|
|
else
|
|
{
|
|
setSectionsByArea(Collections.<String, List<ConfigSection>>emptyMap());
|
|
}
|
|
|
|
// Now prevent setters from being used
|
|
locked = true;
|
|
}
|
|
|
|
@Override
|
|
public void setGlobalConfig(ConfigImpl globalConfig)
|
|
{
|
|
if (locked)
|
|
{
|
|
throw new IllegalStateException("ConfigData has been locked.");
|
|
}
|
|
super.setGlobalConfig(globalConfig);
|
|
}
|
|
|
|
@Override
|
|
public void setEvaluators(Map<String, Evaluator> evaluators)
|
|
{
|
|
if (locked)
|
|
{
|
|
throw new IllegalStateException("ConfigData has been locked.");
|
|
}
|
|
super.setEvaluators(evaluators);
|
|
}
|
|
|
|
@Override
|
|
public void setSectionsByArea(Map<String, List<ConfigSection>> sectionsByArea)
|
|
{
|
|
if (locked)
|
|
{
|
|
throw new IllegalStateException("ConfigData has been locked.");
|
|
}
|
|
super.setSectionsByArea(sectionsByArea);
|
|
}
|
|
|
|
@Override
|
|
public void setSections(List<ConfigSection> sections)
|
|
{
|
|
if (locked)
|
|
{
|
|
throw new IllegalStateException("ConfigData has been locked.");
|
|
}
|
|
super.setSections(sections);
|
|
}
|
|
|
|
@Override
|
|
public void setElementReaders(Map<String, ConfigElementReader> elementReaders)
|
|
{
|
|
if (locked)
|
|
{
|
|
throw new IllegalStateException("ConfigData has been locked.");
|
|
}
|
|
super.setElementReaders(elementReaders);
|
|
}
|
|
|
|
@Override
|
|
public void setConfigDeployments(List<ConfigDeployment> configDeployments)
|
|
{
|
|
if (locked)
|
|
{
|
|
throw new IllegalStateException("ConfigData has been locked.");
|
|
}
|
|
super.setConfigDeployments(configDeployments);
|
|
}
|
|
}
|
|
} |