/* * Hibernate OGM, Domain model persistence for NoSQL datastores * * License: GNU Lesser General Public License (LGPL), version 2.1 or later * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. */ package org.hibernate.ogm.datastore.infinispan.persistencestrategy.impl; import java.io.InputStream; import java.net.URL; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.hibernate.HibernateException; import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform; import org.hibernate.ogm.datastore.infinispan.impl.TransactionManagerLookupDelegator; import org.hibernate.ogm.model.key.spi.AssociationKeyMetadata; import org.hibernate.ogm.model.key.spi.EntityKeyMetadata; import org.hibernate.ogm.model.key.spi.IdSourceKeyMetadata; import org.hibernate.ogm.model.key.spi.RowKey; 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.configuration.global.SerializationConfigurationBuilder; import org.infinispan.configuration.parsing.ConfigurationBuilderHolder; import org.infinispan.manager.DefaultCacheManager; import org.infinispan.manager.EmbeddedCacheManager; import org.infinispan.transaction.TransactionMode; /** * Provides access to the ISPN caches used for storing entities, associations and id sources. The number of caches used * depends on the specific implementation of this contract. * <p> * Implementations 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. * <p> * Implementations must be thread-safe. * * @author Sanne Grinovero * @author Emmanuel Bernard <emmanuel@hibernate.org> * @author Gunnar Morling * * @param <EK> the entity cache key type * @param <AK> the association cache key type * @param <ISK> the identity source cache key type */ public abstract class LocalCacheManager<EK, AK, ISK> { private final EmbeddedCacheManager cacheManager; private final boolean isProvidedCacheManager; protected LocalCacheManager(EmbeddedCacheManager cacheManager) { this.cacheManager = cacheManager; this.isProvidedCacheManager = true; } protected LocalCacheManager(URL configUrl, JtaPlatform platform, Set<String> cacheNames, KeyProvider<EK, AK, ISK> keyProvider) { this.cacheManager = createCustomCacheManager( configUrl, platform, cacheNames, keyProvider ); this.isProvidedCacheManager = false; } private static EmbeddedCacheManager createCustomCacheManager(URL configUrl, JtaPlatform platform, Set<String> cacheNames, KeyProvider<?, ?, ?> keyProvider) { Set<String> allCacheNames = new HashSet<>();//To include both the requires ones and the ones found in the configuration files allCacheNames.addAll( cacheNames ); TransactionManagerLookupDelegator transactionManagerLookupDelegator = new TransactionManagerLookupDelegator( platform ); try { InputStream configurationFile = configUrl.openStream(); try { InfinispanConfigurationParser ispnConfiguration = new InfinispanConfigurationParser(); ConfigurationBuilderHolder configurationBuilderHolder = ispnConfiguration.parseFile( configurationFile ); EmbeddedCacheManager tmpCacheManager = new DefaultCacheManager( configurationBuilderHolder, false ); // override global configuration from the config file to inject externalizers SerializationConfigurationBuilder serializationConfiguration = new GlobalConfigurationBuilder() .read( tmpCacheManager.getCacheManagerConfiguration() ) .serialization(); ExternalizersIntegration.registerOgmExternalizers( serializationConfiguration ); allCacheNames.addAll( tmpCacheManager.getCacheNames() ); GlobalConfiguration globalConfiguration = serializationConfiguration.build(); EmbeddedCacheManager cacheManager = new DefaultCacheManager( globalConfiguration, false ); // override the named cache configuration defined in the configuration file to // inject the platform TransactionManager for ( String cacheName : allCacheNames ) { Configuration originalCfg = tmpCacheManager.getCacheConfiguration( cacheName ); if ( originalCfg == null ) { originalCfg = tmpCacheManager.getDefaultCacheConfiguration(); } Configuration newCfg; if ( originalCfg.transaction().transactionMode() == TransactionMode.TRANSACTIONAL ) { //Inject our TransactionManager lookup delegate for transactional caches ONLY! //injecting one in a non-transactional cache will have side-effects on other configuration settings. newCfg = new ConfigurationBuilder() .read( originalCfg ) .transaction() .transactionManagerLookup( transactionManagerLookupDelegator ) .build(); } else { //But also define all other caches: newCfg = new ConfigurationBuilder() .read( originalCfg ) .build(); } cacheManager.defineConfiguration( cacheName, newCfg ); } cacheManager.start(); return cacheManager; } finally { if ( configurationFile != null ) { configurationFile.close(); } } } catch (Exception e) { throw raiseConfigurationError( e, configUrl.toString() ); } } private static HibernateException raiseConfigurationError(Exception e, String cfgName) { return new HibernateException( "Could not start Infinispan CacheManager using as configuration file: " + cfgName, e ); } public EmbeddedCacheManager getCacheManager() { return cacheManager; } public void stop() { if ( !isProvidedCacheManager ) { cacheManager.stop(); } } public abstract Cache<EK, Map<String, Object>> getEntityCache(EntityKeyMetadata keyMetadata); public abstract Cache<AK, Map<RowKey, Map<String, Object>>> getAssociationCache(AssociationKeyMetadata keyMetadata); public abstract Cache<ISK, Object> getIdSourceCache(IdSourceKeyMetadata keyMetadata); /** * Groups the given entity types by the caches they are stored in. * * @param entityKeyMetadatas the meta-data of the entities * @return the {@link Bucket}s containg the entities corressponding to the entity key meta-datas */ public abstract Set<Bucket<EK>> getWorkBucketsFor(EntityKeyMetadata... entityKeyMetadatas); /** * Describe all the entity key meta-data that work on a given cache */ public static class Bucket<EK> { private final Cache<EK, Map<String, Object>> cache; private final EntityKeyMetadata[] entityKeyMetadatas; public Bucket(Cache<EK, Map<String,Object>> cache, List<EntityKeyMetadata> entityKeyMetadatas) { this.cache = cache; this.entityKeyMetadatas = entityKeyMetadatas.toArray( new EntityKeyMetadata[entityKeyMetadatas.size()] ); } public Bucket(Cache<EK, Map<String,Object>> cache, EntityKeyMetadata... entityKeyMetadatas) { this.cache = cache; this.entityKeyMetadatas = entityKeyMetadatas; } public Cache<EK, Map<String, Object>> getCache() { return cache; } public EntityKeyMetadata[] getEntityKeyMetadata() { return entityKeyMetadatas; } } }