/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* 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.test.cache.infinispan;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cache.infinispan.InfinispanRegionFactory;
import org.hibernate.cache.infinispan.impl.BaseGeneralDataRegion;
import org.hibernate.cache.infinispan.impl.BaseRegion;
import org.hibernate.cache.spi.GeneralDataRegion;
import org.hibernate.cache.spi.QueryResultsRegion;
import org.hibernate.cache.spi.Region;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.test.cache.infinispan.util.CacheTestUtil;
import org.hibernate.test.cache.infinispan.util.ExpectingInterceptor;
import org.hibernate.test.cache.infinispan.util.TestInfinispanRegionFactory;
import org.infinispan.commands.write.PutKeyValueCommand;
import org.infinispan.commands.write.RemoveCommand;
import org.infinispan.configuration.cache.CacheMode;
import org.junit.Test;
import org.infinispan.AdvancedCache;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
* Base class for tests of QueryResultsRegion and TimestampsRegion.
*
* @author Galder ZamarreƱo
* @since 3.5
*/
public abstract class AbstractGeneralDataRegionTest extends AbstractRegionImplTest {
protected static final String KEY = "Key";
protected static final String VALUE1 = "value1";
protected static final String VALUE2 = "value2";
protected static final String VALUE3 = "value3";
@Override
public List<Object[]> getCacheModeParameters() {
// the actual cache mode and access type is irrelevant for the general data regions
return Arrays.<Object[]>asList(new Object[]{ CacheMode.INVALIDATION_SYNC, AccessType.TRANSACTIONAL });
}
@Override
protected void putInRegion(Region region, Object key, Object value) {
((GeneralDataRegion) region).put(null, key, value );
}
@Override
protected void removeFromRegion(Region region, Object key) {
((GeneralDataRegion) region).evict( key );
}
protected interface SFRConsumer {
void accept(List<SessionFactory> sessionFactories, List<GeneralDataRegion> regions) throws Exception;
}
protected void withSessionFactoriesAndRegions(int num, SFRConsumer consumer) throws Exception {
StandardServiceRegistryBuilder ssrb = createStandardServiceRegistryBuilder()
.applySetting(AvailableSettings.CACHE_REGION_FACTORY, TestInfinispanRegionFactory.class.getName());
Properties properties = CacheTestUtil.toProperties( ssrb.getSettings() );
List<StandardServiceRegistry> registries = new ArrayList<>();
List<SessionFactory> sessionFactories = new ArrayList<>();
List<GeneralDataRegion> regions = new ArrayList<>();
for (int i = 0; i < num; ++i) {
StandardServiceRegistry registry = ssrb.build();
registries.add(registry);
SessionFactory sessionFactory = new MetadataSources(registry).buildMetadata().buildSessionFactory();
sessionFactories.add(sessionFactory);
InfinispanRegionFactory regionFactory = (InfinispanRegionFactory) registry.getService(RegionFactory.class);
GeneralDataRegion region = (GeneralDataRegion) createRegion(
regionFactory,
getStandardRegionName( REGION_PREFIX ),
properties,
null
);
regions.add(region);
}
try {
consumer.accept(sessionFactories, regions);
} finally {
for (SessionFactory sessionFactory : sessionFactories) {
sessionFactory.close();
}
for (StandardServiceRegistry registry : registries) {
StandardServiceRegistryBuilder.destroy( registry );
}
}
}
@Test
public void testEvict() throws Exception {
withSessionFactoriesAndRegions(2, ((sessionFactories, regions) -> {
GeneralDataRegion localRegion = regions.get(0);
GeneralDataRegion remoteRegion = regions.get(1);
SharedSessionContractImplementor localSession = (SharedSessionContractImplementor) sessionFactories.get(0).openSession();
SharedSessionContractImplementor remoteSession = (SharedSessionContractImplementor) sessionFactories.get(1).openSession();
AdvancedCache localCache = ((BaseRegion) localRegion).getCache();
AdvancedCache remoteCache = ((BaseRegion) remoteRegion).getCache();
try {
assertNull("local is clean", localRegion.get(localSession, KEY));
assertNull("remote is clean", remoteRegion.get(remoteSession, KEY));
// If this node is backup owner, it will see the update once as originator and then when getting the value from primary
boolean isLocalNodeBackupOwner = localCache.getDistributionManager().locate(KEY).indexOf(localCache.getCacheManager().getAddress()) > 0;
CountDownLatch insertLatch = new CountDownLatch(isLocalNodeBackupOwner ? 3 : 2);
ExpectingInterceptor.get(localCache).when((ctx, cmd) -> cmd instanceof PutKeyValueCommand).countDown(insertLatch);
ExpectingInterceptor.get(remoteCache).when((ctx, cmd) -> cmd instanceof PutKeyValueCommand).countDown(insertLatch);
Transaction tx = localSession.getTransaction();
tx.begin();
try {
localRegion.put(localSession, KEY, VALUE1);
tx.commit();
} catch (Exception e) {
tx.rollback();
throw e;
}
assertTrue(insertLatch.await(2, TimeUnit.SECONDS));
assertEquals(VALUE1, localRegion.get(localSession, KEY));
assertEquals(VALUE1, remoteRegion.get(remoteSession, KEY));
CountDownLatch removeLatch = new CountDownLatch(isLocalNodeBackupOwner ? 3 : 2);
ExpectingInterceptor.get(localCache).when((ctx, cmd) -> cmd instanceof RemoveCommand).countDown(removeLatch);
ExpectingInterceptor.get(remoteCache).when((ctx, cmd) -> cmd instanceof RemoveCommand).countDown(removeLatch);
regionEvict(localRegion);
assertTrue(removeLatch.await(2, TimeUnit.SECONDS));
assertEquals(null, localRegion.get(localSession, KEY));
assertEquals(null, remoteRegion.get(remoteSession, KEY));
} finally {
localSession.close();
remoteSession.close();
ExpectingInterceptor.cleanup(localCache, remoteCache);
}
}));
}
protected void regionEvict(GeneralDataRegion region) throws Exception {
region.evict(KEY);
}
protected abstract String getStandardRegionName(String regionPrefix);
/**
* Test method for {@link QueryResultsRegion#evictAll()}.
* <p/>
* FIXME add testing of the "immediately without regard for transaction isolation" bit in the
* CollectionRegionAccessStrategy API.
*/
public void testEvictAll() throws Exception {
withSessionFactoriesAndRegions(2, (sessionFactories, regions) -> {
GeneralDataRegion localRegion = regions.get(0);
GeneralDataRegion remoteRegion = regions.get(1);
AdvancedCache localCache = ((BaseGeneralDataRegion) localRegion).getCache();
AdvancedCache remoteCache = ((BaseGeneralDataRegion) remoteRegion).getCache();
SharedSessionContractImplementor localSession = (SharedSessionContractImplementor) sessionFactories.get(0).openSession();
SharedSessionContractImplementor remoteSession = (SharedSessionContractImplementor) sessionFactories.get(1).openSession();
try {
Set localKeys = localCache.keySet();
assertEquals( "No valid children in " + localKeys, 0, localKeys.size() );
Set remoteKeys = remoteCache.keySet();
assertEquals( "No valid children in " + remoteKeys, 0, remoteKeys.size() );
assertNull( "local is clean", localRegion.get(null, KEY ) );
assertNull( "remote is clean", remoteRegion.get(null, KEY ) );
localRegion.put(localSession, KEY, VALUE1);
assertEquals( VALUE1, localRegion.get(null, KEY ) );
remoteRegion.put(remoteSession, KEY, VALUE1);
assertEquals( VALUE1, remoteRegion.get(null, KEY ) );
localRegion.evictAll();
// This should re-establish the region root node in the optimistic case
assertNull( localRegion.get(null, KEY ) );
localKeys = localCache.keySet();
assertEquals( "No valid children in " + localKeys, 0, localKeys.size() );
// Re-establishing the region root on the local node doesn't
// propagate it to other nodes. Do a get on the remote node to re-establish
// This only adds a node in the case of optimistic locking
assertEquals( null, remoteRegion.get(null, KEY ) );
remoteKeys = remoteCache.keySet();
assertEquals( "No valid children in " + remoteKeys, 0, remoteKeys.size() );
assertEquals( "local is clean", null, localRegion.get(null, KEY ) );
assertEquals( "remote is clean", null, remoteRegion.get(null, KEY ) );
} finally {
localSession.close();
remoteSession.close();
}
});
}
}