/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* JBoss, Home of Professional Open Source
* Copyright 2011 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* 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,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.hibernate.ogm.datastore.infinispan.impl;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.hibernate.HibernateException;
import org.hibernate.ogm.datastore.spi.DatastoreProvider;
import org.hibernate.ogm.datastore.spi.DefaultDatastoreNames;
import org.hibernate.ogm.dialect.GridDialect;
import org.hibernate.ogm.dialect.infinispan.InfinispanDialect;
import org.hibernate.ogm.util.impl.Log;
import org.hibernate.ogm.util.impl.LoggerFactory;
import org.hibernate.ogm.util.impl.StringHelper;
import org.hibernate.service.jndi.spi.JndiService;
import org.hibernate.service.jta.platform.spi.JtaPlatform;
import org.hibernate.service.spi.Configurable;
import org.hibernate.service.spi.ServiceRegistryAwareService;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.service.spi.Startable;
import org.hibernate.service.spi.Stoppable;
import org.infinispan.Cache;
import org.infinispan.configuration.cache.Configuration;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.manager.DefaultCacheManager;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.util.FileLookupFactory;
/**
* Provides access to Infinispan's CacheManager; one CacheManager is needed for all caches,
* it can be taken via JNDI or started by this ServiceProvider; in this case it will also
* be stopped when no longer needed.
*
* @author Sanne Grinovero
* @author Emmanuel Bernard <emmanuel@hibernate.org>
*/
public class InfinispanDatastoreProvider implements DatastoreProvider, Startable, Stoppable,
ServiceRegistryAwareService, Configurable {
private JtaPlatform jtaPlatform;
private JndiService jndiService;
private Map cfg;
private Map<String,Cache> caches;
private boolean isCacheProvided;
private boolean started = false;
@Override
public Class<? extends GridDialect> getDefaultDialect() {
return InfinispanDialect.class;
}
/**
* The configuration property to use as key to define a custom configuration for Infinispan.
*/
public static final String INFINISPAN_CONFIGURATION_RESOURCENAME = "hibernate.ogm.infinispan.configuration_resourcename";
/**
* The key for the configuration property to define the jndi name of the cachemanager.
* If this property is defined, the cachemanager will be looked up via JNDI.
* JNDI properties passed in the form <tt>hibernate.jndi.*</tt> are used to define the context properties.
*/
public static final String CACHE_MANAGER_RESOURCE_PROP = "hibernate.ogm.infinispan.cachemanager_jndiname";
public static final String INFINISPAN_DEFAULT_CONFIG = "org/hibernate/ogm/datastore/infinispan/default-config.xml";
private static final Log log = LoggerFactory.make();
private EmbeddedCacheManager cacheManager;
public void start() {
if ( started ) {
// ServiceRegistry might invoke start multiple times, but always from the same initialization thread.
//TODO remove the start flag: no longer needed after HHH-7147
return;
}
try {
String jndiProperty = (String) cfg.get( CACHE_MANAGER_RESOURCE_PROP );
if ( jndiProperty == null ) {
String cfgName = (String) cfg.get( INFINISPAN_CONFIGURATION_RESOURCENAME );
if ( StringHelper.isEmpty( cfgName ) ) {
cfgName = INFINISPAN_DEFAULT_CONFIG;
}
log.tracef("Initializing Infinispan from configuration file at %1$s", cfgName);
cacheManager = createCustomCacheManager( cfgName, jtaPlatform );
isCacheProvided = false;
}
else {
log.tracef("Retrieving Infinispan from JNDI at %1$s", jndiProperty);
cacheManager = (EmbeddedCacheManager) jndiService.locate(jndiProperty);
isCacheProvided = true;
}
}
catch (RuntimeException e) {
throw log.unableToInitializeInfinispan( e );
}
eagerlyInitializeCaches(cacheManager);
//clear resources
this.jtaPlatform = null;
this.jndiService = null;
this.cfg = null;
this.started = true;
}
/**
* Need to make sure all needed caches are started before state transfer happens.
* This prevents this node to return undefined cache errors during replication
* when other nodes join this one.
* @param cacheManager
*/
private void eagerlyInitializeCaches(EmbeddedCacheManager cacheManager) {
caches = new ConcurrentHashMap<String, Cache> (3);
putInLocalCache(cacheManager, DefaultDatastoreNames.ASSOCIATION_STORE);
putInLocalCache(cacheManager, DefaultDatastoreNames.ENTITY_STORE);
putInLocalCache(cacheManager, DefaultDatastoreNames.IDENTIFIER_STORE);
}
private void putInLocalCache(EmbeddedCacheManager cacheManager, String cacheName) {
caches.put(
cacheName,
cacheManager.getCache(cacheName)
);
}
private EmbeddedCacheManager createCustomCacheManager(String cfgName, JtaPlatform platform) {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
TransactionManagerLookupDelegator transactionManagerLookupDelegator = new TransactionManagerLookupDelegator( platform );
try {
InputStream configurationFile = FileLookupFactory.newInstance().lookupFileStrict( cfgName, contextClassLoader );
try {
cacheManager = new DefaultCacheManager( configurationFile, false );
// override the named cache configuration defined in the configuration file to
// inject the platform TransactionManager
for (String cacheName : cacheManager.getCacheNames() ) {
Configuration originalCfg = cacheManager.getCacheConfiguration( cacheName );
Configuration newCfg = new ConfigurationBuilder()
.read( originalCfg )
.transaction()
.transactionManagerLookup( transactionManagerLookupDelegator )
.build();
cacheManager.defineConfiguration( cacheName, newCfg );
}
cacheManager.start();
return cacheManager;
}
finally {
if ( configurationFile != null ) {
configurationFile.close();
}
}
} catch (RuntimeException re) {
throw raiseConfigurationError( re, cfgName );
}
catch (IOException e) {
throw raiseConfigurationError( e, cfgName );
}
}
public EmbeddedCacheManager getEmbeddedCacheManager() {
return cacheManager;
}
//prefer generic form over specific ones to prepare for flexible cache setting
public Cache getCache(String name) {
return caches.get(name);
}
public void stop() {
if ( !isCacheProvided && cacheManager != null ) {
cacheManager.stop();
}
}
private HibernateException raiseConfigurationError(Exception e, String cfgName) {
return new HibernateException(
"Could not start Infinispan CacheManager using as configuration file: " + cfgName, e
);
}
@Override
public void injectServices(ServiceRegistryImplementor serviceRegistry) {
jtaPlatform = serviceRegistry.getService( JtaPlatform.class );
jndiService = serviceRegistry.getService( JndiService.class );
}
@Override
public void configure(Map configurationValues) {
cfg = configurationValues;
}
}