All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.ogm.datastore.infinispan.InfinispanDialect Maven / Gradle / Ivy

The newest version!
/*
 * 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 .
 */
package org.hibernate.ogm.datastore.infinispan;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;

import org.hibernate.LockMode;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.dialect.lock.LockingStrategy;
import org.hibernate.dialect.lock.OptimisticForceIncrementLockingStrategy;
import org.hibernate.dialect.lock.OptimisticLockingStrategy;
import org.hibernate.dialect.lock.PessimisticForceIncrementLockingStrategy;
import org.hibernate.ogm.datastore.infinispan.dialect.impl.InfinispanPessimisticWriteLockingStrategy;
import org.hibernate.ogm.datastore.infinispan.dialect.impl.InfinispanTupleSnapshot;
import org.hibernate.ogm.datastore.infinispan.impl.InfinispanEmbeddedDatastoreProvider;
import org.hibernate.ogm.datastore.infinispan.impl.InfinispanEmbeddedStoredProceduresManager;
import org.hibernate.ogm.datastore.infinispan.persistencestrategy.impl.KeyProvider;
import org.hibernate.ogm.datastore.infinispan.persistencestrategy.impl.LocalCacheManager;
import org.hibernate.ogm.datastore.infinispan.persistencestrategy.impl.LocalCacheManager.Bucket;
import org.hibernate.ogm.datastore.map.impl.MapAssociationSnapshot;
import org.hibernate.ogm.datastore.map.impl.MapHelpers;
import org.hibernate.ogm.dialect.query.spi.ClosableIterator;
import org.hibernate.ogm.dialect.spi.AssociationContext;
import org.hibernate.ogm.dialect.spi.AssociationTypeContext;
import org.hibernate.ogm.dialect.spi.BaseGridDialect;
import org.hibernate.ogm.dialect.spi.ModelConsumer;
import org.hibernate.ogm.dialect.spi.NextValueRequest;
import org.hibernate.ogm.dialect.spi.OperationContext;
import org.hibernate.ogm.dialect.spi.TransactionContext;
import org.hibernate.ogm.dialect.spi.TupleContext;
import org.hibernate.ogm.dialect.spi.TupleTypeContext;
import org.hibernate.ogm.dialect.spi.TuplesSupplier;
import org.hibernate.ogm.dialect.storedprocedure.spi.StoredProcedureAwareGridDialect;
import org.hibernate.ogm.entityentry.impl.TuplePointer;
import org.hibernate.ogm.model.key.spi.AssociationKey;
import org.hibernate.ogm.model.key.spi.AssociationKeyMetadata;
import org.hibernate.ogm.model.key.spi.EntityKey;
import org.hibernate.ogm.model.key.spi.EntityKeyMetadata;
import org.hibernate.ogm.model.key.spi.RowKey;
import org.hibernate.ogm.model.spi.Association;
import org.hibernate.ogm.model.spi.Tuple;
import org.hibernate.ogm.model.spi.Tuple.SnapshotType;
import org.hibernate.ogm.storedprocedure.ProcedureQueryParameters;
import org.hibernate.ogm.util.impl.EffectivelyFinal;
import org.hibernate.persister.entity.Lockable;
import org.hibernate.service.spi.ServiceRegistryAwareService;
import org.hibernate.service.spi.ServiceRegistryImplementor;

import org.infinispan.Cache;
import org.infinispan.atomic.AtomicMapLookup;
import org.infinispan.atomic.FineGrainedAtomicMap;
import org.infinispan.container.entries.CacheEntry;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.stream.CacheCollectors;

/**
 * EK is the entity cache key type
 * AK is the association cache key type
 * ISK is the identity source cache key type
 *
 * @author Emmanuel Bernard
 * @author Fabio Massimo Ercoli
 */
public class InfinispanDialect extends BaseGridDialect implements StoredProcedureAwareGridDialect, ServiceRegistryAwareService {

	private final InfinispanEmbeddedDatastoreProvider provider;
	private final InfinispanEmbeddedStoredProceduresManager storedProceduresDelegate;

	@EffectivelyFinal
	private ClassLoaderService classLoaderService;

	public InfinispanDialect(InfinispanEmbeddedDatastoreProvider provider) {
		this.provider = provider;
		storedProceduresDelegate = new InfinispanEmbeddedStoredProceduresManager();
	}

	/**
	 * Get a strategy instance which knows how to acquire a database-level lock
	 * of the specified/mode for this dialect.
	 *
	 * @param lockable The persister for the entity to be locked.
	 * @param lockMode The type of lock to be acquired.
	 * @return The appropriate locking strategy.
	 * @since 3.2
	 */
	@Override
	public LockingStrategy getLockingStrategy(Lockable lockable, LockMode lockMode) {
		if ( lockMode == LockMode.PESSIMISTIC_FORCE_INCREMENT ) {
			return new PessimisticForceIncrementLockingStrategy( lockable, lockMode );
		}
		else if ( lockMode == LockMode.PESSIMISTIC_WRITE ) {
			return new InfinispanPessimisticWriteLockingStrategy( lockable, lockMode );
		}
		else if ( lockMode == LockMode.PESSIMISTIC_READ ) {
			// TODO find a more efficient pessimistic read
			return new InfinispanPessimisticWriteLockingStrategy( lockable, lockMode );
		}
		else if ( lockMode == LockMode.OPTIMISTIC ) {
			return new OptimisticLockingStrategy( lockable, lockMode );
		}
		else if ( lockMode == LockMode.OPTIMISTIC_FORCE_INCREMENT ) {
			return new OptimisticForceIncrementLockingStrategy( lockable, lockMode );
		}
		else {
			return null;
		}
	}

	@Override
	public Tuple getTuple(EntityKey key, OperationContext operationContext) {
		EK cacheKey = getKeyProvider().getEntityCacheKey( key );
		Cache> cache = getCacheManager().getEntityCache( key.getMetadata() );
		return getTupleFromCacheKey( cacheKey, cache );
	}

	private Tuple getTupleFromCacheKey(EK cacheKey, Cache> cache) {
		FineGrainedAtomicMap atomicMap = AtomicMapLookup.getFineGrainedAtomicMap(
				cache,
				cacheKey,
				false
		);
		if ( atomicMap == null ) {
			return null;
		}
		else {
			return new Tuple( new InfinispanTupleSnapshot( atomicMap ), SnapshotType.UPDATE );
		}
	}

	@Override
	public Tuple createTuple(EntityKey key, OperationContext operationContext) {
		//TODO we don't verify that it does not yet exist assuming that this has been done before by the calling code
		//should we improve?
		Cache> cache = getCacheManager().getEntityCache( key.getMetadata() );
		EK cacheKey = getKeyProvider().getEntityCacheKey( key );
		FineGrainedAtomicMap atomicMap =  AtomicMapLookup.getFineGrainedAtomicMap( cache, cacheKey, true );
		return new Tuple( new InfinispanTupleSnapshot( atomicMap ), SnapshotType.INSERT );
	}

	@Override
	public void insertOrUpdateTuple(EntityKey key, TuplePointer tuplePointer, TupleContext tupleContext) {
		Tuple tuple = tuplePointer.getTuple();
		Map atomicMap = ( (InfinispanTupleSnapshot) tuple.getSnapshot() ).getAtomicMap();
		MapHelpers.applyTupleOpsOnMap( tuple, atomicMap );
	}

	@Override
	public void removeTuple(EntityKey key, TupleContext tupleContext) {
		Cache> cache = getCacheManager().getEntityCache( key.getMetadata() );
		EK cacheKey = getKeyProvider().getEntityCacheKey( key );
		AtomicMapLookup.removeAtomicMap( cache, cacheKey );
	}

	@Override
	public Association getAssociation(AssociationKey key, AssociationContext associationContext) {
		Cache>> cache = getCacheManager().getAssociationCache(
				key.getMetadata()
		);
		AK cacheKey = getKeyProvider().getAssociationCacheKey( key );
		Map> atomicMap = AtomicMapLookup.getFineGrainedAtomicMap( cache, cacheKey, false );
		return atomicMap == null ? null : new Association( new MapAssociationSnapshot( atomicMap ) );
	}

	@Override
	public Association createAssociation(AssociationKey key, AssociationContext associationContext) {
		//TODO we don't verify that it does not yet exist assuming that this has been done before by the calling code
		//should we improve?
		Cache>> cache = getCacheManager().getAssociationCache(
				key.getMetadata()
		);
		AK cacheKey = getKeyProvider().getAssociationCacheKey( key );
		Map> atomicMap = AtomicMapLookup.getFineGrainedAtomicMap( cache, cacheKey, true );
		return new Association( new MapAssociationSnapshot( atomicMap ) );
	}

	@Override
	public void insertOrUpdateAssociation(AssociationKey key, Association association, AssociationContext associationContext) {
		MapHelpers.updateAssociation( association );
	}

	@Override
	public void removeAssociation(AssociationKey key, AssociationContext associationContext) {
		Cache>> cache = getCacheManager().getAssociationCache(
				key.getMetadata()
		);
		AK cacheKey = getKeyProvider().getAssociationCacheKey( key );
		AtomicMapLookup.removeAtomicMap( cache, cacheKey );
	}

	@Override
	public boolean isStoredInEntityStructure(AssociationKeyMetadata associationKeyMetadata, AssociationTypeContext associationTypeContext) {
		return false;
	}

	@Override
	public Number nextValue(NextValueRequest request) {
		return provider.nextValue( request );
	}

	@Override
	public void forEachTuple( ModelConsumer consumer, TupleTypeContext tupleTypeContext, EntityKeyMetadata entityKeyMetadata ) {
		Set> buckets = getCacheManager().getWorkBucketsFor( entityKeyMetadata );

		for ( Bucket bucket : buckets ) {
			Map> queryResult = new HashMap<>();

			List>> collect = bucket.getCache().getAdvancedCache().cacheEntrySet()
				.stream()
				.filter( getKeyProvider().getFilter( entityKeyMetadata ) )
				// also collector needs to be Serializable (for non local caches)
				.collect( CacheCollectors.serializableCollector( () -> Collectors.toList() ) );

			for ( CacheEntry> entry : collect ) {
				queryResult.put( entry.getKey(), entry.getValue() );
			}

			// At runtime values of queryResult will be members of class org.infinispan.atomic.impl.AtomicKeySetImpl
			// this is because of the new implementation of FineGrainedAtomicMap Infinispan class (since 9.1)
			// query result return anyway valid keys, the values will be reloaded later by the InfinispanTupleIterator
			InfinispanTuplesSupplier supplier = new InfinispanTuplesSupplier( bucket.getCache(), queryResult );
			consumer.consume( supplier );
		}
	}

	@SuppressWarnings("unchecked")
	private LocalCacheManager getCacheManager() {
		return (LocalCacheManager) provider.getCacheManager();
	}

	@SuppressWarnings("unchecked")
	private KeyProvider getKeyProvider() {
		return (KeyProvider) provider.getKeyProvider();
	}

	@Override
	public ClosableIterator callStoredProcedure( String storedProcedureName, ProcedureQueryParameters queryParameters, TupleContext tupleContext ) {
		EmbeddedCacheManager embeddedCacheManager = getCacheManager().getCacheManager();
		return storedProceduresDelegate.callStoredProcedure( embeddedCacheManager, storedProcedureName, queryParameters, classLoaderService );
	}

	@Override
	public void injectServices(ServiceRegistryImplementor serviceRegistry) {
		this.classLoaderService = serviceRegistry.getService( ClassLoaderService.class );
	}

	private class InfinispanTuplesSupplier implements TuplesSupplier {

		private final Map> queryResult;
		private final Cache> cache;

		public InfinispanTuplesSupplier(Cache> cache, Map> queryResult) {
			this.cache = cache;
			this.queryResult = queryResult;
		}

		@Override
		public ClosableIterator get(TransactionContext transactionContext) {
			Iterator>> iterator = queryResult.entrySet().iterator();
			return new InfinispanTupleIterator( cache, iterator );
		}
	}

	private class InfinispanTupleIterator implements ClosableIterator {

		private final Iterator>> iterator;
		private final Cache> cache;

		public InfinispanTupleIterator(Cache> cache, Iterator>> iterator) {
			this.cache = cache;
			this.iterator = iterator;
		}

		@Override
		public boolean hasNext() {
			return iterator.hasNext();
		}

		@Override
		public Tuple next() {
			Entry> entry = iterator.next();
			return getTupleFromCacheKey( (EK) entry.getKey(), (Cache>) cache );
		}

		@Override
		public void close() {
		}
	}

	/**
	 * With the introduction of Infinispan clustered counter
	 * SequenceGenerator strategy is now supported
	 *
	 * @return true
	 */
	@Override
	public boolean supportsSequences() {
		return true;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy