/*
* JBoss, a division of Red Hat
* Copyright 2012, Red Hat Middleware, LLC, and individual
* contributors as indicated by the @authors tag. 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.exoplatform.services.organization.idm;
import java.io.InputStream;
import org.exoplatform.container.monitor.jvm.J2EEServerInfo;
import org.gatein.common.classloader.DelegatingClassLoader;
import org.gatein.common.logging.Logger;
import org.gatein.common.logging.LoggerFactory;
import org.infinispan.Cache;
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.DefaultCacheManager;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.transaction.lookup.JBossStandaloneJTAManagerLookup;
import org.infinispan.transaction.lookup.JBossTransactionManagerLookup;
import org.infinispan.transaction.lookup.TransactionManagerLookup;
import org.picketlink.idm.cache.APICacheProvider;
import org.picketlink.idm.impl.cache.InfinispanAPICacheProviderImpl;
import org.picketlink.idm.impl.cache.InfinispanIdentityStoreCacheProviderImpl;
import org.picketlink.idm.spi.cache.IdentityStoreCacheProvider;
/**
* Singleton for creating infinispan caches and PLIDM cache providers. It's singleton actually as cacheManager is shared between
* all portal containers.
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
class InfinispanCacheFactory {
private static final InfinispanCacheFactory INSTANCE = new InfinispanCacheFactory();
private static final Logger log = LoggerFactory.getLogger(InfinispanCacheFactory.class);
private InfinispanCacheFactory() {
}
public static final InfinispanCacheFactory getInstance() {
return INSTANCE;
}
// Infinispan cache manager is shared between all portal containers,
private EmbeddedCacheManager cacheManager;
// Indicate whether cache entries for Structure can be expired or not
private boolean skipExpirationOfStructureCacheEntries = false;
// Used only if skipExpirationOfStructureCacheEntries is false. It's obtained from infinispan configuration (not from
// service parameter)
private long cacheLifespanOfLeafNodes = -1;
public void setSkipExpirationOfStructureCacheEntries(boolean skipExpirationOfStructureCacheEntries) {
if (cacheManager != null && (skipExpirationOfStructureCacheEntries != this.skipExpirationOfStructureCacheEntries)) {
log.warn("CacheManager is already initialized. Setting of skipExpirationOfStructureCacheEntries won't have effect");
} else {
this.skipExpirationOfStructureCacheEntries = skipExpirationOfStructureCacheEntries;
}
}
APICacheProvider createAPICacheProvider(long staleCacheNodesLinksCleanerDelay, Cache infinispanCache) {
InfinispanAPICacheProviderImpl apiCacheProvider = new InfinispanAPICacheProviderImpl();
apiCacheProvider.initialize(infinispanCache, skipExpirationOfStructureCacheEntries, cacheLifespanOfLeafNodes,
staleCacheNodesLinksCleanerDelay);
return apiCacheProvider;
}
IntegrationCache createIntegrationCache(Cache infinispanCache) {
IntegrationCache integrationCache = new IntegrationCache();
// We will use -1 for staleCacheNodesLinksCleanerDelay, as integrationCache is using same cache as apiCacheProvider,
// so we don't need to start another cleaner thread
integrationCache.initialize(infinispanCache, skipExpirationOfStructureCacheEntries, cacheLifespanOfLeafNodes, -1);
return integrationCache;
}
IdentityStoreCacheProvider createStoreCacheProvider(long staleCacheNodesLinksCleanerDelay, Cache infinispanCache) {
InfinispanIdentityStoreCacheProviderImpl storeCacheProvider = new InfinispanIdentityStoreCacheProviderImpl();
storeCacheProvider.initialize(infinispanCache, skipExpirationOfStructureCacheEntries, cacheLifespanOfLeafNodes,
staleCacheNodesLinksCleanerDelay);
return storeCacheProvider;
}
/**
* Create, configure and start infinispan cache
*
* @param configStream input stream with infinispan configuration. Some things from this stream will be changed
* programmatically before cache creation
* @param portalContainerName name of portal container
* @param apiOrStore Value can be either "api" if cache will be for apiCacheProvider or "store" for storeCacheProvider
* @return created and started infinispan cache
* @throws Exception
*/
Cache createInfinispanCache(InputStream configStream, String portalContainerName, String apiOrStore) throws Exception {
ClassLoader infinispanCl = EmbeddedCacheManager.class.getClassLoader();
ClassLoader portalCl = Thread.currentThread().getContextClassLoader();
// Infinispan classloader is first delegate, so in AS7 environment, infinispan is able to see
// jgroups3 classes with bigger priority than jgroups2 classes
ClassLoader delegating = new DelegatingClassLoader(infinispanCl, portalCl);
try {
// Set delegating classloader as tccl
Thread.currentThread().setContextClassLoader(delegating);
String cacheName = "idm-" + portalContainerName + "-" + apiOrStore;
EmbeddedCacheManager cacheManager = getSharedCacheManager(configStream);
return cacheManager.getCache(cacheName);
} finally {
// Put portal classloader to be tccl again
Thread.currentThread().setContextClassLoader(portalCl);
}
}
/**
* Create and configure cacheManager, which will be used to create infinispan caches.
*
* @param configStream stream with infinispan configuration
* @return cacheManager
* @throws Exception
*/
private EmbeddedCacheManager getSharedCacheManager(InputStream configStream) throws Exception {
if (cacheManager == null) {
EmbeddedCacheManager cacheManager = new DefaultCacheManager(configStream, false);
GlobalConfiguration globalConfigFromXml = cacheManager.getCacheManagerConfiguration();
GlobalConfigurationBuilder globalConfigBuilder = new GlobalConfigurationBuilder();
globalConfigBuilder.read(globalConfigFromXml);
Configuration configFromXml = cacheManager.getDefaultCacheConfiguration();
ConfigurationBuilder configBuilder = new ConfigurationBuilder().read(configFromXml);
// Configure transactionManagerLookup programmatically if not provided in configuration
TransactionManagerLookup tmLookup = configFromXml.transaction().transactionManagerLookup();
if (tmLookup == null) {
tmLookup = getTransactionManagerLookup();
configBuilder.transaction().transactionManagerLookup(tmLookup);
}
log.debug("Infinispan transaction manager lookup: " + tmLookup);
// If we don't want structure cache entries to expire, we need to configure infinite lifespan and keep original
// lifespan value, which will be send to tree api
if (skipExpirationOfStructureCacheEntries) {
cacheLifespanOfLeafNodes = configFromXml.expiration().lifespan();
configBuilder.expiration().lifespan(-1);
log.debug("Expiration of structure cache entries is disabled. Leaf nodes will use expiration "
+ cacheLifespanOfLeafNodes);
}
cacheManager = new DefaultCacheManager(globalConfigBuilder.build(), configBuilder.build(), true);
this.cacheManager = cacheManager;
}
return cacheManager;
}
/**
* @return JBossTransactionManagerLookup if we are in AS7 or JBossStandaloneJTAManagerLookup otherwise
*/
private TransactionManagerLookup getTransactionManagerLookup() {
if (new J2EEServerInfo().isJBoss()) {
return new JBossTransactionManagerLookup();
} else {
return new JBossStandaloneJTAManagerLookup();
}
}
}