/*
* 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.List;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.infinispan.Cache;
import org.infinispan.config.Configuration;
import org.infinispan.config.GlobalConfiguration;
import org.infinispan.lifecycle.ComponentStatus;
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.CacheStopped;
import org.infinispan.notifications.cachemanagerlistener.event.CacheStoppedEvent;
import org.infinispan.remoting.transport.Address;
/**
* Cache manager wrapper to work around ISPN-658
* @author Paul Ferraro
*/
@Listener
public class DefaultCacheContainer implements EmbeddedCacheManager
{
private final EmbeddedCacheManager container;
private final ConcurrentMap<String, EmbeddedCacheManager> containers = new ConcurrentHashMap<String, EmbeddedCacheManager>();
public DefaultCacheContainer(EmbeddedCacheManager manager)
{
this.container = manager;
}
@Override
public <K, V> Cache<K, V> getCache()
{
return this.getCache(CacheContainer.DEFAULT_CACHE_NAME, this.container.getDefaultConfiguration());
}
@Override
public <K, V> Cache<K, V> getCache(String cacheName)
{
return this.getCache(cacheName, this.container.defineConfiguration(cacheName, new Configuration()));
}
// Until ISPN-658 is fixed, we need to create a separate adhoc cache manager if requested cache uses DIST mode.
private <K, V> Cache<K, V> getCache(final String cacheName, Configuration config)
{
if (config.getCacheMode().isDistributed())
{
GlobalConfiguration globalConfig = this.container.getGlobalConfiguration();
// Create unique cluster name using cache name
String clusterName = globalConfig.getClusterName() + "/" + cacheName;
EmbeddedCacheManager container = this.containers.get(clusterName);
if (container == null)
{
// Workaround for ISPN-744
GlobalConfiguration global = new GlobalConfiguration();
global.setAllowDuplicateDomains(globalConfig.isAllowDuplicateDomains());
global.setAsyncListenerExecutorFactoryClass(globalConfig.getAsyncListenerExecutorFactoryClass());
global.setAsyncListenerExecutorProperties(globalConfig.getAsyncListenerExecutorProperties());
global.setAsyncTransportExecutorFactoryClass(globalConfig.getAsyncTransportExecutorFactoryClass());
global.setAsyncTransportExecutorProperties(globalConfig.getAsyncTransportExecutorProperties());
global.setClusterName(clusterName);
global.setDistributedSyncTimeout(globalConfig.getDistributedSyncTimeout());
global.setEvictionScheduledExecutorFactoryClass(globalConfig.getEvictionScheduledExecutorFactoryClass());
global.setEvictionScheduledExecutorProperties(globalConfig.getEvictionScheduledExecutorProperties());
global.setExposeGlobalJmxStatistics(globalConfig.isExposeGlobalJmxStatistics());
global.setJmxDomain(globalConfig.getJmxDomain());
global.setMarshallerClass(globalConfig.getMarshallerClass());
global.setMarshallVersion(globalConfig.getMarshallVersion());
global.setMBeanServerLookup(globalConfig.getMBeanServerLookup());
global.setReplicationQueueScheduledExecutorFactoryClass(globalConfig.getReplicationQueueScheduledExecutorFactoryClass());
global.setReplicationQueueScheduledExecutorProperties(globalConfig.getReplicationQueueScheduledExecutorProperties());
global.setShutdownHookBehavior(globalConfig.getShutdownHookBehavior());
global.setStrictPeerToPeer(globalConfig.isStrictPeerToPeer());
global.setTransportClass(globalConfig.getTransportClass());
global.setTransportNodeName(globalConfig.getTransportNodeName());
Properties properties = new Properties(globalConfig.getTransportProperties());
properties.setProperty("clusterId", clusterName);
global.setTransportProperties(properties);
// Create single use cache manager
container = new DefaultCacheManager(global, config, false)
{
/**
* {@inheritDoc}
* @see org.infinispan.manager.DefaultCacheManager#getCache(java.lang.String)
*/
@Override
public <KK, VV> Cache<KK, VV> getCache(String name)
{
if (cacheName.equals(name))
{
return super.getCache(CacheContainer.DEFAULT_CACHE_NAME);
}
return DefaultCacheContainer.this.getCache(cacheName);
}
/**
* {@inheritDoc}
* @see org.infinispan.manager.DefaultCacheManager#addListener(java.lang.Object)
*/
@Override
public void addListener(Object listener)
{
super.addListener(listener);
// Warning - very, very hacky...
// Better solution - use custom notifier that allows
// some level of control over listeners order
if (listener != DefaultCacheContainer.this)
{
// Make sure our listener is last since our listener stops the container!
this.removeListener(DefaultCacheContainer.this);
this.addListener(DefaultCacheContainer.this);
}
}
};
EmbeddedCacheManager existing = this.containers.putIfAbsent(clusterName, container);
if (existing == null)
{
container.addListener(this);
for (Object listener: this.container.getListeners())
{
container.addListener(listener);
}
// The cache manager should stop when the cache stops
container.start();
}
else
{
container = existing;
}
}
return container.getCache(cacheName);
}
return this.container.getCache(cacheName);
}
@Override
public synchronized void start()
{
this.container.start();
}
@Override
public synchronized void stop()
{
this.container.stop();
}
@Override
public void addListener(Object listener)
{
this.container.addListener(listener);
}
@Override
public void removeListener(Object listener)
{
this.container.removeListener(listener);
}
@Override
public Set<Object> getListeners()
{
return this.container.getListeners();
}
@Override
public Configuration defineConfiguration(String cacheName, Configuration configurationOverride)
{
return this.container.defineConfiguration(cacheName, configurationOverride);
}
@Override
public Configuration defineConfiguration(String cacheName, String templateCacheName, Configuration configurationOverride)
{
return this.container.defineConfiguration(cacheName, templateCacheName, configurationOverride);
}
@Override
public String getClusterName()
{
return this.container.getClusterName();
}
@Override
public List<Address> getMembers()
{
return this.container.getMembers();
}
@Override
public Address getAddress()
{
return this.container.getAddress();
}
@Override
public boolean isCoordinator()
{
return this.container.isCoordinator();
}
@Override
public ComponentStatus getStatus()
{
return this.container.getStatus();
}
@Override
public GlobalConfiguration getGlobalConfiguration()
{
return this.container.getGlobalConfiguration();
}
@Override
public Configuration getDefaultConfiguration()
{
return this.container.getDefaultConfiguration();
}
@Override
public Set<String> getCacheNames()
{
return this.container.getCacheNames();
}
@CacheStopped
public synchronized void cacheStopped(CacheStoppedEvent event)
{
final EmbeddedCacheManager manager = event.getCacheManager();
this.containers.remove(manager.getClusterName());
manager.stop();
}
}