/*
* JBoss, Home of Professional Open Source.
* Copyright 2010, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file 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.jboss.ha.ispn;
import java.util.Locale;
import java.util.Properties;
import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.infinispan.config.Configuration;
import org.infinispan.config.GlobalConfiguration;
import org.infinispan.config.GlobalConfiguration.ShutdownHookBehavior;
import org.infinispan.jmx.CacheJmxRegistration;
import org.infinispan.jmx.ComponentsJmxRegistration;
import org.infinispan.jmx.MBeanServerLookup;
import org.infinispan.manager.CacheContainer;
import org.infinispan.manager.DefaultCacheManager;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.notifications.Listener;
import org.infinispan.notifications.cachemanagerlistener.annotation.CacheStarted;
import org.infinispan.notifications.cachemanagerlistener.annotation.CacheStopped;
import org.infinispan.notifications.cachemanagerlistener.event.CacheStartedEvent;
import org.infinispan.notifications.cachemanagerlistener.event.CacheStoppedEvent;
import org.infinispan.remoting.transport.jgroups.JGroupsChannelLookup;
import org.infinispan.remoting.transport.jgroups.JGroupsTransport;
import org.jboss.logging.Logger;
import org.jgroups.Channel;
import org.jgroups.ChannelFactory;
/**
* An Infinispan cache container factory that injects its channel from a channel factory.
*
* @author Paul Ferraro
*/
@SuppressWarnings("deprecation")
@Listener
public class DefaultCacheContainerFactory implements CacheContainerFactory, MBeanServerLookup
{
public static final String CHANNEL_ID = "channelId";
public static final String CHANNEL_FACTORY = "channelFactory";
private static Logger logger = Logger.getLogger(DefaultCacheContainerFactory.class);
private final ChannelFactory channelFactory;
private final MBeanServer server;
/**
* Creates a new cache container factory using the specified channel factory.
* @param channelFactory a channel factory
*/
public DefaultCacheContainerFactory(ChannelFactory channelFactory, MBeanServer server)
{
this.channelFactory = channelFactory;
this.server = server;
}
/**
* {@inheritDoc}
* @see org.jboss.ha.ispn.CacheContainerFactory#createCacheContainer(org.jboss.ha.ispn.CacheContainerConfiguration)
*/
@Override
public EmbeddedCacheManager createCacheContainer(CacheContainerConfiguration configuration)
{
GlobalConfiguration global = configuration.getGlobalConfiguration();
// Don't register shutdown hook - caches/containers will stop when registry stops
global.setShutdownHookBehavior(ShutdownHookBehavior.DONT_REGISTER);
global.setMBeanServerLookup(this);
if (global.getTransportClass() != null)
{
global.setStrictPeerToPeer(false);
Properties properties = global.getTransportProperties();
if (!properties.containsKey(JGroupsTransport.CHANNEL_LOOKUP))
{
properties.put(CHANNEL_FACTORY, this.channelFactory);
properties.setProperty(CHANNEL_ID, global.getClusterName());
properties.setProperty(JGroupsTransport.CHANNEL_LOOKUP, ChannelFactoryChannelLookup.class.getName());
}
}
EmbeddedCacheManager manager = new DefaultCacheManager(global, configuration.getDefaultConfiguration(), false);
// Add named configurations
for (Configuration config: configuration.getNamedConfigurations())
{
manager.defineConfiguration(config.getName(), config);
}
return new DefaultCacheContainer(manager);
}
/**
* {@inheritDoc}
* @see org.infinispan.jmx.MBeanServerLookup#getMBeanServer(java.util.Properties)
*/
@Override
public MBeanServer getMBeanServer(Properties properties)
{
return this.server;
}
@CacheStarted
public void cacheStarted(CacheStartedEvent event)
{
log("Started", event.getCacheName(), event.getCacheManager());
}
@CacheStopped
public void cacheStopped(CacheStoppedEvent event)
{
String cacheName = event.getCacheName();
EmbeddedCacheManager container = event.getCacheManager();
log("Stopped", cacheName, container);
// Infinispan does not unregister cache mbean when cache stops (only when cache manager is stopped), so do it now to avoid classloader leaks
Configuration configuration = cacheName.equals(CacheContainer.DEFAULT_CACHE_NAME) ? container.getDefaultConfiguration() : container.defineConfiguration(cacheName, new Configuration());
if (configuration.isExposeJmxStatistics())
{
GlobalConfiguration global = container.getGlobalConfiguration();
String domain = global.getJmxDomain();
String jmxCacheName = String.format("%s(%s)", cacheName, configuration.getCacheModeString().toLowerCase(Locale.ENGLISH));
String containerName = global.getCacheManagerName();
try
{
// Fragile code alert!
ObjectName name = ObjectName.getInstance(String.format("%s:%s,%s=%s,manager=%s,%s=%s", domain, CacheJmxRegistration.CACHE_JMX_GROUP, ComponentsJmxRegistration.NAME_KEY, ObjectName.quote(jmxCacheName), ObjectName.quote(containerName), ComponentsJmxRegistration.COMPONENT_KEY, "Cache"));
if (this.server.isRegistered(name))
{
this.server.unregisterMBean(name);
logger.debug(String.format("Unregistered cache mbean: %s", name));
}
}
catch (JMException e)
{
logger.warn(String.format("Failed to unregister mbean for %s cache", cacheName), e);
}
}
}
private static void log(String event, String cacheName, EmbeddedCacheManager container)
{
logger.info(String.format("%s \"%s\" cache from \"%s\" container", event, cacheName, container.getGlobalConfiguration().getCacheManagerName()));
}
/**
* JGroupsChannelLookup implementation that uses a channel factory.
*/
public static class ChannelFactoryChannelLookup implements JGroupsChannelLookup
{
public static final String STACK = "stack";
public static final String DEFAULT_STACK = "udp";
public static final String AUTO_START = "auto-start";
public static final String DEFAULT_AUTO_START = Boolean.toString(true);
public static final String AUTO_STOP = "auto-stop";
public static final String DEFAULT_AUTO_STOP = Boolean.toString(true);
private boolean autoStart = true;
private boolean autoStop = true;
@Override
public Channel getJGroupsChannel(Properties properties)
{
this.autoStart = Boolean.valueOf(properties.getProperty(AUTO_START, DEFAULT_AUTO_START));
this.autoStop = Boolean.valueOf(properties.getProperty(AUTO_STOP, DEFAULT_AUTO_STOP));
String stack = properties.getProperty(STACK, DEFAULT_STACK);
String id = properties.getProperty(CHANNEL_ID);
ChannelFactory factory = (ChannelFactory) properties.get(CHANNEL_FACTORY);
try
{
return factory.createMultiplexerChannel(stack, id);
}
catch (Exception e)
{
throw new IllegalArgumentException(e);
}
}
@Override
public boolean shouldStartAndConnect()
{
return this.autoStart;
}
@Override
public boolean shouldStopAndDisconnect()
{
return this.autoStop;
}
}
}