Isolate the transaction managers between the primary environment and the second node environment

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@14364 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Brian Stansberry 2008-02-25 23:20:08 +00:00
parent 5414f6bf99
commit e73589d1bc
9 changed files with 491 additions and 35 deletions

View File

@ -29,7 +29,6 @@ import org.hibernate.cfg.Environment;
import org.hibernate.cfg.Mappings;
import org.hibernate.dialect.Dialect;
import org.hibernate.junit.functional.FunctionalTestCase;
import org.hibernate.transaction.CMTTransactionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -68,9 +67,9 @@ public abstract class CacheTestCaseBase extends FunctionalTestCase {
cfg.setProperty(Environment.CACHE_REGION_FACTORY, getCacheRegionFactory().getName());
cfg.setProperty(Environment.USE_QUERY_CACHE, String.valueOf(getUseQueryCache()));
cfg.setProperty(Environment.CONNECTION_PROVIDER, org.hibernate.test.tm.ConnectionProviderImpl.class.getName());
cfg.setProperty(Environment.TRANSACTION_MANAGER_STRATEGY, org.hibernate.test.tm.TransactionManagerLookupImpl.class.getName());
cfg.setProperty( Environment.TRANSACTION_STRATEGY, CMTTransactionFactory.class.getName() );
cfg.setProperty(Environment.CONNECTION_PROVIDER, getConnectionProviderClass().getName());
cfg.setProperty(Environment.TRANSACTION_MANAGER_STRATEGY, getTransactionManagerLookupClass().getName());
// cfg.setProperty( Environment.TRANSACTION_STRATEGY, CMTTransactionFactory.class.getName() );
configureCacheFactory(cfg);
}
@ -89,6 +88,14 @@ public abstract class CacheTestCaseBase extends FunctionalTestCase {
protected abstract Class<? extends RegionFactory> getCacheRegionFactory();
protected abstract boolean getUseQueryCache();
protected Class getConnectionProviderClass() {
return org.hibernate.test.tm.ConnectionProviderImpl.class;
}
protected Class getTransactionManagerLookupClass() {
return org.hibernate.test.tm.TransactionManagerLookupImpl.class;
}
@Override
public void afterConfigurationBuilt(Mappings mappings, Dialect dialect) {

View File

@ -22,6 +22,10 @@ import org.hibernate.cfg.Mappings;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.junit.functional.ExecutionEnvironment;
import org.hibernate.test.cache.jbc2.functional.util.DualNodeConnectionProviderImpl;
import org.hibernate.test.cache.jbc2.functional.util.DualNodeTestUtil;
import org.hibernate.test.cache.jbc2.functional.util.DualNodeTransactionManagerLookup;
import org.hibernate.test.cache.jbc2.functional.util.TestCacheInstanceManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -35,6 +39,8 @@ public abstract class DualNodeTestCaseBase extends CacheTestCaseBase
{
private static final Logger log = LoggerFactory.getLogger( CacheTestCaseBase.class );
public static final String CACHE_MANAGER_NAME_PROP = "hibernate.test.cluster.node.id";
private ExecutionEnvironment secondNodeEnvironment;
private org.hibernate.classic.Session secondNodeSession;
@ -64,14 +70,31 @@ public abstract class DualNodeTestCaseBase extends CacheTestCaseBase
*
* @param the Configuration to update.
*/
protected abstract void configureFirstNode(Configuration cfg);
protected void configureFirstNode(Configuration cfg)
{
cfg.setProperty(DualNodeTestUtil.NODE_ID_PROP,
DualNodeTestUtil.LOCAL);
}
/**
* Apply any node-specific configurations to our second node.
*
* @param the Configuration to update.
*/
protected abstract void configureSecondNode(Configuration cfg);
protected void configureSecondNode(Configuration cfg)
{
cfg.setProperty(DualNodeTestUtil.NODE_ID_PROP,
DualNodeTestUtil.REMOTE);
}
@Override
protected Class getConnectionProviderClass() {
return DualNodeConnectionProviderImpl.class;
}
@Override
protected Class getTransactionManagerLookupClass() {
return DualNodeTransactionManagerLookup.class;
}
@Override
protected void prepareTest() throws Exception

View File

@ -34,8 +34,11 @@ import org.hibernate.SessionFactory;
import org.hibernate.cache.RegionFactory;
import org.hibernate.cache.jbc2.builder.MultiplexingCacheInstanceManager;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.test.cache.jbc2.functional.util.DualNodeTestUtil;
import org.hibernate.test.cache.jbc2.functional.util.TestCacheInstanceManager;
import org.hibernate.test.cache.jbc2.functional.util.TestJBossCacheRegionFactory;
import org.hibernate.transaction.CMTTransactionFactory;
import org.jboss.cache.Cache;
import org.jboss.cache.CacheManager;
import org.jboss.cache.Fqn;
@ -55,10 +58,7 @@ extends DualNodeTestCaseBase
{
protected final Logger log = LoggerFactory.getLogger(getClass());
private static final long SLEEP_TIME = 100l;
private static final String LOCAL = "local";
private static final String REMOTE = "remote";
private static final long SLEEP_TIME = 50l;
private static final Integer CUSTOMER_ID = new Integer(1);
@ -74,6 +74,7 @@ extends DualNodeTestCaseBase
@Override
public void configure(Configuration cfg)
{
cfg.setProperty( Environment.TRANSACTION_STRATEGY, CMTTransactionFactory.class.getName() );
super.configure(cfg);
}
@ -97,20 +98,6 @@ extends DualNodeTestCaseBase
cfg.setProperty(MultiplexingCacheInstanceManager.ENTITY_CACHE_RESOURCE_PROP,
getEntityCacheConfigName());
}
@Override
protected void configureFirstNode(Configuration cfg)
{
cfg.setProperty(TestCacheInstanceManager.CACHE_MANAGER_NAME_PROP,
LOCAL);
}
@Override
protected void configureSecondNode(Configuration cfg)
{
cfg.setProperty(TestCacheInstanceManager.CACHE_MANAGER_NAME_PROP,
REMOTE);
}
protected String getEntityCacheConfigName() {
return "pessimistic-shared";
@ -123,7 +110,7 @@ extends DualNodeTestCaseBase
// Bind a listener to the "local" cache
// Our region factory makes its CacheManager available to us
CacheManager localManager = TestCacheInstanceManager.getTestCacheManager(LOCAL);
CacheManager localManager = TestCacheInstanceManager.getTestCacheManager(DualNodeTestUtil.LOCAL);
Cache localCache = localManager.getCache(getEntityCacheConfigName(), true);
MyListener localListener = new MyListener();
localCache.addCacheListener(localListener);
@ -131,7 +118,7 @@ extends DualNodeTestCaseBase
TransactionManager localTM = localCache.getConfiguration().getRuntimeConfig().getTransactionManager();
// Bind a listener to the "remote" cache
CacheManager remoteManager = TestCacheInstanceManager.getTestCacheManager(REMOTE);
CacheManager remoteManager = TestCacheInstanceManager.getTestCacheManager(DualNodeTestUtil.REMOTE);
Cache remoteCache = remoteManager.getCache(getEntityCacheConfigName(), true);
MyListener remoteListener = new MyListener();
remoteCache.addCacheListener(remoteListener);
@ -154,7 +141,9 @@ extends DualNodeTestCaseBase
// This actually brings the collection into the cache
getCustomer(ids.customerId, localFactory, localTM);
// Now the collection is in the cache so, we the 2nd "get"
sleep(SLEEP_TIME);
// Now the collection is in the cache so, the 2nd "get"
// should read everything from the cache
System.out.println("Find(2) node 0");
localListener.clear();
@ -163,10 +152,6 @@ extends DualNodeTestCaseBase
//Check the read came from the cache
System.out.println("Check cache 0");
assertLoadedFromCache(localListener, ids.customerId, ids.contactIds);
// The above placement of the collection in the cache is replicated async
// so pause a bit before checking node 1
sleep(SLEEP_TIME);
System.out.println("Find node 1");
getCustomer(ids.customerId, remoteFactory, remoteTM);

View File

@ -0,0 +1,86 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2007, Red Hat Middleware LLC or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors.  All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.cache.jbc2.functional.util;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import org.hibernate.HibernateException;
import org.hibernate.connection.ConnectionProvider;
import org.hibernate.connection.ConnectionProviderFactory;
/**
* A {@link ConnectionProvider} implementation adding JTA-style transactionality
* around the returned connections using the {@link DualNodeJtaTransactionManagerImpl}.
*
* @author Brian Stansberry
*/
public class DualNodeConnectionProviderImpl implements ConnectionProvider {
private static ConnectionProvider actualConnectionProvider = ConnectionProviderFactory.newConnectionProvider();
private String nodeId;
private boolean isTransactional;
public static ConnectionProvider getActualConnectionProvider() {
return actualConnectionProvider;
}
public void configure(Properties props) throws HibernateException {
nodeId = props.getProperty(DualNodeTestUtil.NODE_ID_PROP);
if (nodeId == null)
throw new HibernateException(DualNodeTestUtil.NODE_ID_PROP + " not configured");
}
public Connection getConnection() throws SQLException {
DualNodeJtaTransactionImpl currentTransaction = DualNodeJtaTransactionManagerImpl.getInstance(nodeId).getCurrentTransaction();
if ( currentTransaction == null ) {
isTransactional = false;
return actualConnectionProvider.getConnection();
}
else {
isTransactional = true;
Connection connection = currentTransaction.getEnlistedConnection();
if ( connection == null ) {
connection = actualConnectionProvider.getConnection();
currentTransaction.enlistConnection( connection );
}
return connection;
}
}
public void closeConnection(Connection conn) throws SQLException {
if ( !isTransactional ) {
conn.close();
}
}
public void close() throws HibernateException {
actualConnectionProvider.close();
}
public boolean supportsAggressiveRelease() {
return true;
}
}

View File

@ -0,0 +1,160 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2007, Red Hat Middleware LLC or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors.  All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.cache.jbc2.functional.util;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.LinkedList;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.RollbackException;
import javax.transaction.Status;
import javax.transaction.Synchronization;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.xa.XAResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* SimpleJtaTransactionImpl variant that works with DualNodeTransactionManagerImpl.
*
* @author Brian Stansberry
*/
public class DualNodeJtaTransactionImpl implements Transaction {
private static final Logger log = LoggerFactory.getLogger( DualNodeJtaTransactionImpl.class );
private int status;
private LinkedList synchronizations;
private Connection connection; // the only resource we care about is jdbc connection
private final DualNodeJtaTransactionManagerImpl jtaTransactionManager;
public DualNodeJtaTransactionImpl(DualNodeJtaTransactionManagerImpl jtaTransactionManager) {
this.jtaTransactionManager = jtaTransactionManager;
this.status = Status.STATUS_ACTIVE;
}
public int getStatus() {
return status;
}
public void commit()
throws RollbackException, HeuristicMixedException, HeuristicRollbackException, IllegalStateException, SystemException {
if ( status == Status.STATUS_MARKED_ROLLBACK ) {
log.trace( "on commit, status was marked for rollback-only" );
rollback();
}
else {
status = Status.STATUS_PREPARING;
for ( int i = 0; i < synchronizations.size(); i++ ) {
Synchronization s = ( Synchronization ) synchronizations.get( i );
s.beforeCompletion();
}
status = Status.STATUS_COMMITTING;
if ( connection != null ) {
try {
connection.commit();
connection.close();
}
catch ( SQLException sqle ) {
status = Status.STATUS_UNKNOWN;
throw new SystemException();
}
}
status = Status.STATUS_COMMITTED;
for ( int i = 0; i < synchronizations.size(); i++ ) {
Synchronization s = ( Synchronization ) synchronizations.get( i );
s.afterCompletion( status );
}
//status = Status.STATUS_NO_TRANSACTION;
jtaTransactionManager.endCurrent( this );
}
}
public void rollback() throws IllegalStateException, SystemException {
status = Status.STATUS_ROLLEDBACK;
if ( connection != null ) {
try {
connection.rollback();
connection.close();
}
catch ( SQLException sqle ) {
status = Status.STATUS_UNKNOWN;
throw new SystemException();
}
}
for ( int i = 0; i < synchronizations.size(); i++ ) {
Synchronization s = ( Synchronization ) synchronizations.get( i );
s.afterCompletion( status );
}
//status = Status.STATUS_NO_TRANSACTION;
jtaTransactionManager.endCurrent( this );
}
public void setRollbackOnly() throws IllegalStateException, SystemException {
status = Status.STATUS_MARKED_ROLLBACK;
}
public void registerSynchronization(Synchronization synchronization)
throws RollbackException, IllegalStateException, SystemException {
// todo : find the spec-allowable statuses during which synch can be registered...
if ( synchronizations == null ) {
synchronizations = new LinkedList();
}
synchronizations.add( synchronization );
}
public void enlistConnection(Connection connection) {
if ( this.connection != null ) {
throw new IllegalStateException( "Connection already registered" );
}
this.connection = connection;
}
public Connection getEnlistedConnection() {
return connection;
}
public boolean enlistResource(XAResource xaResource)
throws RollbackException, IllegalStateException, SystemException {
return false;
}
public boolean delistResource(XAResource xaResource, int i) throws IllegalStateException, SystemException {
return false;
}
}

View File

@ -0,0 +1,115 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2007, Red Hat Middleware LLC or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors.  All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.cache.jbc2.functional.util;
import java.util.Hashtable;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.InvalidTransactionException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.Status;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
/**
* Variant of SimpleJtaTransactionManagerImpl that doesn't use a VM-singleton,
* but rather a set of impls keyed by a node id.
*
* @author Brian Stansberry
*/
public class DualNodeJtaTransactionManagerImpl implements TransactionManager {
private static final Hashtable INSTANCES = new Hashtable();
private DualNodeJtaTransactionImpl currentTransaction;
public synchronized static DualNodeJtaTransactionManagerImpl getInstance(String nodeId) {
DualNodeJtaTransactionManagerImpl tm = (DualNodeJtaTransactionManagerImpl) INSTANCES.get(nodeId);
if (tm == null) {
tm = new DualNodeJtaTransactionManagerImpl();
INSTANCES.put(nodeId, tm);
}
return tm;
}
public int getStatus() throws SystemException {
return currentTransaction == null ? Status.STATUS_NO_TRANSACTION : currentTransaction.getStatus();
}
public Transaction getTransaction() throws SystemException {
return currentTransaction;
}
public DualNodeJtaTransactionImpl getCurrentTransaction() {
return currentTransaction;
}
public void begin() throws NotSupportedException, SystemException {
currentTransaction = new DualNodeJtaTransactionImpl( this );
}
public Transaction suspend() throws SystemException {
DualNodeJtaTransactionImpl suspended = currentTransaction;
currentTransaction = null;
return suspended;
}
public void resume(Transaction transaction)
throws InvalidTransactionException, IllegalStateException, SystemException {
currentTransaction = ( DualNodeJtaTransactionImpl ) transaction;
}
public void commit()
throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException {
if ( currentTransaction == null ) {
throw new IllegalStateException( "no current transaction to commit" );
}
currentTransaction.commit();
}
public void rollback() throws IllegalStateException, SecurityException, SystemException {
if ( currentTransaction == null ) {
throw new IllegalStateException( "no current transaction" );
}
currentTransaction.rollback();
}
public void setRollbackOnly() throws IllegalStateException, SystemException {
if ( currentTransaction == null ) {
throw new IllegalStateException( "no current transaction" );
}
currentTransaction.setRollbackOnly();
}
public void setTransactionTimeout(int i) throws SystemException {
}
void endCurrent(DualNodeJtaTransactionImpl transaction) {
if ( transaction == currentTransaction ) {
currentTransaction = null;
}
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2007, Red Hat Middleware, LLC. All rights reserved.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, v. 2.1. This program is distributed in the
* hope that it will be useful, but WITHOUT A WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. You should have received a
* copy of the GNU Lesser General Public License, v.2.1 along with this
* distribution; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Red Hat Author(s): Brian Stansberry
*/
package org.hibernate.test.cache.jbc2.functional.util;
/**
* A DualNodeTestUtil.
*
* @author <a href="brian.stansberry@jboss.com">Brian Stansberry</a>
* @version $Revision: 1 $
*/
public class DualNodeTestUtil
{
public static final String NODE_ID_PROP = "hibernate.test.cluster.node.id";
public static final String LOCAL = "local";
public static final String REMOTE = "remote";
}

View File

@ -0,0 +1,51 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2007, Red Hat Middleware LLC or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors.  All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.cache.jbc2.functional.util;
import java.util.Properties;
import javax.transaction.TransactionManager;
import org.hibernate.transaction.TransactionManagerLookup;
import org.hibernate.HibernateException;
/**
* SimpleJtaTransactionManagerLookupImpl subclass that finds a different
* DualNodeTransactionManager based on the value of property
* {@link DualNodeTestUtil#NODE_ID_PROP}.
*
* @author Brian Stansberry
*/
public class DualNodeTransactionManagerLookup implements TransactionManagerLookup {
public TransactionManager getTransactionManager(Properties props) throws HibernateException {
String nodeId = props.getProperty(DualNodeTestUtil.NODE_ID_PROP);
if (nodeId == null)
throw new HibernateException(DualNodeTestUtil.NODE_ID_PROP + " not configured");
return DualNodeJtaTransactionManagerImpl.getInstance(nodeId);
}
public String getUserTransactionName() {
throw new UnsupportedOperationException( "jndi currently not implemented for these tests" );
}
}

View File

@ -43,8 +43,6 @@ public class TestCacheInstanceManager extends MultiplexingCacheInstanceManager {
private static final Hashtable cacheManagers = new Hashtable();
public static final String CACHE_MANAGER_NAME_PROP = "hibernate.test.cache.jbc2.cache.manager.name";
public static CacheManager getTestCacheManager(String name) {
return (CacheManager) cacheManagers.get(name);
}
@ -63,7 +61,7 @@ public class TestCacheInstanceManager extends MultiplexingCacheInstanceManager {
super.start(settings, properties);
cacheManagerName = properties.getProperty(CACHE_MANAGER_NAME_PROP);
cacheManagerName = properties.getProperty(DualNodeTestUtil.NODE_ID_PROP);
cacheManagers.put(cacheManagerName, getCacheFactory());
}