package org.infinispan.manager;
import static org.infinispan.factories.KnownComponentNames.CACHE_DEPENDENCY_GRAPH;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.infinispan.Cache;
import org.infinispan.IllegalLifecycleStateException;
import org.infinispan.Version;
import org.infinispan.commands.RemoveCacheCommand;
import org.infinispan.commons.CacheConfigurationException;
import org.infinispan.commons.CacheException;
import org.infinispan.commons.api.Lifecycle;
import org.infinispan.commons.util.CollectionFactory;
import org.infinispan.commons.util.FileLookupFactory;
import org.infinispan.commons.util.Immutables;
import org.infinispan.configuration.ConfigurationManager;
import org.infinispan.configuration.cache.Configuration;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.configuration.format.PropertyFormatter;
import org.infinispan.configuration.global.GlobalConfiguration;
import org.infinispan.configuration.global.GlobalConfigurationBuilder;
import org.infinispan.configuration.global.TransportConfiguration;
import org.infinispan.configuration.parsing.ConfigurationBuilderHolder;
import org.infinispan.configuration.parsing.ParserRegistry;
import org.infinispan.factories.ComponentRegistry;
import org.infinispan.factories.GlobalComponentRegistry;
import org.infinispan.factories.InternalCacheFactory;
import org.infinispan.factories.KnownComponentNames;
import org.infinispan.factories.annotations.SurvivesRestarts;
import org.infinispan.factories.scopes.Scope;
import org.infinispan.factories.scopes.Scopes;
import org.infinispan.health.Health;
import org.infinispan.health.impl.HealthImpl;
import org.infinispan.health.impl.jmx.HealthJMXExposerImpl;
import org.infinispan.health.jmx.HealthJMXExposer;
import org.infinispan.jmx.CacheJmxRegistration;
import org.infinispan.jmx.CacheManagerJmxRegistration;
import org.infinispan.jmx.annotations.DataType;
import org.infinispan.jmx.annotations.DisplayType;
import org.infinispan.jmx.annotations.MBean;
import org.infinispan.jmx.annotations.ManagedAttribute;
import org.infinispan.jmx.annotations.ManagedOperation;
import org.infinispan.jmx.annotations.Parameter;
import org.infinispan.lifecycle.ComponentStatus;
import org.infinispan.manager.impl.ClusterExecutors;
import org.infinispan.notifications.cachemanagerlistener.CacheManagerNotifier;
import org.infinispan.registry.InternalCacheRegistry;
import org.infinispan.remoting.inboundhandler.DeliverOrder;
import org.infinispan.remoting.responses.Response;
import org.infinispan.remoting.rpc.ResponseMode;
import org.infinispan.remoting.transport.Address;
import org.infinispan.remoting.transport.Transport;
import org.infinispan.remoting.transport.jgroups.JGroupsTransport;
import org.infinispan.security.AuditContext;
import org.infinispan.security.AuthorizationPermission;
import org.infinispan.security.impl.AuthorizationHelper;
import org.infinispan.security.impl.PrincipalRoleMapperContextImpl;
import org.infinispan.security.impl.SecureCacheImpl;
import org.infinispan.stats.CacheContainerStats;
import org.infinispan.stats.impl.CacheContainerStatsImpl;
import org.infinispan.util.ByteString;
import org.infinispan.util.CyclicDependencyException;
import org.infinispan.util.DependencyGraph;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import net.jcip.annotations.GuardedBy;
/**
* A <tt>CacheManager</tt> is the primary mechanism for retrieving a {@link Cache} instance, and is often used as a
* starting point to using the {@link Cache}.
* <p/>
* <tt>CacheManager</tt>s are heavyweight objects, and we foresee no more than one <tt>CacheManager</tt> being used per
* JVM (unless specific configuration requirements require more than one; but either way, this would be a minimal and
* finite number of instances).
* <p/>
* Constructing a <tt>CacheManager</tt> is done via one of its constructors, which optionally take in a {@link
* org.infinispan.configuration.cache.Configuration} or a path or URL to a configuration XML file.
* <p/>
* Lifecycle - <tt>CacheManager</tt>s have a lifecycle (it implements {@link Lifecycle}) and the default constructors
* also call {@link #start()}. Overloaded versions of the constructors are available, that do not start the
* <tt>CacheManager</tt>, although it must be kept in mind that <tt>CacheManager</tt>s need to be started before they
* can be used to create <tt>Cache</tt> instances.
* <p/>
* Once constructed, <tt>CacheManager</tt>s should be made available to any component that requires a <tt>Cache</tt>,
* via JNDI or via some other mechanism such as an IoC container.
* <p/>
* You obtain <tt>Cache</tt> instances from the <tt>CacheManager</tt> by using one of the overloaded
* <tt>getCache()</tt>, methods. Note that with <tt>getCache()</tt>, there is no guarantee that the instance you get is
* brand-new and empty, since caches are named and shared. Because of this, the <tt>CacheManager</tt> also acts as a
* repository of <tt>Cache</tt>s, and is an effective mechanism of looking up or creating <tt>Cache</tt>s on demand.
* <p/>
* When the system shuts down, it should call {@link #stop()} on the <tt>CacheManager</tt>. This will ensure all caches
* within its scope are properly stopped as well.
* <p/>
* Sample usage:
* <pre><code>
* CacheManager manager = CacheManager.getInstance("my-config-file.xml");
* Cache<String, Person> entityCache = manager.getCache("myEntityCache");
* entityCache.put("aPerson", new Person());
*
* ConfigurationBuilder confBuilder = new ConfigurationBuilder();
* confBuilder.clustering().cacheMode(CacheMode.REPL_SYNC);
* manager.defineConfiguration("myReplicatedCache", confBuilder.build());
* Cache<String, String> replicatedCache = manager.getCache("myReplicatedCache");
* </code></pre>
*
* @author Manik Surtani
* @author Galder ZamarreƱo
* @since 4.0
*/
@Scope(Scopes.GLOBAL)
@SurvivesRestarts
@MBean(objectName = DefaultCacheManager.OBJECT_NAME, description = "Component that acts as a manager, factory and container for caches in the system.")
public class DefaultCacheManager implements EmbeddedCacheManager {
public static final String OBJECT_NAME = "CacheManager";
private static final Log log = LogFactory.getLog(DefaultCacheManager.class);
private final ConcurrentMap<String, CacheWrapper> caches = CollectionFactory.makeConcurrentMap();
private final GlobalComponentRegistry globalComponentRegistry;
private final AuthorizationHelper authzHelper;
private final DependencyGraph<String> cacheDependencyGraph = new DependencyGraph<>();
private final CacheContainerStats stats;
private final Health health;
private final ConfigurationManager configurationManager;
private final String defaultCacheName;
@GuardedBy("this")
private boolean stopping;
/**
* Constructs and starts a default instance of the CacheManager, using configuration defaults. See {@link org.infinispan.configuration.cache.Configuration Configuration}
* and {@link org.infinispan.configuration.global.GlobalConfiguration GlobalConfiguration} for details of these defaults.
*/
public DefaultCacheManager() {
this(null, null, true);
}
/**
* Constructs a default instance of the CacheManager, using configuration defaults. See {@link org.infinispan.configuration.cache.Configuration Configuration}
* and {@link org.infinispan.configuration.global.GlobalConfiguration GlobalConfiguration} for details of these defaults.
*
* @param start if true, the cache manager is started
*/
public DefaultCacheManager(boolean start) {
this(null, null, start);
}
/**
* Constructs and starts a new instance of the CacheManager, using the default configuration passed in. See {@link org.infinispan.configuration.cache.Configuration Configuration}
* and {@link org.infinispan.configuration.global.GlobalConfiguration GlobalConfiguration} for details of these defaults.
*
* @param defaultConfiguration configuration to use as a template for all caches created
*/
public DefaultCacheManager(Configuration defaultConfiguration) {
this(null, defaultConfiguration, true);
}
/**
* Constructs a new instance of the CacheManager, using the default configuration passed in. See
* {@link org.infinispan.configuration.global.GlobalConfiguration GlobalConfiguration} for details of these defaults.
*
* @param defaultConfiguration configuration file to use as a template for all caches created
* @param start if true, the cache manager is started
*/
public DefaultCacheManager(Configuration defaultConfiguration, boolean start) {
this(null, defaultConfiguration, start);
}
/**
* Constructs and starts a new instance of the CacheManager, using the global configuration passed in, and system
* defaults for the default named cache configuration. See {@link org.infinispan.configuration.cache.Configuration Configuration}
* for details of these defaults.
*
* @param globalConfiguration GlobalConfiguration to use for all caches created
*/
public DefaultCacheManager(GlobalConfiguration globalConfiguration) {
this(globalConfiguration, null, true);
}
/**
* Constructs a new instance of the CacheManager, using the global configuration passed in, and system defaults for
* the default named cache configuration. See {@link org.infinispan.configuration.cache.Configuration Configuration}
* for details of these defaults.
*
* @param globalConfiguration GlobalConfiguration to use for all caches created
* @param start if true, the cache manager is started.
*/
public DefaultCacheManager(GlobalConfiguration globalConfiguration, boolean start) {
this(globalConfiguration, null, start);
}
/**
* Constructs and starts a new instance of the CacheManager, using the global and default configurations passed in.
* If either of these are null, system defaults are used.
*
* @param globalConfiguration global configuration to use. If null, a default instance is created.
* @param defaultConfiguration default configuration to use. If null, a default instance is created.
*/
public DefaultCacheManager(GlobalConfiguration globalConfiguration, Configuration defaultConfiguration) {
this(globalConfiguration, defaultConfiguration, true);
}
/**
* Constructs a new instance of the CacheManager, using the global and default configurations passed in. If either of
* these are null, system defaults are used.
*
* @param globalConfiguration global configuration to use. If null, a default instance is created.
* @param defaultConfiguration default configuration to use. If null, a default instance is created.
* @param start if true, the cache manager is started
*/
public DefaultCacheManager(GlobalConfiguration globalConfiguration, Configuration defaultConfiguration,
boolean start) {
globalConfiguration = globalConfiguration == null ? new GlobalConfigurationBuilder().build() : globalConfiguration;
this.configurationManager = new ConfigurationManager(globalConfiguration);
if (defaultConfiguration != null) {
if (globalConfiguration.defaultCacheName().isPresent()) {
defaultCacheName = globalConfiguration.defaultCacheName().get();
} else {
log.defaultCacheConfigurationWithoutName();
defaultCacheName = DEFAULT_CACHE_NAME;
}
configurationManager.putConfiguration(defaultCacheName, defaultConfiguration);
} else {
if (globalConfiguration.defaultCacheName().isPresent()) {
throw log.missingDefaultCacheDeclaration(globalConfiguration.defaultCacheName().get());
} else {
defaultCacheName = null;
}
}
this.authzHelper = new AuthorizationHelper(globalConfiguration.security(), AuditContext.CACHEMANAGER, globalConfiguration.globalJmxStatistics().cacheManagerName());
this.globalComponentRegistry = new GlobalComponentRegistry(globalConfiguration, this, caches.keySet());
this.globalComponentRegistry.registerComponent(configurationManager, ConfigurationManager.class);
this.globalComponentRegistry.registerComponent(cacheDependencyGraph, CACHE_DEPENDENCY_GRAPH, false);
this.globalComponentRegistry.registerComponent(authzHelper, AuthorizationHelper.class);
this.stats = new CacheContainerStatsImpl(this);
health = new HealthImpl(this);
globalComponentRegistry.registerComponent(new HealthJMXExposerImpl(health), HealthJMXExposer.class);
if (start)
start();
}
/**
* Constructs and starts a new instance of the CacheManager, using the configuration file name passed in. This
* constructor first searches for the named file on the classpath, and failing that, treats the file name as an
* absolute path.
*
* @param configurationFile name of configuration file to use as a template for all caches created
*
* @throws java.io.IOException if there is a problem with the configuration file.
*/
public DefaultCacheManager(String configurationFile) throws IOException {
this(configurationFile, true);
}
/**
* Constructs a new instance of the CacheManager, using the configuration file name passed in. This constructor first
* searches for the named file on the classpath, and failing that, treats the file name as an absolute path.
*
* @param configurationFile name of configuration file to use as a template for all caches created
* @param start if true, the cache manager is started
*
* @throws java.io.IOException if there is a problem with the configuration file.
*/
public DefaultCacheManager(String configurationFile, boolean start) throws IOException {
this(FileLookupFactory.newInstance().lookupFileStrict(configurationFile, Thread.currentThread().getContextClassLoader()), start);
}
/**
* Constructs and starts a new instance of the CacheManager, using the input stream passed in to read configuration
* file contents.
*
* @param configurationStream stream containing configuration file contents, to use as a template for all caches
* created
*
* @throws java.io.IOException if there is a problem with the configuration stream.
*/
public DefaultCacheManager(InputStream configurationStream) throws IOException {
this(configurationStream, true);
}
/**
* Constructs a new instance of the CacheManager, using the input stream passed in to read configuration file
* contents.
*
* @param configurationStream stream containing configuration file contents, to use as a template for all caches
* created
* @param start if true, the cache manager is started
*
* @throws java.io.IOException if there is a problem reading the configuration stream
*/
public DefaultCacheManager(InputStream configurationStream, boolean start) throws IOException {
this(new ParserRegistry().parse(configurationStream), start);
}
/**
* Constructs a new instance of the CacheManager, using the holder passed in to read configuration settings.
*
* @param holder holder containing configuration settings, to use as a template for all caches
* created
* @param start if true, the cache manager is started
*/
public DefaultCacheManager(ConfigurationBuilderHolder holder, boolean start) {
try {
configurationManager = new ConfigurationManager(holder);
GlobalConfiguration globalConfiguration = configurationManager.getGlobalConfiguration();
defaultCacheName = globalConfiguration.defaultCacheName().orElse(null);
globalComponentRegistry = new GlobalComponentRegistry(globalConfiguration, this, caches.keySet());
globalComponentRegistry.registerComponent(configurationManager, ConfigurationManager.class);
globalComponentRegistry.registerComponent(cacheDependencyGraph, CACHE_DEPENDENCY_GRAPH, false);
authzHelper = new AuthorizationHelper(globalConfiguration.security(), AuditContext.CACHEMANAGER, globalConfiguration.globalJmxStatistics().cacheManagerName());
stats = new CacheContainerStatsImpl(this);
health = new HealthImpl(this);
globalComponentRegistry.registerComponent(new HealthJMXExposerImpl(health), HealthJMXExposer.class);
} catch (CacheConfigurationException ce) {
throw ce;
} catch (RuntimeException re) {
throw new CacheConfigurationException(re);
}
if (start)
start();
}
@Override
public Configuration defineConfiguration(String name, Configuration configuration) {
return doDefineConfiguration(name, configuration);
}
@Override
public Configuration defineConfiguration(String name, String template, Configuration configurationOverride) {
if (template != null) {
Configuration c = configurationManager.getConfiguration(template);
if (c == null) {
throw log.undeclaredConfiguration(template, name);
} else {
return doDefineConfiguration(name, c, configurationOverride);
}
}
return doDefineConfiguration(name, configurationOverride);
}
private Configuration doDefineConfiguration(String name, Configuration... configurations) {
authzHelper.checkPermission(AuthorizationPermission.ADMIN);
assertIsNotTerminated();
if (name == null || configurations == null)
throw new NullPointerException("Null arguments not allowed");
if (name.equals(DEFAULT_CACHE_NAME))
throw log.illegalCacheName(DEFAULT_CACHE_NAME);
Configuration existing = configurationManager.getConfiguration(name);
if (existing != null) {
throw log.configAlreadyDefined(name);
}
ConfigurationBuilder builder = new ConfigurationBuilder();
boolean template = true;
for (Configuration configuration : configurations) {
if (configuration != null) {
builder.read(configuration);
template = template && configuration.isTemplate();
} else {
throw new NullPointerException("Null arguments not allowed");
}
}
builder.template(template);
return configurationManager.putConfiguration(name, builder);
}
@Override
public void undefineConfiguration(String configurationName) {
authzHelper.checkPermission(AuthorizationPermission.ADMIN);
if (configurationName.equals(DEFAULT_CACHE_NAME))
throw log.illegalCacheName(DEFAULT_CACHE_NAME);
Configuration existing = configurationManager.getConfiguration(configurationName);
if (existing != null) {
for(CacheWrapper cache : caches.values()) {
if (cache.getCache().getCacheConfiguration() == existing && cache.getCache().getStatus() != ComponentStatus.TERMINATED) {
throw log.configurationInUse(configurationName);
}
}
configurationManager.removeConfiguration(configurationName);
}
}
/**
* Retrieves the default cache associated with this cache manager. Note that the default cache does not need to be
* explicitly created with {@link #createCache(String, String)} (String)} since it is automatically created lazily when first used.
* <p/>
* As such, this method is always guaranteed to return the default cache.
*
* @return the default cache.
*/
@Override
public <K, V> Cache<K, V> getCache() {
if (defaultCacheName == null) {
throw log.noDefaultCache();
}
return internalGetCache(defaultCacheName, defaultCacheName);
}
/**
* Retrieves a named cache from the system. If the cache has been previously created with the same name, the running
* cache instance is returned. Otherwise, this method attempts to create the cache first.
* <p/>
* When creating a new cache, this method will use the configuration passed in to the CacheManager on construction,
* as a template, and then optionally apply any overrides previously defined for the named cache using the {@link
* #defineConfiguration(String, Configuration)} or {@link #defineConfiguration(String, String, Configuration)}
* methods, or declared in the configuration file.
*
* @param cacheName name of cache to retrieve
*
* @return a cache instance identified by cacheName
*/
@Override
public <K, V> Cache<K, V> getCache(String cacheName) {
return getCache(cacheName, cacheName);
}
@Override
public <K, V> Cache<K, V> getCache(String cacheName, String configurationName) {
if (cacheName == null)
throw new NullPointerException("Null arguments not allowed");
if (DEFAULT_CACHE_NAME.equals(cacheName)) {
if (defaultCacheName == null) {
throw log.noDefaultCache();
}
cacheName = defaultCacheName;
log.deprecatedDefaultCache();
}
return internalGetCache(cacheName, configurationName);
}
public <K, V> Cache<K, V> internalGetCache(String cacheName, String configurationName) {
assertIsNotTerminated();
CacheWrapper cw = caches.get(cacheName);
if (cw != null) {
return cw.getCache();
}
return createCache(cacheName, configurationName);
}
@Override
public boolean cacheExists(String cacheName) {
return caches.containsKey(cacheName);
}
@Override
public <K, V> Cache<K, V> getCache(String cacheName, boolean createIfAbsent) {
return getCache(cacheName, cacheName, createIfAbsent);
}
@Override
public <K, V> Cache<K, V> getCache(String cacheName, String configurationTemplate, boolean createIfAbsent) {
boolean cacheExists = cacheExists(cacheName);
if (!cacheExists && !createIfAbsent)
return null;
else
return getCache(cacheName, configurationTemplate);
}
@Override
public EmbeddedCacheManager startCaches(final String... cacheNames) {
authzHelper.checkPermission(AuthorizationPermission.LIFECYCLE);
Map<String, Thread> threads = new HashMap<>(cacheNames.length);
final AtomicReference<RuntimeException> exception = new AtomicReference<RuntimeException>(null);
for (final String cacheName : cacheNames) {
if (!threads.containsKey(cacheName)) {
String threadName = "CacheStartThread," + configurationManager.getGlobalConfiguration().transport().nodeName() + "," + cacheName;
Thread thread = new Thread(threadName) {
@Override
public void run() {
try {
createCache(cacheName, cacheName);
} catch (RuntimeException e) {
exception.set(e);
} catch (Throwable t) {
exception.set(new RuntimeException(t));
}
}
};
thread.start();
threads.put(cacheName, thread);
}
}
try {
for (Thread thread : threads.values()) {
thread.join();
}
} catch (InterruptedException e) {
throw new CacheException("Interrupted while waiting for the caches to start");
}
RuntimeException runtimeException = exception.get();
if (runtimeException != null) {
throw runtimeException;
}
return this;
}
@Override
public void removeCache(String cacheName) {
authzHelper.checkPermission(AuthorizationPermission.ADMIN);
ComponentRegistry cacheComponentRegistry = globalComponentRegistry.getNamedComponentRegistry(cacheName);
if (cacheComponentRegistry != null) {
RemoveCacheCommand cmd = new RemoveCacheCommand(ByteString.fromString(cacheName), this);
Transport transport = getTransport();
try {
CompletableFuture<?> future;
if (transport != null) {
Configuration c = configurationManager.getConfiguration(cacheName, defaultCacheName);
// Use sync replication timeout
CompletableFuture<Map<Address, Response>> remoteFuture =
transport.invokeRemotelyAsync(null, cmd, ResponseMode.SYNCHRONOUS_IGNORE_LEAVERS,
c.clustering().remoteTimeout(), null, DeliverOrder.NONE, false);
future = cmd.invokeAsync().thenCompose(o -> remoteFuture);
} else {
future = cmd.invokeAsync();
}
future.get();
} catch (Throwable t) {
throw new CacheException("Error removing cache", t);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public List<Address> getMembers() {
Transport t = getTransport();
return t == null ? null : t.getMembers();
}
/**
* {@inheritDoc}
*/
@Override
public Address getAddress() {
Transport t = getTransport();
return t == null ? null : t.getAddress();
}
/**
* {@inheritDoc}
*/
@Override
public Address getCoordinator() {
Transport t = getTransport();
return t == null ? null : t.getCoordinator();
}
@ManagedAttribute(description = "The logical address of the cluster's coordinator", displayName = "Coordinator address", displayType = DisplayType.SUMMARY)
public String getCoordinatorAddress() {
Transport t = getTransport();
return t == null ? "N/A" : t.getCoordinator().toString();
}
/**
* {@inheritDoc}
*/
@Override
@ManagedAttribute(description = "Indicates whether this node is coordinator", displayName = "Is coordinator?", displayType = DisplayType.SUMMARY)
public boolean isCoordinator() {
Transport t = getTransport();
return t != null && t.isCoordinator();
}
private <K, V> Cache<K, V> createCache(String cacheName, String configurationName) {
final boolean trace = log.isTraceEnabled();
LogFactory.pushNDC(cacheName, trace);
try {
Cache<K, V> cache = wireAndStartCache(cacheName, configurationName);
// a null return value means the cache was created by someone else before we got the lock
if (cache == null)
return caches.get(cacheName).getCache();
return cache;
} finally {
LogFactory.popNDC(trace);
}
}
/**
* @return a null return value means the cache was created by someone else before we got the lock
*/
private <K, V> Cache<K, V> wireAndStartCache(String cacheName, String configurationName) {
CacheWrapper createdCacheWrapper = null;
Configuration c;
boolean needToNotifyCacheStarted = false;
try {
synchronized (caches) {
//fetch it again with the lock held
CacheWrapper existingCacheWrapper = caches.get(cacheName);
if (existingCacheWrapper != null) {
return null; //signal that the cache was created by someone else
}
boolean sameCache = cacheName.equals(configurationName);
c = configurationManager.getConfiguration(configurationName, defaultCacheName);
if (c == null) {
throw log.noSuchCacheConfiguration(configurationName);
} else if (!sameCache) {
Configuration definedConfig = configurationManager.getConfiguration(cacheName);
if (definedConfig != null) {
log.warnAttemptToOverrideExistingConfiguration(cacheName);
c = definedConfig;
}
}
if (c.security().authorization().enabled()) {
// Don't even attempt to wire anything if we don't have LIFECYCLE privileges
authzHelper.checkPermission(c.security().authorization(), AuthorizationPermission.LIFECYCLE);
}
if (c.isTemplate() && sameCache) {
throw log.templateConfigurationStartAttempt(cacheName);
}
createdCacheWrapper = new CacheWrapper();
if (caches.put(cacheName, createdCacheWrapper) != null) {
throw new IllegalStateException("attempt to initialize the cache twice");
}
}
log.tracef("About to wire and start cache %s", cacheName);
Cache<K, V> cache = new InternalCacheFactory<K, V>().createCache(c, globalComponentRegistry, cacheName);
ComponentRegistry cr = cache.getAdvancedCache().getComponentRegistry();
if(cache.getAdvancedCache().getAuthorizationManager() != null) {
cache = new SecureCacheImpl<K, V>(cache.getAdvancedCache());
}
createdCacheWrapper.setCache(cache);
boolean notStartedYet = cr.getStatus() != ComponentStatus.RUNNING && cr.getStatus() != ComponentStatus.INITIALIZING;
// start the cache-level components
cache.start();
needToNotifyCacheStarted = notStartedYet && cr.getStatus() == ComponentStatus.RUNNING;
return cache;
} finally {
// allow other threads to access the cache
if (createdCacheWrapper != null) {
log.tracef("Closing latch for cache %s", cacheName);
createdCacheWrapper.latch.countDown();
if (needToNotifyCacheStarted) {
globalComponentRegistry.notifyCacheStarted(cacheName);
}
}
}
}
@Override
public void start() {
authzHelper.checkPermission(AuthorizationPermission.LIFECYCLE);
final GlobalConfiguration globalConfiguration = configurationManager.getGlobalConfiguration();
if (globalConfiguration.security().authorization().enabled() && System.getSecurityManager() == null) {
log.authorizationEnabledWithoutSecurityManager();
}
globalComponentRegistry.getComponent(CacheManagerJmxRegistration.class).start();
String clusterName = globalConfiguration.transport().clusterName();
String nodeName = globalConfiguration.transport().nodeName();
if (globalConfiguration.security().authorization().enabled()) {
globalConfiguration.security().authorization().principalRoleMapper().setContext(new PrincipalRoleMapperContextImpl(this));
}
globalComponentRegistry.start();
log.debugf("Started cache manager %s on %s", clusterName, nodeName);
}
private void terminate(String cacheName) {
CacheWrapper cacheWrapper = this.caches.get(cacheName);
if (cacheWrapper != null) {
Cache<?, ?> cache = cacheWrapper.cache;
if (cache == null) {
log.tracef("Ignoring cache %s, which hasn't properly started yet!", cacheName);
return;
}
unregisterCacheMBean(cache);
if (cache.getStatus().isTerminated()) {
log.tracef("Ignoring cache %s, it is already terminated.", cacheName);
return;
}
cache.stop();
}
}
@Override
public void stop() {
authzHelper.checkPermission(AuthorizationPermission.LIFECYCLE);
synchronized (this) {
if (stopping) {
log.trace("Ignore call to stop as the cache manager is stopping");
return;
}
log.debugf("Stopping cache manager %s on %s", configurationManager.getGlobalConfiguration().transport().clusterName(), getAddress());
stopping = true;
stopCaches();
globalComponentRegistry.getComponent(CacheManagerJmxRegistration.class).stop();
globalComponentRegistry.stop();
}
}
private void stopCaches() {
Set<String> cachesToStop = new LinkedHashSet<>(this.caches.size());
// stop ordered caches first
try {
List<String> ordered = cacheDependencyGraph.topologicalSort();
cachesToStop.addAll(ordered);
} catch (CyclicDependencyException e) {
log.stopOrderIgnored();
}
// The caches map includes the default cache
cachesToStop.addAll(caches.keySet());
log.tracef("Cache stop order: %s", cachesToStop);
for (String cacheName : cachesToStop) {
try {
terminate(cacheName);
} catch (Throwable t) {
log.componentFailedToStop(t);
}
}
}
private void unregisterCacheMBean(Cache<?, ?> cache) {
// Unregister cache mbean regardless of jmx statistics setting
cache.getAdvancedCache().getComponentRegistry().getComponent(CacheJmxRegistration.class)
.unregisterCacheMBean();
}
@Override
public void addListener(Object listener) {
authzHelper.checkPermission(AuthorizationPermission.LISTEN);
CacheManagerNotifier notifier = globalComponentRegistry.getComponent(CacheManagerNotifier.class);
notifier.addListener(listener);
}
@Override
public void removeListener(Object listener) {
authzHelper.checkPermission(AuthorizationPermission.LISTEN);
CacheManagerNotifier notifier = globalComponentRegistry.getComponent(CacheManagerNotifier.class);
notifier.removeListener(listener);
}
@Override
public Set<Object> getListeners() {
authzHelper.checkPermission(AuthorizationPermission.LISTEN);
CacheManagerNotifier notifier = globalComponentRegistry.getComponent(CacheManagerNotifier.class);
return notifier.getListeners();
}
@Override
public ComponentStatus getStatus() {
authzHelper.checkPermission(AuthorizationPermission.LIFECYCLE);
return globalComponentRegistry.getStatus();
}
@Override
public GlobalConfiguration getCacheManagerConfiguration() {
return configurationManager.getGlobalConfiguration();
}
@Override
public org.infinispan.configuration.cache.Configuration getDefaultCacheConfiguration() {
if (defaultCacheName != null) {
return configurationManager.getConfiguration(defaultCacheName);
} else {
return null;
}
}
@Override
public Configuration getCacheConfiguration(String name) {
Configuration configuration = configurationManager.getConfiguration(name);
if (configuration == null && cacheExists(name)) {
return getDefaultCacheConfiguration();
}
return configuration;
}
@Override
public Set<String> getCacheNames() {
// Get the XML/programmatically defined caches
Set<String> names = new HashSet<>(configurationManager.getDefinedCaches());
// Add the caches created dynamically without explicit config
// Since caches could be modified dynamically, make a safe copy of keys
names.addAll(Immutables.immutableSetConvert(caches.keySet()));
names.remove(DEFAULT_CACHE_NAME);
InternalCacheRegistry internalCacheRegistry = globalComponentRegistry.getComponent(InternalCacheRegistry.class);
internalCacheRegistry.filterPrivateCaches(names);
if (names.isEmpty())
return Collections.emptySet();
else
return Immutables.immutableSetWrap(names);
}
@Override
public Set<String> getCacheConfigurationNames() {
// Get the XML/programmatically defined caches
Set<String> names = new HashSet<>(configurationManager.getDefinedConfigurations());
names.remove(DEFAULT_CACHE_NAME);
InternalCacheRegistry internalCacheRegistry = globalComponentRegistry.getComponent(InternalCacheRegistry.class);
internalCacheRegistry.filterPrivateCaches(names);
if (names.isEmpty())
return Collections.emptySet();
else
return Immutables.immutableSetWrap(names);
}
@Override
public boolean isRunning(String cacheName) {
CacheWrapper w = caches.get(cacheName);
try {
return w != null && w.latch.await(0, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
return false;
}
}
@Override
public boolean isDefaultRunning() {
return isRunning(DEFAULT_CACHE_NAME);
}
@ManagedAttribute(description = "The status of the cache manager instance.", displayName = "Cache manager status", dataType = DataType.TRAIT, displayType = DisplayType.SUMMARY)
public String getCacheManagerStatus() {
return getStatus().toString();
}
@ManagedAttribute(description = "The defined cache names and their statuses. The default cache is not included in this representation.", displayName = "List of defined caches", dataType = DataType.TRAIT, displayType = DisplayType.SUMMARY)
public String getDefinedCacheNames() {
StringBuilder result = new StringBuilder("[");
for (String cacheName : getCacheNames()) {
boolean started = caches.containsKey(cacheName);
result.append(cacheName).append(started ? "(created)" : "(not created)");
}
result.append("]");
return result.toString();
}
@ManagedAttribute(description = "The defined cache configuration names.", displayName = "List of defined cache configurations", dataType = DataType.TRAIT, displayType = DisplayType.SUMMARY)
public String getDefinedCacheConfigurationNames() {
StringBuilder result = new StringBuilder("[");
boolean comma = false;
for (String cacheName : getCacheConfigurationNames()) {
if (comma)
result.append(",");
else
comma = true;
result.append(cacheName);
}
result.append("]");
return result.toString();
}
@ManagedAttribute(description = "The total number of defined cache configurations.", displayName = "Number of caches defined", displayType = DisplayType.SUMMARY)
public String getDefinedCacheCount() {
return String.valueOf(configurationManager.getDefinedCaches().size());
}
@ManagedAttribute(description = "The total number of created caches, including the default cache.", displayName = "Number of caches created", displayType = DisplayType.SUMMARY)
public String getCreatedCacheCount() {
return String.valueOf(this.caches.keySet().size());
}
@ManagedAttribute(description = "The total number of running caches, including the default cache.", displayName = "Number of running caches", displayType = DisplayType.SUMMARY)
public String getRunningCacheCount() {
int running = 0;
for (CacheWrapper cachew : caches.values()) {
Cache<?, ?> cache = cachew.cache;
if (cache != null && cache.getStatus() == ComponentStatus.RUNNING)
running++;
}
return String.valueOf(running);
}
@ManagedAttribute(description = "Returns the version of Infinispan", displayName = "Infinispan version", displayType = DisplayType.SUMMARY, dataType = DataType.TRAIT)
public String getVersion() {
return Version.getVersion();
}
@ManagedAttribute(description = "The name of this cache manager", displayName = "Cache manager name", displayType = DisplayType.SUMMARY, dataType = DataType.TRAIT)
public String getName() {
return configurationManager.getGlobalConfiguration().globalJmxStatistics().cacheManagerName();
}
@ManagedOperation(description = "Starts the default cache associated with this cache manager", displayName = "Starts the default cache")
public void startCache() {
getCache();
}
@ManagedOperation(description = "Starts a named cache from this cache manager", name = "startCache", displayName = "Starts a cache with the given name")
public void startCache(@Parameter(name = "cacheName", description = "Name of cache to start") String cacheName) {
getCache(cacheName);
}
@ManagedAttribute(description = "The network address associated with this instance", displayName = "Network address", dataType = DataType.TRAIT, displayType = DisplayType.SUMMARY)
public String getNodeAddress() {
return getLogicalAddressString();
}
@ManagedAttribute(description = "The physical network addresses associated with this instance", displayName = "Physical network addresses", dataType = DataType.TRAIT, displayType = DisplayType.SUMMARY)
public String getPhysicalAddresses() {
Transport t = getTransport();
if (t == null) return "local";
List<Address> address = t.getPhysicalAddresses();
return address == null ? "local" : address.toString();
}
@ManagedAttribute(description = "List of members in the cluster", displayName = "Cluster members", dataType = DataType.TRAIT, displayType = DisplayType.SUMMARY)
public String getClusterMembers() {
Transport t = getTransport();
if (t == null) return "local";
List<Address> addressList = t.getMembers();
return addressList.toString();
}
@ManagedAttribute(description = "Size of the cluster in number of nodes", displayName = "Cluster size", displayType = DisplayType.SUMMARY)
public int getClusterSize() {
Transport t = getTransport();
if (t == null) return 1;
return t.getMembers().size();
}
/**
* {@inheritDoc}
*/
@ManagedAttribute(description = "Cluster name", displayName = "Cluster name", dataType = DataType.TRAIT, displayType = DisplayType.SUMMARY)
@Override
public String getClusterName() {
return configurationManager.getGlobalConfiguration().transport().clusterName();
}
private String getLogicalAddressString() {
return getAddress() == null ? "local" : getAddress().toString();
}
private void assertIsNotTerminated() {
if (globalComponentRegistry.getStatus().isTerminated())
throw new IllegalLifecycleStateException(
"Cache container has been stopped and cannot be reused. Recreate the cache container.");
}
@Override
public Transport getTransport() {
if (globalComponentRegistry == null) return null;
return globalComponentRegistry.getComponent(Transport.class);
}
@Override
public GlobalComponentRegistry getGlobalComponentRegistry() {
return globalComponentRegistry;
}
@Override
public void addCacheDependency(String from, String to) {
cacheDependencyGraph.addDependency(from, to);
}
@Override
public String toString() {
return super.toString() + "@Address:" + getAddress();
}
private final static class CacheWrapper {
private volatile Cache<?, ?> cache;
private final CountDownLatch latch = new CountDownLatch(1);
public void setCache(Cache<?, ?> cache) {
this.cache = cache;
}
@SuppressWarnings("unchecked")
private <K, V> Cache<K, V> getCache() {
try {
latch.await();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
return (Cache<K, V>) cache;
}
}
/**
* {@inheritDoc}
*/
@ManagedAttribute(description = "Global configuration properties", displayName = "Global configuration properties", dataType = DataType.TRAIT, displayType = DisplayType.SUMMARY)
public Properties getGlobalConfigurationAsProperties() {
return new PropertyFormatter().format(configurationManager.getGlobalConfiguration());
}
@Override
public CacheContainerStats getStats() {
return stats;
}
@Override
public Health getHealth() {
return health;
}
@Override
public ClusterExecutor executor() {
if (globalComponentRegistry.getStatus() != ComponentStatus.RUNNING) {
throw new IllegalStateException("CacheManager must be started before retrieving a ClusterExecutor!");
}
JGroupsTransport transport = (JGroupsTransport) globalComponentRegistry.getComponent(Transport.class);
if (transport != null) {
long time = getCacheManagerConfiguration().transport().distributedSyncTimeout();
return ClusterExecutors.allSubmissionExecutor(null, this, transport, time, TimeUnit.MILLISECONDS,
globalComponentRegistry.getComponent(ExecutorService.class, KnownComponentNames.REMOTE_COMMAND_EXECUTOR),
globalComponentRegistry.getComponent(ScheduledExecutorService.class, KnownComponentNames.TIMEOUT_SCHEDULE_EXECUTOR));
} else {
return ClusterExecutors.allSubmissionExecutor(null, this, null,
TransportConfiguration.DISTRIBUTED_SYNC_TIMEOUT.getDefaultValue(), TimeUnit.MILLISECONDS, ForkJoinPool.commonPool(),
globalComponentRegistry.getComponent(ScheduledExecutorService.class, KnownComponentNames.TIMEOUT_SCHEDULE_EXECUTOR));
}
}
}