org.hibernate.cache.infinispan.InfinispanRegionFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hibernate-infinispan
Show all versions of hibernate-infinispan
A module of the Hibernate Core project
package org.hibernate.cache.infinispan;
import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.transaction.TransactionManager;
import org.hibernate.cache.CacheDataDescription;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.CollectionRegion;
import org.hibernate.cache.EntityRegion;
import org.hibernate.cache.QueryResultsRegion;
import org.hibernate.cache.RegionFactory;
import org.hibernate.cache.TimestampsRegion;
import org.hibernate.cache.access.AccessType;
import org.hibernate.cache.infinispan.collection.CollectionRegionImpl;
import org.hibernate.cache.infinispan.entity.EntityRegionImpl;
import org.hibernate.cache.infinispan.impl.ClassLoaderAwareCache;
import org.hibernate.cache.infinispan.query.QueryResultsRegionImpl;
import org.hibernate.cache.infinispan.timestamp.TimestampsRegionImpl;
import org.hibernate.cache.infinispan.timestamp.TimestampTypeOverrides;
import org.hibernate.cache.infinispan.tm.HibernateTransactionManagerLookup;
import org.hibernate.cache.infinispan.util.CacheAdapter;
import org.hibernate.cache.infinispan.util.CacheAdapterImpl;
import org.hibernate.cfg.Settings;
import org.hibernate.util.PropertiesHelper;
import org.infinispan.AdvancedCache;
import org.infinispan.Cache;
import org.infinispan.config.Configuration;
import org.infinispan.manager.DefaultCacheManager;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
/**
* A {@link RegionFactory} for Infinispan-backed cache
* regions.
*
* @author Chris Bredesen
* @author Galder Zamarreño
* @since 3.5
*/
public class InfinispanRegionFactory implements RegionFactory {
private static final Log log = LogFactory.getLog(InfinispanRegionFactory.class);
private static final String PREFIX = "hibernate.cache.infinispan.";
private static final String CONFIG_SUFFIX = ".cfg";
private static final String STRATEGY_SUFFIX = ".eviction.strategy";
private static final String WAKE_UP_INTERVAL_SUFFIX = ".eviction.wake_up_interval";
private static final String MAX_ENTRIES_SUFFIX = ".eviction.max_entries";
private static final String LIFESPAN_SUFFIX = ".expiration.lifespan";
private static final String MAX_IDLE_SUFFIX = ".expiration.max_idle";
// private static final String STATISTICS_SUFFIX = ".statistics";
/**
* Classpath or filesystem resource containing Infinispan configurations the factory should use.
*
* @see #DEF_INFINISPAN_CONFIG_RESOURCE
*/
public static final String INFINISPAN_CONFIG_RESOURCE_PROP = "hibernate.cache.infinispan.cfg";
public static final String INFINISPAN_GLOBAL_STATISTICS_PROP = "hibernate.cache.infinispan.statistics";
private static final String ENTITY_KEY = "entity";
/**
* Name of the configuration that should be used for entity caches.
*
* @see #DEF_ENTITY_RESOURCE
*/
public static final String ENTITY_CACHE_RESOURCE_PROP = PREFIX + ENTITY_KEY + CONFIG_SUFFIX;
private static final String COLLECTION_KEY = "collection";
/**
* Name of the configuration that should be used for collection caches.
* No default value, as by default we try to use the same Infinispan cache
* instance we use for entity caching.
*
* @see #ENTITY_CACHE_RESOURCE_PROP
* @see #DEF_ENTITY_RESOURCE
*/
public static final String COLLECTION_CACHE_RESOURCE_PROP = PREFIX + COLLECTION_KEY + CONFIG_SUFFIX;
private static final String TIMESTAMPS_KEY = "timestamps";
/**
* Name of the configuration that should be used for timestamp caches.
*
* @see #DEF_TIMESTAMPS_RESOURCE
*/
public static final String TIMESTAMPS_CACHE_RESOURCE_PROP = PREFIX + TIMESTAMPS_KEY + CONFIG_SUFFIX;
private static final String QUERY_KEY = "query";
/**
* Name of the configuration that should be used for query caches.
*
* @see #DEF_QUERY_RESOURCE
*/
public static final String QUERY_CACHE_RESOURCE_PROP = PREFIX + QUERY_KEY + CONFIG_SUFFIX;
/**
* Default value for {@link #INFINISPAN_CONFIG_RESOURCE_PROP}. Specifies the "infinispan-configs.xml" file in this package.
*/
public static final String DEF_INFINISPAN_CONFIG_RESOURCE = "org/hibernate/cache/infinispan/builder/infinispan-configs.xml";
/**
* Default value for {@link #ENTITY_CACHE_RESOURCE_PROP}.
*/
public static final String DEF_ENTITY_RESOURCE = "entity";
/**
* Default value for {@link #TIMESTAMPS_CACHE_RESOURCE_PROP}.
*/
public static final String DEF_TIMESTAMPS_RESOURCE = "timestamps";
/**
* Default value for {@link #QUERY_CACHE_RESOURCE_PROP}.
*/
public static final String DEF_QUERY_RESOURCE = "local-query";
private EmbeddedCacheManager manager;
private final Map typeOverrides = new HashMap();
private final Set definedConfigurations = new HashSet();
private org.infinispan.transaction.lookup.TransactionManagerLookup transactionManagerlookup;
private TransactionManager transactionManager;
/**
* Create a new instance using the default configuration.
*/
public InfinispanRegionFactory() {
}
/**
* Create a new instance using conifguration properties in props
.
*
* @param props
* Environmental properties; currently unused.
*/
public InfinispanRegionFactory(Properties props) {
}
/** {@inheritDoc} */
public CollectionRegion buildCollectionRegion(String regionName, Properties properties, CacheDataDescription metadata) throws CacheException {
if (log.isDebugEnabled()) log.debug("Building collection cache region [" + regionName + "]");
Cache cache = getCache(regionName, COLLECTION_KEY, properties);
CacheAdapter cacheAdapter = CacheAdapterImpl.newInstance(cache);
CollectionRegionImpl region = new CollectionRegionImpl(cacheAdapter, regionName, metadata, transactionManager, this);
region.start();
return region;
}
/** {@inheritDoc} */
public EntityRegion buildEntityRegion(String regionName, Properties properties, CacheDataDescription metadata) throws CacheException {
if (log.isDebugEnabled()) log.debug("Building entity cache region [" + regionName + "]");
Cache cache = getCache(regionName, ENTITY_KEY, properties);
CacheAdapter cacheAdapter = CacheAdapterImpl.newInstance(cache);
EntityRegionImpl region = new EntityRegionImpl(cacheAdapter, regionName, metadata, transactionManager, this);
region.start();
return region;
}
/**
* {@inheritDoc}
*/
public QueryResultsRegion buildQueryResultsRegion(String regionName, Properties properties)
throws CacheException {
if (log.isDebugEnabled()) log.debug("Building query results cache region [" + regionName + "]");
String cacheName = typeOverrides.get(QUERY_KEY).getCacheName();
// If region name is not default one, lookup a cache for that region name
if (!regionName.equals("org.hibernate.cache.StandardQueryCache"))
cacheName = regionName;
Cache cache = getCache(cacheName, QUERY_KEY, properties);
CacheAdapter cacheAdapter = CacheAdapterImpl.newInstance(cache);
QueryResultsRegionImpl region = new QueryResultsRegionImpl(cacheAdapter, regionName, properties, transactionManager, this);
region.start();
return region;
}
/**
* {@inheritDoc}
*/
public TimestampsRegion buildTimestampsRegion(String regionName, Properties properties)
throws CacheException {
if (log.isDebugEnabled()) log.debug("Building timestamps cache region [" + regionName + "]");
Cache cache = getCache(regionName, TIMESTAMPS_KEY, properties);
CacheAdapter cacheAdapter = CacheAdapterImpl.newInstance(cache);
TimestampsRegionImpl region = createTimestampsRegion(cacheAdapter, regionName);
region.start();
return region;
}
protected TimestampsRegionImpl createTimestampsRegion(CacheAdapter cacheAdapter, String regionName) {
return new TimestampsRegionImpl(cacheAdapter, regionName, transactionManager, this);
}
protected TransactionManager getTransactionManager() {
return transactionManager;
}
/**
* {@inheritDoc}
*/
public boolean isMinimalPutsEnabledByDefault() {
return true;
}
@Override
public AccessType getDefaultAccessType() {
return AccessType.TRANSACTIONAL;
}
/**
* {@inheritDoc}
*/
public long nextTimestamp() {
return System.currentTimeMillis() / 100;
}
public void setCacheManager(EmbeddedCacheManager manager) {
this.manager = manager;
}
public EmbeddedCacheManager getCacheManager() {
return manager;
}
/**
* {@inheritDoc}
*/
public void start(Settings settings, Properties properties) throws CacheException {
log.debug("Starting Infinispan region factory");
try {
transactionManagerlookup = new HibernateTransactionManagerLookup(settings, properties);
transactionManager = transactionManagerlookup.getTransactionManager();
manager = createCacheManager(properties);
initGenericDataTypeOverrides();
Enumeration keys = properties.propertyNames();
while (keys.hasMoreElements()) {
String key = (String) keys.nextElement();
int prefixLoc = -1;
if ((prefixLoc = key.indexOf(PREFIX)) != -1) {
dissectProperty(prefixLoc, key, properties);
}
}
defineGenericDataTypeCacheConfigurations(settings, properties);
} catch (CacheException ce) {
throw ce;
} catch (Throwable t) {
throw new CacheException("Unable to start region factory", t);
}
}
/**
* {@inheritDoc}
*/
public void stop() {
log.debug("Stopping Infinispan CacheManager");
manager.stop();
}
/**
* Returns an unmodifiable map containing configured entity/collection type configuration overrides.
* This method should be used primarily for testing/checking purpouses.
*
* @return an unmodifiable map.
*/
public Map getTypeOverrides() {
return Collections.unmodifiableMap(typeOverrides);
}
public Set getDefinedConfigurations() {
return Collections.unmodifiableSet(definedConfigurations);
}
protected EmbeddedCacheManager createCacheManager(Properties properties) throws CacheException {
try {
String configLoc = PropertiesHelper.getString(INFINISPAN_CONFIG_RESOURCE_PROP, properties, DEF_INFINISPAN_CONFIG_RESOURCE);
EmbeddedCacheManager manager = new DefaultCacheManager(configLoc, false);
String globalStats = PropertiesHelper.extractPropertyValue(INFINISPAN_GLOBAL_STATISTICS_PROP, properties);
if (globalStats != null) {
manager.getGlobalConfiguration().setExposeGlobalJmxStatistics(Boolean.parseBoolean(globalStats));
}
manager.start();
return manager;
} catch (IOException e) {
throw new CacheException("Unable to create default cache manager", e);
}
}
private Map initGenericDataTypeOverrides() {
TypeOverrides entityOverrides = new TypeOverrides();
entityOverrides.setCacheName(DEF_ENTITY_RESOURCE);
typeOverrides.put(ENTITY_KEY, entityOverrides);
TypeOverrides collectionOverrides = new TypeOverrides();
collectionOverrides.setCacheName(DEF_ENTITY_RESOURCE);
typeOverrides.put(COLLECTION_KEY, collectionOverrides);
TypeOverrides timestampOverrides = new TimestampTypeOverrides();
timestampOverrides.setCacheName(DEF_TIMESTAMPS_RESOURCE);
typeOverrides.put(TIMESTAMPS_KEY, timestampOverrides);
TypeOverrides queryOverrides = new TypeOverrides();
queryOverrides.setCacheName(DEF_QUERY_RESOURCE);
typeOverrides.put(QUERY_KEY, queryOverrides);
return typeOverrides;
}
private void dissectProperty(int prefixLoc, String key, Properties properties) {
TypeOverrides cfgOverride = null;
int suffixLoc = -1;
if (!key.equals(INFINISPAN_CONFIG_RESOURCE_PROP) && (suffixLoc = key.indexOf(CONFIG_SUFFIX)) != -1) {
cfgOverride = getOrCreateConfig(prefixLoc, key, suffixLoc);
cfgOverride.setCacheName(PropertiesHelper.extractPropertyValue(key, properties));
} else if ((suffixLoc = key.indexOf(STRATEGY_SUFFIX)) != -1) {
cfgOverride = getOrCreateConfig(prefixLoc, key, suffixLoc);
cfgOverride.setEvictionStrategy(PropertiesHelper.extractPropertyValue(key, properties));
} else if ((suffixLoc = key.indexOf(WAKE_UP_INTERVAL_SUFFIX)) != -1) {
cfgOverride = getOrCreateConfig(prefixLoc, key, suffixLoc);
cfgOverride.setEvictionWakeUpInterval(Long.parseLong(PropertiesHelper.extractPropertyValue(key, properties)));
} else if ((suffixLoc = key.indexOf(MAX_ENTRIES_SUFFIX)) != -1) {
cfgOverride = getOrCreateConfig(prefixLoc, key, suffixLoc);
cfgOverride.setEvictionMaxEntries(PropertiesHelper.getInt(key, properties, -1));
} else if ((suffixLoc = key.indexOf(LIFESPAN_SUFFIX)) != -1) {
cfgOverride = getOrCreateConfig(prefixLoc, key, suffixLoc);
cfgOverride.setExpirationLifespan(Long.parseLong(PropertiesHelper.extractPropertyValue(key, properties)));
} else if ((suffixLoc = key.indexOf(MAX_IDLE_SUFFIX)) != -1) {
cfgOverride = getOrCreateConfig(prefixLoc, key, suffixLoc);
cfgOverride.setExpirationMaxIdle(Long.parseLong(PropertiesHelper.extractPropertyValue(key, properties)));
}
// else if ((suffixLoc = key.indexOf(STATISTICS_SUFFIX)) != -1) {
// cfgOverride = getOrCreateConfig(prefixLoc, key, suffixLoc);
// cfgOverride.setExposeStatistics(Boolean.parseBoolean(PropertiesHelper.extractPropertyValue(key, properties)));
// }
}
private TypeOverrides getOrCreateConfig(int prefixLoc, String key, int suffixLoc) {
String name = key.substring(prefixLoc + PREFIX.length(), suffixLoc);
TypeOverrides cfgOverride = typeOverrides.get(name);
if (cfgOverride == null) {
cfgOverride = new TypeOverrides();
typeOverrides.put(name, cfgOverride);
}
return cfgOverride;
}
private void defineGenericDataTypeCacheConfigurations(Settings settings, Properties properties) throws CacheException {
String[] defaultGenericDataTypes = new String[]{ENTITY_KEY, COLLECTION_KEY, TIMESTAMPS_KEY, QUERY_KEY};
for (String type : defaultGenericDataTypes) {
TypeOverrides override = overrideStatisticsIfPresent(typeOverrides.get(type), properties);
String cacheName = override.getCacheName();
Configuration newCacheCfg = override.createInfinispanConfiguration();
// Apply overrides
Configuration cacheConfig = manager.defineConfiguration(cacheName, cacheName, newCacheCfg);
// Configure transaction manager
cacheConfig = configureTransactionManager(cacheConfig, cacheName, properties);
manager.defineConfiguration(cacheName, cacheName, cacheConfig);
definedConfigurations.add(cacheName);
override.validateInfinispanConfiguration(cacheConfig);
}
}
private Cache getCache(String regionName, String typeKey, Properties properties) {
TypeOverrides regionOverride = typeOverrides.get(regionName);
if (!definedConfigurations.contains(regionName)) {
String templateCacheName = null;
Configuration regionCacheCfg = null;
if (regionOverride != null) {
if (log.isDebugEnabled()) log.debug("Cache region specific configuration exists: " + regionOverride);
regionOverride = overrideStatisticsIfPresent(regionOverride, properties);
regionCacheCfg = regionOverride.createInfinispanConfiguration();
String cacheName = regionOverride.getCacheName();
if (cacheName != null) // Region specific override with a given cache name
templateCacheName = cacheName;
else // Region specific override without cache name, so template cache name is generic for data type.
templateCacheName = typeOverrides.get(typeKey).getCacheName();
} else {
// No region specific overrides, template cache name is generic for data type.
templateCacheName = typeOverrides.get(typeKey).getCacheName();
regionCacheCfg = typeOverrides.get(typeKey).createInfinispanConfiguration();
}
// Configure transaction manager
regionCacheCfg = configureTransactionManager(regionCacheCfg, templateCacheName, properties);
// Apply overrides
manager.defineConfiguration(regionName, templateCacheName, regionCacheCfg);
definedConfigurations.add(regionName);
}
Cache cache = manager.getCache(regionName);
if (!cache.getStatus().allowInvocations()) {
cache.start();
}
return createCacheWrapper(cache.getAdvancedCache());
}
protected ClassLoaderAwareCache createCacheWrapper(AdvancedCache cache) {
return new ClassLoaderAwareCache(cache, Thread.currentThread().getContextClassLoader());
}
private Configuration configureTransactionManager(Configuration regionOverrides, String templateCacheName, Properties properties) {
// Get existing configuration to verify whether a tm was configured or not.
Configuration templateConfig = manager.defineConfiguration(templateCacheName, new Configuration());
String ispnTmLookupClassName = templateConfig.getTransactionManagerLookupClass();
String hbTmLookupClassName = org.hibernate.cache.infinispan.tm.HibernateTransactionManagerLookup.class.getName();
if (ispnTmLookupClassName != null && !ispnTmLookupClassName.equals(hbTmLookupClassName)) {
log.debug("Infinispan is configured [" + ispnTmLookupClassName + "] with a different transaction manager lookup " +
"class than Hibernate [" + hbTmLookupClassName + "]");
} else {
regionOverrides.setTransactionManagerLookup(transactionManagerlookup);
}
return regionOverrides;
}
private TypeOverrides overrideStatisticsIfPresent(TypeOverrides override, Properties properties) {
String globalStats = PropertiesHelper.extractPropertyValue(INFINISPAN_GLOBAL_STATISTICS_PROP, properties);
if (globalStats != null) {
override.setExposeStatistics(Boolean.parseBoolean(globalStats));
}
return override;
}
}