package org.hibernate.test.cache.infinispan.functional.cluster;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cache.spi.NaturalIdCacheKey;
import org.hibernate.criterion.Restrictions;
import org.hibernate.test.cache.infinispan.functional.Citizen;
import org.hibernate.test.cache.infinispan.functional.NaturalIdOnManyToOne;
import org.hibernate.test.cache.infinispan.functional.State;
import org.infinispan.Cache;
import org.infinispan.manager.CacheContainer;
import org.infinispan.notifications.Listener;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryVisited;
import org.infinispan.notifications.cachelistener.event.CacheEntryVisitedEvent;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import org.jboss.util.collection.ConcurrentSet;
import org.junit.After;
import org.junit.Test;
import javax.transaction.TransactionManager;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import static org.infinispan.test.TestingUtil.tmpDirectory;
import static org.infinispan.test.TestingUtil.withTx;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* // TODO: Document this
*
* @author Galder ZamarreƱo
* @since // TODO
*/
public class NaturalIdInvalidationTestCase extends DualNodeTestCase {
private static final Log log = LogFactory.getLog(NaturalIdInvalidationTestCase.class);
private static final long SLEEP_TIME = 50l;
private static final Integer CUSTOMER_ID = new Integer( 1 );
private static int test = 0;
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] {
Citizen.class, State.class,
NaturalIdOnManyToOne.class
};
}
@Test
public void testAll() throws Exception {
log.info( "*** testAll()" );
// Bind a listener to the "local" cache
// Our region factory makes its CacheManager available to us
CacheContainer localManager = ClusterAwareRegionFactory.getCacheManager(DualNodeTestCase.LOCAL);
Cache localNaturalIdCache = localManager.getCache(Citizen.class.getName() + "##NaturalId");
MyListener localListener = new MyListener( "local" );
localNaturalIdCache.addListener(localListener);
TransactionManager localTM = DualNodeJtaTransactionManagerImpl.getInstance(DualNodeTestCase.LOCAL);
// Bind a listener to the "remote" cache
CacheContainer remoteManager = ClusterAwareRegionFactory.getCacheManager(DualNodeTestCase.REMOTE);
Cache remoteNaturalIdCache = remoteManager.getCache(Citizen.class.getName() + "##NaturalId");
MyListener remoteListener = new MyListener( "remote" );
remoteNaturalIdCache.addListener(remoteListener);
TransactionManager remoteTM = DualNodeJtaTransactionManagerImpl.getInstance(DualNodeTestCase.REMOTE);
SessionFactory localFactory = sessionFactory();
SessionFactory remoteFactory = secondNodeEnvironment().getSessionFactory();
try {
assertTrue(remoteListener.isEmpty());
assertTrue(localListener.isEmpty());
saveSomeCitizens(localTM, localFactory);
assertTrue(remoteListener.isEmpty());
assertTrue(localListener.isEmpty());
// Sleep a bit to let async commit propagate. Really just to
// help keep the logs organized for debugging any issues
sleep( SLEEP_TIME );
log.debug("Find node 0");
// This actually brings the collection into the cache
getCitizenWithCriteria(localTM, localFactory);
sleep( SLEEP_TIME );
// Now the collection is in the cache so, the 2nd "get"
// should read everything from the cache
log.debug( "Find(2) node 0" );
localListener.clear();
getCitizenWithCriteria(localTM, localFactory);
// Check the read came from the cache
log.debug( "Check cache 0" );
assertLoadedFromCache(localListener, "1234");
log.debug( "Find node 1" );
// This actually brings the collection into the cache since invalidation is in use
getCitizenWithCriteria(remoteTM, remoteFactory);
// Now the collection is in the cache so, the 2nd "get"
// should read everything from the cache
log.debug( "Find(2) node 1" );
remoteListener.clear();
getCitizenWithCriteria(remoteTM, remoteFactory);
// Check the read came from the cache
log.debug( "Check cache 1" );
assertLoadedFromCache(remoteListener, "1234");
// Modify customer in remote
remoteListener.clear();
deleteCitizenWithCriteria(remoteTM, remoteFactory);
sleep(250);
Set localKeys = localNaturalIdCache.keySet();
assertEquals(1, localKeys.size());
// Only key left is the one for the citizen *not* in France
localKeys.toString().contains("000");
}
catch (Exception e) {
log.error("Error", e);
throw e;
} finally {
withTx(localTM, new Callable<Void>() {
@Override
public Void call() throws Exception {
Session s = sessionFactory().openSession();
s.beginTransaction();
s.createQuery( "delete NaturalIdOnManyToOne" ).executeUpdate();
s.createQuery( "delete Citizen" ).executeUpdate();
s.createQuery( "delete State" ).executeUpdate();
s.getTransaction().commit();
s.close();
return null;
}
});
}
}
private void assertLoadedFromCache(MyListener localListener, String id) {
for (String visited : localListener.visited){
if (visited.contains(id))
return;
}
fail("Citizen (" + id + ") should have present in the cache");
}
private void saveSomeCitizens(TransactionManager tm, final SessionFactory sf) throws Exception {
final Citizen c1 = new Citizen();
c1.setFirstname( "Emmanuel" );
c1.setLastname( "Bernard" );
c1.setSsn( "1234" );
final State france = new State();
france.setName( "Ile de France" );
c1.setState( france );
final Citizen c2 = new Citizen();
c2.setFirstname( "Gavin" );
c2.setLastname( "King" );
c2.setSsn( "000" );
final State australia = new State();
australia.setName( "Australia" );
c2.setState( australia );
withTx(tm, new Callable<Void>() {
@Override
public Void call() throws Exception {
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
s.persist( australia );
s.persist( france );
s.persist( c1 );
s.persist( c2 );
tx.commit();
s.close();
return null;
}
});
}
private void getCitizenWithCriteria(TransactionManager tm, final SessionFactory sf) throws Exception {
withTx(tm, new Callable<Void >() {
@Override
public Void call() throws Exception {
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
State france = getState(s, "Ile de France");
Criteria criteria = s.createCriteria( Citizen.class );
criteria.add( Restrictions.naturalId().set( "ssn", "1234" ).set( "state", france ) );
criteria.setCacheable( true );
criteria.list();
// cleanup
tx.commit();
s.close();
return null;
}
});
}
private void deleteCitizenWithCriteria(TransactionManager tm, final SessionFactory sf) throws Exception {
withTx(tm, new Callable<Void >() {
@Override
public Void call() throws Exception {
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
State france = getState(s, "Ile de France");
Criteria criteria = s.createCriteria( Citizen.class );
criteria.add( Restrictions.naturalId().set( "ssn", "1234" ).set( "state", france ) );
criteria.setCacheable( true );
Citizen c = (Citizen) criteria.uniqueResult();
s.delete(c);
// cleanup
tx.commit();
s.close();
return null;
}
});
}
private State getState(Session s, String name) {
Criteria criteria = s.createCriteria( State.class );
criteria.add( Restrictions.eq("name", name) );
criteria.setCacheable(true);
return (State) criteria.list().get( 0 );
}
@Listener
public static class MyListener {
private static final Log log = LogFactory.getLog( MyListener.class );
private Set<String> visited = new ConcurrentSet<String>();
private final String name;
public MyListener(String name) {
this.name = name;
}
public void clear() {
visited.clear();
}
public boolean isEmpty() {
return visited.isEmpty();
}
@CacheEntryVisited
public void nodeVisited(CacheEntryVisitedEvent event) {
log.debug( event.toString() );
if ( !event.isPre() ) {
NaturalIdCacheKey cacheKey = (NaturalIdCacheKey) event.getKey();
visited.add(cacheKey.toString());
// Integer primKey = (Integer) cacheKey.getKey();
// String key = (String) cacheKey.getEntityOrRoleName() + '#' + primKey;
// log.debug( "MyListener[" + name + "] - Visiting key " + key );
// // String name = fqn.toString();
// String token = ".functional.";
// int index = key.indexOf( token );
// if ( index > -1 ) {
// index += token.length();
// key = key.substring( index );
// log.debug( "MyListener[" + name + "] - recording visit to " + key );
// visited.add( key );
// }
}
}
}
}