/*
* 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.functional;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.function.Predicate;
import org.hibernate.Session;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.cache.infinispan.util.FutureUpdate;
import org.hibernate.cache.infinispan.util.TombstoneUpdate;
import org.hibernate.cache.internal.SimpleCacheKeysFactory;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Environment;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.RootClass;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorBuilderImpl;
import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl;
import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder;
import org.hibernate.test.cache.infinispan.util.ExpectingInterceptor;
import org.hibernate.testing.BeforeClassOnce;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.hibernate.testing.junit4.CustomParameterized;
import org.hibernate.test.cache.infinispan.tm.JtaPlatformImpl;
import org.hibernate.test.cache.infinispan.tm.XaConnectionProvider;
import org.hibernate.test.cache.infinispan.util.InfinispanTestingSetup;
import org.hibernate.test.cache.infinispan.util.TestInfinispanRegionFactory;
import org.hibernate.test.cache.infinispan.util.TxUtil;
import org.infinispan.AdvancedCache;
import org.infinispan.commands.write.PutKeyValueCommand;
import org.junit.After;
import org.junit.ClassRule;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.infinispan.configuration.cache.CacheMode;
/**
* @author Galder ZamarreƱo
* @since 3.5
*/
@RunWith(CustomParameterized.class)
public abstract class AbstractFunctionalTest extends BaseNonConfigCoreFunctionalTestCase {
protected static final Object[] TRANSACTIONAL = new Object[]{"transactional", JtaPlatformImpl.class, JtaTransactionCoordinatorBuilderImpl.class, XaConnectionProvider.class, AccessType.TRANSACTIONAL, true, CacheMode.INVALIDATION_SYNC, false };
protected static final Object[] READ_WRITE_INVALIDATION = new Object[]{"read-write", null, JdbcResourceLocalTransactionCoordinatorBuilderImpl.class, null, AccessType.READ_WRITE, false, CacheMode.INVALIDATION_SYNC, false };
protected static final Object[] READ_ONLY_INVALIDATION = new Object[]{"read-only", null, JdbcResourceLocalTransactionCoordinatorBuilderImpl.class, null, AccessType.READ_ONLY, false, CacheMode.INVALIDATION_SYNC, false };
protected static final Object[] READ_WRITE_REPLICATED = new Object[]{"read-write", null, JdbcResourceLocalTransactionCoordinatorBuilderImpl.class, null, AccessType.READ_WRITE, false, CacheMode.REPL_SYNC, false };
protected static final Object[] READ_ONLY_REPLICATED = new Object[]{"read-only", null, JdbcResourceLocalTransactionCoordinatorBuilderImpl.class, null, AccessType.READ_ONLY, false, CacheMode.REPL_SYNC, false };
protected static final Object[] READ_WRITE_DISTRIBUTED = new Object[]{"read-write", null, JdbcResourceLocalTransactionCoordinatorBuilderImpl.class, null, AccessType.READ_WRITE, false, CacheMode.DIST_SYNC, false };
protected static final Object[] READ_ONLY_DISTRIBUTED = new Object[]{"read-only", null, JdbcResourceLocalTransactionCoordinatorBuilderImpl.class, null, AccessType.READ_ONLY, false, CacheMode.DIST_SYNC, false };
protected static final Object[] NONSTRICT_REPLICATED = new Object[]{"nonstrict", null, JdbcResourceLocalTransactionCoordinatorBuilderImpl.class, null, AccessType.NONSTRICT_READ_WRITE, false, CacheMode.REPL_SYNC, true };
protected static final Object[] NONSTRICT_DISTRIBUTED = new Object[]{"nonstrict", null, JdbcResourceLocalTransactionCoordinatorBuilderImpl.class, null, AccessType.NONSTRICT_READ_WRITE, false, CacheMode.DIST_SYNC, true };
// We need to use @ClassRule here since in @BeforeClassOnce startUp we're preparing the session factory,
// constructing CacheManager along - and there we check that the test has the name already set
@ClassRule
public static final InfinispanTestingSetup infinispanTestIdentifier = new InfinispanTestingSetup();
@Parameterized.Parameter(value = 0)
public String mode;
@Parameterized.Parameter(value = 1)
public Class<? extends JtaPlatform> jtaPlatformClass;
@Parameterized.Parameter(value = 2)
public Class<? extends TransactionCoordinatorBuilder> transactionCoordinatorBuilderClass;
@Parameterized.Parameter(value = 3)
public Class<? extends ConnectionProvider> connectionProviderClass;
@Parameterized.Parameter(value = 4)
public AccessType accessType;
@Parameterized.Parameter(value = 5)
public boolean useTransactionalCache;
@Parameterized.Parameter(value = 6)
public CacheMode cacheMode;
@Parameterized.Parameter(value = 7)
public boolean addVersions;
protected boolean useJta;
protected List<Runnable> cleanup = new ArrayList<>();
@CustomParameterized.Order(0)
@Parameterized.Parameters(name = "{0}, {6}")
public abstract List<Object[]> getParameters();
public List<Object[]> getParameters(boolean tx, boolean rw, boolean ro, boolean nonstrict) {
ArrayList<Object[]> parameters = new ArrayList<>();
if (tx) {
parameters.add(TRANSACTIONAL);
}
if (rw) {
parameters.add(READ_WRITE_INVALIDATION);
parameters.add(READ_WRITE_REPLICATED);
parameters.add(READ_WRITE_DISTRIBUTED);
}
if (ro) {
parameters.add(READ_ONLY_INVALIDATION);
parameters.add(READ_ONLY_REPLICATED);
parameters.add(READ_ONLY_DISTRIBUTED);
}
if (nonstrict) {
parameters.add(NONSTRICT_REPLICATED);
parameters.add(NONSTRICT_DISTRIBUTED);
}
return parameters;
}
@BeforeClassOnce
public void setUseJta() {
useJta = jtaPlatformClass != null;
}
@After
public void runCleanup() {
cleanup.forEach(Runnable::run);
cleanup.clear();
}
@Override
public String[] getMappings() {
return new String[] {
"cache/infinispan/functional/entities/Item.hbm.xml",
"cache/infinispan/functional/entities/Customer.hbm.xml",
"cache/infinispan/functional/entities/Contact.hbm.xml"
};
}
@Override
protected void afterMetadataBuilt(Metadata metadata) {
if (addVersions) {
for (PersistentClass clazz : metadata.getEntityBindings()) {
if (clazz.getVersion() != null) {
continue;
}
try {
clazz.getMappedClass().getMethod("getVersion");
clazz.getMappedClass().getMethod("setVersion", long.class);
} catch (NoSuchMethodException e) {
continue;
}
RootClass rootClazz = clazz.getRootClass();
Property versionProperty = new Property();
versionProperty.setName("version");
SimpleValue value = new SimpleValue((MetadataImplementor) metadata, rootClazz.getTable());
value.setTypeName("long");
Column column = new Column();
column.setValue(value);
column.setName("version");
value.addColumn(column);
rootClazz.getTable().addColumn(column);
versionProperty.setValue(value);
rootClazz.setVersion(versionProperty);
rootClazz.addProperty(versionProperty);
}
}
}
@Override
public String getCacheConcurrencyStrategy() {
return accessType.getExternalName();
}
protected Class<? extends RegionFactory> getRegionFactoryClass() {
return TestInfinispanRegionFactory.class;
}
protected boolean getUseQueryCache() {
return true;
}
@Override
@SuppressWarnings("unchecked")
protected void addSettings(Map settings) {
super.addSettings( settings );
settings.put( Environment.USE_SECOND_LEVEL_CACHE, "true" );
settings.put( Environment.GENERATE_STATISTICS, "true" );
settings.put( Environment.USE_QUERY_CACHE, String.valueOf( getUseQueryCache() ) );
settings.put( Environment.CACHE_REGION_FACTORY, getRegionFactoryClass().getName() );
settings.put( Environment.CACHE_KEYS_FACTORY, SimpleCacheKeysFactory.SHORT_NAME );
settings.put( TestInfinispanRegionFactory.TRANSACTIONAL, useTransactionalCache );
settings.put( TestInfinispanRegionFactory.CACHE_MODE, cacheMode);
if ( jtaPlatformClass != null ) {
settings.put( AvailableSettings.JTA_PLATFORM, jtaPlatformClass.getName() );
}
settings.put( Environment.TRANSACTION_COORDINATOR_STRATEGY, transactionCoordinatorBuilderClass.getName() );
if ( connectionProviderClass != null) {
settings.put(Environment.CONNECTION_PROVIDER, connectionProviderClass.getName());
}
}
protected void markRollbackOnly(Session session) {
TxUtil.markRollbackOnly(useJta, session);
}
protected CountDownLatch expectAfterUpdate(AdvancedCache cache, int numUpdates) {
return expectPutWithValue(cache, value -> value instanceof FutureUpdate, numUpdates);
}
protected CountDownLatch expectEvict(AdvancedCache cache, int numUpdates) {
return expectPutWithValue(cache, value -> value instanceof TombstoneUpdate && ((TombstoneUpdate) value).getValue() == null, numUpdates);
}
protected CountDownLatch expectPutWithValue(AdvancedCache cache, Predicate<Object> valuePredicate, int numUpdates) {
if (!cacheMode.isInvalidation() && accessType != AccessType.NONSTRICT_READ_WRITE) {
CountDownLatch latch = new CountDownLatch(numUpdates);
ExpectingInterceptor.get(cache)
.when((ctx, cmd) -> cmd instanceof PutKeyValueCommand && valuePredicate.test(((PutKeyValueCommand) cmd).getValue()))
.countDown(latch);
cleanup.add(() -> ExpectingInterceptor.cleanup(cache));
return latch;
} else {
return new CountDownLatch(0);
}
}
}