org.modeshape.jcr.LocalEnvironment Maven / Gradle / Ivy
/*
* ModeShape (http://www.modeshape.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.modeshape.jcr;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.infinispan.configuration.cache.Configuration;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.configuration.global.GlobalConfiguration;
import org.infinispan.configuration.global.GlobalConfigurationBuilder;
import org.infinispan.manager.CacheContainer;
import org.infinispan.manager.DefaultCacheManager;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.transaction.LockingMode;
import org.infinispan.transaction.TransactionMode;
import org.infinispan.transaction.lookup.GenericTransactionManagerLookup;
import org.infinispan.transaction.lookup.TransactionManagerLookup;
import org.modeshape.common.logging.Logger;
import org.modeshape.common.util.CheckArg;
import org.modeshape.common.util.DelegatingClassLoader;
import org.modeshape.common.util.StringURLClassLoader;
import org.modeshape.common.util.StringUtil;
/**
* An {@link Environment} that can be used within a local (non-clustered) process.
*
* To use a custom Environment instance, simply create a {@link RepositoryConfiguration} as usual but then call the
* {@link RepositoryConfiguration#with(Environment)} with the Environment instance and then use the resulting
* RepositoryConfiguration instance.
*
*
* When a ModeShape {@link RepositoryConfiguration repository configuration} defines cache containers with configuration files on
* the file system or the classpath, then a {@link LocalEnvironment} instance can be used as-is with no other configuration or
* setup.
*
*
* If applications wish to programmatically configure the Infinispan caches or cache containers, then those configurations can be
* registered with a LocalEnvironment instance. Specifically, the {@link #addCacheContainer(String, CacheContainer)} and
* {@link #addCacheContainerIfAbsent(String, CacheContainer)} methods register a programmatically created instance of a
* {@link CacheContainer}. Alternatively, the {@link #defineCache(String, String, Configuration)} method can be used to register a
* named cache with a programmatically created {@link Configuration Infinispan cache configuration}.
*
*/
public class LocalEnvironment implements Environment {
public static final Class extends TransactionManagerLookup> DEFAULT_TRANSACTION_MANAGER_LOOKUP_CLASS = GenericTransactionManagerLookup.class;
/**
* The name for the default cache container that is used when {@link #getCacheContainer()} is called or if null is supplied as
* the name in {@link #getCacheContainer(String)}.
*/
public static final String DEFAULT_CONFIGURATION_NAME = "defaultCacheContainer";
private final Class extends TransactionManagerLookup> transactionManagerLookupClass;
private final ConcurrentMap containers = new ConcurrentHashMap();
private volatile boolean shared = false;
private final Logger logger = Logger.getLogger(getClass());
public LocalEnvironment() {
this.transactionManagerLookupClass = DEFAULT_TRANSACTION_MANAGER_LOOKUP_CLASS;
}
public LocalEnvironment( Class extends TransactionManagerLookup> transactionManagerLookupClass ) {
if (transactionManagerLookupClass == null) transactionManagerLookupClass = DEFAULT_TRANSACTION_MANAGER_LOOKUP_CLASS;
this.transactionManagerLookupClass = transactionManagerLookupClass;
}
/**
* Get the default cache container.
*
* @return the default cache container; never null
* @throws IOException
* @throws NamingException
*/
public CacheContainer getCacheContainer() throws IOException, NamingException {
return getCacheContainer(null);
}
@Override
public synchronized CacheContainer getCacheContainer( String name ) throws IOException, NamingException {
if (name == null) name = DEFAULT_CONFIGURATION_NAME;
CacheContainer container = containers.get(name);
if (container == null) {
container = createContainer(name);
containers.put(name, container);
}
return container;
}
/**
* Shutdown this environment, allowing it to reclaim any resources.
*
* This method does nothing if the environment has been marked as {@link #isShared() shared}.
*
*/
@Override
public synchronized void shutdown() {
if (!shared) doShutdown();
}
/**
* Shutdown all containers and caches.
*/
protected void doShutdown() {
for (CacheContainer container : containers.values()) {
shutdown(container);
}
containers.clear();
}
@Override
public ClassLoader getClassLoader( ClassLoader fallbackLoader,
String... classpathEntries ) {
List urls = new ArrayList();
if (classpathEntries != null) {
for (String url : classpathEntries) {
if (!StringUtil.isBlank(url)) {
urls.add(url);
}
}
}
Set delegates = new LinkedHashSet<>();
if (!urls.isEmpty()) {
StringURLClassLoader urlClassLoader = new StringURLClassLoader(urls);
// only if any custom urls were parsed add this loader
if (urlClassLoader.getURLs().length > 0) {
delegates.add(urlClassLoader);
}
}
ClassLoader currentLoader = getClass().getClassLoader();
ClassLoader tccl = Thread.currentThread().getContextClassLoader();
//add the TCCL to the list if it's not the same as the current loader or the fallback loader
if (fallbackLoader != null && !fallbackLoader.equals(tccl) ||
fallbackLoader == null && !currentLoader.equals(tccl)) {
delegates.add(tccl);
}
if (fallbackLoader != null && !fallbackLoader.equals(currentLoader)) {
// if the parent of fallback is the same as the current loader, just use that
if (fallbackLoader.getParent().equals(currentLoader)) {
currentLoader = fallbackLoader;
} else {
delegates.add(fallbackLoader);
}
}
return delegates.isEmpty() ? currentLoader : new DelegatingClassLoader(currentLoader, delegates);
}
protected void shutdown( CacheContainer container ) {
container.stop();
}
protected Class extends TransactionManagerLookup> transactionManagerLookupClass() {
return transactionManagerLookupClass;
}
protected TransactionManagerLookup transactionManagerLookupInstance() {
try {
return transactionManagerLookupClass().newInstance();
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
protected CacheContainer createContainer( String configFile ) throws IOException, NamingException {
CacheContainer container = null;
// First try finding the cache configuration ...
if (configFile != null && !configFile.equals(DEFAULT_CONFIGURATION_NAME)) {
configFile = configFile.trim();
try {
logger.debug("Starting cache manager using configuration at '{0}'", configFile);
container = new DefaultCacheManager(configFile);
} catch (FileNotFoundException e) {
// Configuration file was not found, so try JNDI using configFileName as JNDI name...
container = (CacheContainer)jndiContext().lookup(configFile);
}
}
if (container == null) {
// The default Infinispan configuration is in-memory, local and non-clustered.
// But we need a transaction manager, so use the generic TM which is a good default ...
ConfigurationBuilder config = createDefaultConfigurationBuilder();
GlobalConfigurationBuilder global = createGlobalConfigurationBuilder();
container = createContainer(global, config);
}
return container;
}
/**
* Create the default configuration.
*
* @return the default cache configuration.
*/
protected ConfigurationBuilder createDefaultConfigurationBuilder() {
ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
configurationBuilder.transaction().transactionMode(TransactionMode.TRANSACTIONAL);
configurationBuilder.transaction().transactionManagerLookup(transactionManagerLookupInstance());
configurationBuilder.transaction().lockingMode(LockingMode.PESSIMISTIC);
return configurationBuilder;
}
/**
* Create the global configuration.
*
* @return the global configuration.
*/
protected GlobalConfigurationBuilder createGlobalConfigurationBuilder() {
GlobalConfigurationBuilder global = new GlobalConfigurationBuilder();
global.globalJmxStatistics().allowDuplicateDomains(true);
// TODO author=Horia Chiorean date=7/26/12 description=MODE-1524 - Currently we don't use advanced externalizers
// global = global.fluent().serialization().addAdvancedExternalizer(Schematic.externalizers()).build();
return global;
}
/**
* Create a cache container using the supplied configurations.
*
* @param globalConfigurationBuilder the global configuration builder
* @param configurationBuilder the default cache configuration builder
* @return the cache container
*/
protected CacheContainer createContainer( GlobalConfigurationBuilder globalConfigurationBuilder,
ConfigurationBuilder configurationBuilder ) {
GlobalConfiguration globalConfiguration = globalConfigurationBuilder.build();
Configuration configuration = configurationBuilder.build();
logger.debug("Starting cache manager with global configuration \n{0}\nand default configuration:\n{1}",
globalConfiguration,
configuration);
return new DefaultCacheManager(globalConfiguration, configuration);
}
/**
* Create the default configuration.
*
* @return the default cache configuration.
* @deprecated see {@link #createDefaultConfigurationBuilder()}
*/
@Deprecated
protected Configuration createDefaultConfiguration() {
return createDefaultConfigurationBuilder().build();
}
/**
* Create the global configuration.
*
* @return the global configuration.
* @deprecated see {@link #createGlobalConfigurationBuilder()}
*/
@Deprecated
protected GlobalConfiguration createGlobalConfiguration() {
return createGlobalConfigurationBuilder().build();
}
/**
* Create a cache container using the supplied configurations.
*
* @param globalConfiguration the global configuration
* @param configuration the default cache configuration
* @return the cache container
* @deprecated use {@link #createContainer(GlobalConfigurationBuilder, ConfigurationBuilder)} instead
*/
@Deprecated
protected CacheContainer createContainer( GlobalConfiguration globalConfiguration,
Configuration configuration ) {
logger.debug("Starting cache manager with global configuration \n{0}\nand default configuration:\n{1}",
globalConfiguration,
configuration);
return new DefaultCacheManager(globalConfiguration, configuration);
}
protected Context jndiContext() throws NamingException {
return new InitialContext();
}
/**
* Add the supplied {@link CacheContainer} under the supplied name if and only if there is not already a cache container
* registered at that name.
*
* @param name the cache container name; may be null if the {@link #DEFAULT_CONFIGURATION_NAME default configuration name}
* should be used
* @param cacheContainer the cache container; may not be null
*/
public void addCacheContainerIfAbsent( String name,
CacheContainer cacheContainer ) {
CheckArg.isNotNull(cacheContainer, "cacheContainer");
containers.putIfAbsent(name, cacheContainer);
}
/**
* Add the supplied {@link CacheContainer} under the supplied name if and only if there is not already a cache container
* registered at that name.
*
* @param name the cache container name; may be null if the {@link #DEFAULT_CONFIGURATION_NAME default configuration name}
* should be used
* @param cacheContainer the cache container; may not be null
* @return the cache container that was previously registered in this environment by the supplied name, or null if there was
* no such previously-registered cache container
*/
public CacheContainer addCacheContainer( String name,
CacheContainer cacheContainer ) {
CheckArg.isNotNull(cacheContainer, "cacheContainer");
return containers.put(name, cacheContainer);
}
/**
* Define within the default cache container an Infinispan cache with the given cache name and configuration. Note that the
* cache container is created if required, but if it exists it must implement the {@link EmbeddedCacheManager} interface for
* this method to succeed.
*
* @param cacheName the name of the cache being defined; may not be null
* @param configuration the cache configuration; may not be null
* @return the clone of the supplied configuration that is used by the cache container; never null
*/
public Configuration defineCache( String cacheName,
Configuration configuration ) {
CheckArg.isNotNull(cacheName, "cacheName");
CheckArg.isNotNull(configuration, "configuration");
return defineCache(null, cacheName, configuration);
}
/**
* Define within the named cache container an Infinispan cache with the given cache name and configuration. Note that the
* cache container is created if required, but if it exists it must implement the {@link EmbeddedCacheManager} interface for
* this method to succeed.
*
* @param cacheContainerName the name of the cache container; if null, the {@link #DEFAULT_CONFIGURATION_NAME default
* container name} is used
* @param cacheName the name of the cache being defined; may not be null
* @param configuration the cache configuration; may not be null
* @return the clone of the supplied configuration that is used by the cache container; never null
*/
public Configuration defineCache( String cacheContainerName,
String cacheName,
Configuration configuration ) {
CheckArg.isNotNull(cacheName, "cacheName");
CheckArg.isNotNull(configuration, "configuration");
if (cacheContainerName == null) cacheContainerName = DEFAULT_CONFIGURATION_NAME;
CacheContainer container = containers.get(cacheContainerName);
if (container == null) {
Configuration config = createDefaultConfiguration();
GlobalConfiguration global = createGlobalConfiguration();
CacheContainer newContainer = createContainer(global, config);
container = containers.putIfAbsent(cacheContainerName, newContainer);
if (container == null) container = newContainer;
}
return ((EmbeddedCacheManager)container).defineConfiguration(cacheName, configuration);
}
/**
* Set whether this environment is shared amongst multiple repositories. Shared environments are not shutdown automatically,
* and the application is expected to shutdown all containers and caches. By default, environments are not shared unless this
* method is explicitly called with a parameter value of true
.
*
* @param shared true if this environment is shared, or false otherwise
* @see #isShared()
*/
public void setShared( boolean shared ) {
this.shared = shared;
}
/**
* Return whether this environment is shared amongst multiple repositories.
*
* @return true if this environment is shared, or false otherwise
* @see #setShared(boolean)
*/
public boolean isShared() {
return shared;
}
}