/*
* JBoss, Home of Professional Open Source
* Copyright 2009 Red Hat Inc. and/or its affiliates and other
* contributors as indicated by the @author tags. All rights reserved.
* See the copyright.txt in the distribution for a full listing of
* individual contributors.
*
* This 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 2.1 of
* the License, or (at your option) any later version.
*
* This software 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 this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.infinispan.factories;
import org.infinispan.CacheException;
import org.infinispan.Version;
import org.infinispan.commands.module.ModuleCommandFactory;
import org.infinispan.commands.module.ModuleCommandInitializer;
import org.infinispan.config.GlobalConfiguration;
import org.infinispan.factories.annotations.SurvivesRestarts;
import org.infinispan.factories.components.ComponentMetadataRepo;
import org.infinispan.factories.scopes.Scope;
import org.infinispan.factories.scopes.Scopes;
import org.infinispan.jmx.CacheManagerJmxRegistration;
import org.infinispan.lifecycle.ComponentStatus;
import org.infinispan.lifecycle.ModuleLifecycle;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.manager.EmbeddedCacheManagerStartupException;
import org.infinispan.notifications.cachemanagerlistener.CacheManagerNotifier;
import org.infinispan.notifications.cachemanagerlistener.CacheManagerNotifierImpl;
import org.infinispan.remoting.transport.Transport;
import org.infinispan.util.ModuleProperties;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.infinispan.config.GlobalConfiguration.ShutdownHookBehavior.DEFAULT;
import static org.infinispan.config.GlobalConfiguration.ShutdownHookBehavior.REGISTER;
/**
* A global component registry where shared components are stored.
*
* @author Manik Surtani
* @since 4.0
*/
@Scope(Scopes.GLOBAL)
@SurvivesRestarts
public class GlobalComponentRegistry extends AbstractComponentRegistry {
private static final Log log = LogFactory.getLog(GlobalComponentRegistry.class);
private static boolean versionLogged = false;
/**
* Hook to shut down the cache when the JVM exits.
*/
private Thread shutdownHook;
/**
* A flag that the shutdown hook sets before calling cache.stop(). Allows stop() to identify if it has been called
* from a shutdown hook.
*/
private boolean invokedFromShutdownHook;
private final GlobalConfiguration globalConfiguration;
/**
* Tracking set of created caches in order to make it easy to remove a cache on remote nodes.
*/
private final Set<String> createdCaches;
private final ModuleProperties moduleProperties = new ModuleProperties();
final List<ModuleLifecycle> moduleLifecycles;
final Map<String, ComponentRegistry> namedComponents = new HashMap<String, ComponentRegistry>(4);
/**
* Creates an instance of the component registry. The configuration passed in is automatically registered.
*
* @param configuration configuration with which this is created
*/
public GlobalComponentRegistry(GlobalConfiguration configuration,
EmbeddedCacheManager cacheManager,
Set<String> createdCaches) {
super(configuration.getClassLoader()); // registers the default classloader
moduleLifecycles = moduleProperties.resolveModuleLifecycles(defaultClassLoader);
// Load up the component metadata
ComponentMetadataRepo.initialize(moduleProperties.getModuleMetadataFiles(defaultClassLoader), defaultClassLoader);
try {
// this order is important ...
globalConfiguration = configuration;
registerComponent(this, GlobalComponentRegistry.class);
registerComponent(cacheManager, EmbeddedCacheManager.class);
registerComponent(configuration, GlobalConfiguration.class);
registerComponent(new CacheManagerJmxRegistration(), CacheManagerJmxRegistration.class);
registerComponent(new CacheManagerNotifierImpl(), CacheManagerNotifier.class);
moduleProperties.loadModuleCommandHandlers(configuration.getClassLoader());
Map<Byte, ModuleCommandFactory> factories = moduleProperties.moduleCommandFactories();
if (factories != null && !factories.isEmpty())
registerNonVolatileComponent(factories, KnownComponentNames.MODULE_COMMAND_FACTORIES);
else
registerNonVolatileComponent(Collections.<Object, Object>emptyMap(), KnownComponentNames.MODULE_COMMAND_FACTORIES);
this.createdCaches = createdCaches;
// This is necessary to make sure the transport has been started and is available to other components that
// may need it. This is a messy approach though - a proper fix will be in ISPN-1698
getOrCreateComponent(Transport.class);
} catch (Exception e) {
throw new CacheException("Unable to construct a GlobalComponentRegistry!", e);
}
}
@Override
protected Log getLog() {
return log;
}
@Override
protected void removeShutdownHook() {
// if this is called from a source other than the shutdown hook, de-register the shutdown hook.
if (!invokedFromShutdownHook && shutdownHook != null) Runtime.getRuntime().removeShutdownHook(shutdownHook);
}
@Override
protected void addShutdownHook() {
ArrayList<MBeanServer> al = MBeanServerFactory.findMBeanServer(null);
boolean registerShutdownHook = (globalConfiguration.getShutdownHookBehavior() == DEFAULT && al.isEmpty())
|| globalConfiguration.getShutdownHookBehavior() == REGISTER;
if (registerShutdownHook) {
log.tracef("Registering a shutdown hook. Configured behavior = %s", globalConfiguration.getShutdownHookBehavior());
shutdownHook = new Thread() {
@Override
public void run() {
try {
invokedFromShutdownHook = true;
GlobalComponentRegistry.this.stop();
} finally {
invokedFromShutdownHook = false;
}
}
};
Runtime.getRuntime().addShutdownHook(shutdownHook);
} else {
log.tracef("Not registering a shutdown hook. Configured behavior = %s", globalConfiguration.getShutdownHookBehavior());
}
}
public final ComponentRegistry getNamedComponentRegistry(String name) {
return namedComponents.get(name);
}
public final void registerNamedComponentRegistry(ComponentRegistry componentRegistry, String name) {
namedComponents.put(name, componentRegistry);
}
public final void unregisterNamedComponentRegistry(String name) {
namedComponents.remove(name);
}
public final void rewireNamedRegistries() {
for (ComponentRegistry cr : namedComponents.values())
cr.rewire();
}
public Map<Byte,ModuleCommandInitializer> getModuleCommandInitializers() {
return moduleProperties.moduleCommandInitializers();
}
@Override
public void start() {
try {
boolean needToNotify = state != ComponentStatus.RUNNING && state != ComponentStatus.INITIALIZING;
if (needToNotify) {
for (ModuleLifecycle l : moduleLifecycles) {
l.cacheManagerStarting(this, globalConfiguration);
}
}
super.start();
if (!versionLogged) {
log.version(Version.printVersion());
versionLogged = true;
}
if (needToNotify && state == ComponentStatus.RUNNING) {
for (ModuleLifecycle l : moduleLifecycles) {
l.cacheManagerStarted(this);
}
}
} catch (RuntimeException rte) {
try {
resetVolatileComponents();
rewire();
} catch (Exception e) {
if (log.isDebugEnabled())
log.warn("Unable to reset GlobalComponentRegistry after a failed restart!", e);
else
log.warn("Unable to reset GlobalComponentRegistry after a failed restart due to an exception of type " + e.getClass().getSimpleName() + " with message " + e.getMessage() +". Use DEBUG level logging for full exception details.");
}
throw new EmbeddedCacheManagerStartupException(rte);
}
}
@Override
public void stop() {
boolean needToNotify = state == ComponentStatus.RUNNING || state == ComponentStatus.INITIALIZING;
if (needToNotify) {
for (ModuleLifecycle l : moduleLifecycles) {
l.cacheManagerStopping(this);
}
}
super.stop();
if (state == ComponentStatus.TERMINATED && needToNotify) {
for (ModuleLifecycle l : moduleLifecycles) {
l.cacheManagerStopped(this);
}
}
}
public final GlobalConfiguration getGlobalConfiguration() {
return globalConfiguration;
}
/**
* Removes a cache with the given name, returning true if the cache was removed.
*/
public boolean removeCache(String cacheName) {
return createdCaches.remove(cacheName);
}
public ModuleProperties getModuleProperties() {
return moduleProperties;
}
}