Recently I decided to deepen my investigation upon Spring WebSphereUowTransactionManager. I always thought it was a shame Hibernate wasn’t fully supported under WebSphere and decided to investigate it a little more since I had the impression that after my first investigation that resulted in my first attempt of extending current hibernate transaction lookup for WebSphere it would take only a little more effort.
Considerations for the Implementation
The first decision taken for the implementation was that it should have no compile-time dependency upon WebSphere as Hibernate original TransactionManager and TransactionManagerLookup. This resulted in having all the calls to UOWManager and UOWSynchronizationRegistry using reflection and hence we needed to pass a UOWAction instance to UOWManager.runUnderUOW I chose to use a Java Dynamic Proxy.
Changes to Hibernate and Implementation
Since I didn’t want to embed another inner class into Isolater.java I made the Delegate interface public instead of private. The last trick was to return my delegate in place of JtaDelegate when configured for WebSphere. I sincerely feel like a factory was a better option over here but I didn’t take any time to investigate where to plug it. The resulting code for Isolater is show below:
public static void doIsolatedWork(IsolatedWork work, SessionImplementor session) throws HibernateException {
boolean isJta = session.getFactory().getTransactionManager() != null;
if ( isJta ) {
if (session.getFactory().getTransactionManager() instanceof UOWTransactionManagerAdapter)
new WebSphereUOWDelegate(session).delegateWork(work, true);
else
new JtaDelegate( session ).delegateWork( work, true );
}
else {
new JdbcDelegate( session ).delegateWork( work, true );
}
}
public static void doNonTransactedWork(IsolatedWork work, SessionImplementor session) throws HibernateException {
boolean isJta = session.getFactory().getTransactionManager() != null;
if ( isJta ) {
if (session.getFactory().getTransactionManager() instanceof UOWTransactionManagerAdapter)
new WebSphereUOWDelegate(session).delegateWork(work, false);
else
new JtaDelegate( session ).delegateWork( work, false );
}
else {
new JdbcDelegate( session ).delegateWork( work, false );
}
}
public static interface Delegate {
public void delegateWork(IsolatedWork work, boolean transacted) throws HibernateException;
}
The resulting Isolater.Delegate implementation for WebSphere delegates the doTheWork to a method on UOWTransactionManagerAdapter that creates the UOWAction proxy and carries it on to UOWManager.
private void doTheWork(IsolatedWork work, UOWTransactionManagerAdapter transactionManager, boolean transacted) {
try {
// obtain our isolated connection
Connection connection = session.getFactory().getConnectionProvider().getConnection();
try {
// do the actual work
if (transacted)
transactionManager.doIsolatedWork(work, connection, false, true);
else
transactionManager.doIsolatedWork(work, connection, false, false);
}
catch ( HibernateException e ) {
throw e;
}
catch ( Exception e ) {
throw new HibernateException( "Unable to perform isolated work", e );
}
finally {
try {
// no matter what, release the connection (handle)
session.getFactory().getConnectionProvider().closeConnection( connection );
}
catch ( Throwable ignore ) {
log.info( "Unable to release isolated connection [" + ignore + "]" );
}
}
}
catch ( SQLException sqle ) {
throw JDBCExceptionHelper.convert(
sqlExceptionConverter(),
sqle,
"unable to obtain isolated JDBC connection"
);
}
}
A class implementing InvocationHandler is responsible for delegating the UOWAction implementation to the Hibernate IsolatedWork
static class UOWInvocationHandler implements InvocationHandler {
private IsolatedWork work;
private Connection connection;
public UOWInvocationHandler(IsolatedWork work, Connection connection) {
super();
this.work = work;
this.connection = connection;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
work.doWork(connection);
return void.class;
}
}
Finally we have the doIsolatedWork method on UOWTransactionManagerAdapter
public void doIsolatedWork(IsolatedWork work, Connection conn, boolean join, boolean global) throws SystemException, InvalidTransactionException, IllegalStateException {
Object p = Proxy.newProxyInstance
(uowActionClass.getClassLoader(),
new Class[] { uowActionClass }, new UOWInvocationHandler(work, conn));
try {
runUnderUOWMethod.invoke(uowManager, new Object[]{global ? UOW_TYPE_GLOBAL_TRANSACTION : UOW_TYPE_LOCAL_TRANSACTION, Boolean.FALSE.booleanValue(),p});
} catch (IllegalArgumentException e) {
throw new HibernateException(e);
} catch (IllegalAccessException e) {
throw new HibernateException(e);
} catch (InvocationTargetException e) {
throw (HibernateException) e.getTargetException().getCause();
}
}
I still need to develop a test application for the Isolater.doNonTransactedWork method… if anyone knows how Hibernate triggers that flow and is willing to give a try, please let me know on the comments.
With a patched Hibernate you only need to configure the hibernate.cfg.xml with the following transaction.manager_lookup_class:
<property name="hibernate.transaction.manager_lookup_class">
org.hibernate.transaction.WebSphereUOWTransactionLookup
</property>
I am providing a Diff for anyone willing to patch Hibernate and testing this implementation.
diff -r -d -N src/org/hibernate/engine/transaction/Isolater.java ..\..\hib-orig\src/org/hibernate/engine/transaction/Isolater.java
40d39
< import org.hibernate.transaction.WebSphereUOWTransactionLookup.UOWTransactionManagerAdapter;
68,71c67
< if (session.getFactory().getTransactionManager() instanceof UOWTransactionManagerAdapter)
< new WebSphereUOWDelegate(session).delegateWork(work, true);
< else
< new JtaDelegate( session ).delegateWork( work, true );
---
> new JtaDelegate( session ).delegateWork( work, true );
89,92c85
< if (session.getFactory().getTransactionManager() instanceof UOWTransactionManagerAdapter)
< new WebSphereUOWDelegate(session).delegateWork(work, false);
< else
< new JtaDelegate( session ).delegateWork( work, false );
---
> new JtaDelegate( session ).delegateWork( work, false );
103c96
< public static interface Delegate {
---
> private static interface Delegate {
diff -r -d -N src/org/hibernate/engine/transaction/WebSphereUOWDelegate.java ..\..\hib-orig\src/org/hibernate/engine/transaction/WebSphereUOWDelegate.java
1,89d0
< package org.hibernate.engine.transaction;
<
< import java.sql.Connection;
< import java.sql.SQLException;
<
< import org.hibernate.HibernateException;
< import org.hibernate.engine.SessionImplementor;
< import org.hibernate.engine.transaction.Isolater.Delegate;
< import org.hibernate.exception.JDBCExceptionHelper;
< import org.hibernate.exception.SQLExceptionConverter;
< import org.hibernate.transaction.WebSphereUOWTransactionLookup.UOWTransactionManagerAdapter;
< import org.slf4j.Logger;
< import org.slf4j.LoggerFactory;
<
< /**
< * An isolation delegate for WebSphere UOW-based transactions. Essentially calls UOW for
< * suspending any current transaction, does the work in a new transaction, and then
< * resumes the initial transaction (if there was one).
< */
< public class WebSphereUOWDelegate implements Delegate {
< private static final Logger log = LoggerFactory.getLogger( Isolater.class );
<
< private final SessionImplementor session;
<
< public WebSphereUOWDelegate(SessionImplementor session) {
< this.session = session;
< }
<
< public void delegateWork(IsolatedWork work, boolean transacted) throws HibernateException {
< UOWTransactionManagerAdapter transactionManager = (UOWTransactionManagerAdapter) session.getFactory().getTransactionManager();
<
< // then peform the requested work
< if ( transacted ) {
< doTheWorkInNewTransaction( work, transactionManager );
< }
< else {
< doTheWorkInNoTransaction( work, transactionManager);
< }
< }
<
< private void doTheWorkInNewTransaction(IsolatedWork work, UOWTransactionManagerAdapter transactionManager) {
< doTheWork( work , transactionManager, true);
< }
<
< private void doTheWorkInNoTransaction(IsolatedWork work, UOWTransactionManagerAdapter transactionManager) {
< doTheWork( work , transactionManager, false);
< }
<
< private void doTheWork(IsolatedWork work, UOWTransactionManagerAdapter transactionManager, boolean transacted) {
< try {
< // obtain our isolated connection
< Connection connection = session.getFactory().getConnectionProvider().getConnection();
< try {
< // do the actual work
< if (transacted)
< transactionManager.doIsolatedWork(work, connection, false, true);
< else
< transactionManager.doIsolatedWork(work, connection, false, false);
< }
< catch ( HibernateException e ) {
< throw e;
< }
< catch ( Exception e ) {
< throw new HibernateException( "Unable to perform isolated work", e );
< }
< finally {
< try {
< // no matter what, release the connection (handle)
< session.getFactory().getConnectionProvider().closeConnection( connection );
< }
< catch ( Throwable ignore ) {
< log.info( "Unable to release isolated connection [" + ignore + "]" );
< }
< }
< }
< catch ( SQLException sqle ) {
< throw JDBCExceptionHelper.convert(
< sqlExceptionConverter(),
< sqle,
< "unable to obtain isolated JDBC connection"
< );
< }
< }
<
< private SQLExceptionConverter sqlExceptionConverter() {
< return session.getFactory().getSQLExceptionConverter();
< }
<
< }
diff -r -d -N src/org/hibernate/transaction/WebSphereUOWTransactionLookup.java ..\..\hib-orig\src/org/hibernate/transaction/WebSphereUOWTransactionLookup.java
1,304d0
< package org.hibernate.transaction;
<
< import java.lang.reflect.InvocationHandler;
< import java.lang.reflect.InvocationTargetException;
< import java.lang.reflect.Method;
< import java.lang.reflect.Proxy;
< import java.sql.Connection;
< import java.util.Properties;
<
< import javax.naming.NamingException;
< 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.Synchronization;
< import javax.transaction.SystemException;
< import javax.transaction.Transaction;
< import javax.transaction.TransactionManager;
< import javax.transaction.xa.XAResource;
<
< import org.hibernate.HibernateException;
< import org.hibernate.engine.transaction.IsolatedWork;
< import org.hibernate.transaction.WebSphereUOWTransactionLookup.UOWTransactionManagerAdapter.UOWTransactionAdapter;
< import org.hibernate.util.NamingHelper;
<
< /**
< * @author rafaelri Support for proprietary interfaces for registering
< * synchronizations in WebSphere 6.1+
< */
< public class WebSphereUOWTransactionLookup implements TransactionManagerLookup {
<
< private static final String UOW_MANAGER_JNDI = "java:comp/websphere/UOWManager";
< private static final String UOW_SYNCHRONIZATION_MANAGER_JNDI = "java:comp/websphere/UOWSynchronizationRegistry";
<
< public TransactionManager getTransactionManager(Properties props)
< throws HibernateException {
< return new UOWTransactionManagerAdapter(props);
< }
<
< public String getUserTransactionName() {
< return "java:comp/UserTransaction";
< }
<
< public static class UOWTransactionManagerAdapter implements TransactionManager {
< private final Object uowManager;
< private final Class uowManagerClass;
< private final Object uowSynchronizationRegistry;
< private final Class uowSynchronizationRegistryClass;
< private final Class uowActionClass;
< private final Method registerSynchronizationMethod;
< private final Method setRollbackOnlyMethod;
< private final Method setUOWTimeoutMethod;
< private final Method getUOWTimeoutMethod;
< private final Method runUnderUOWMethod;
< public final int UOW_TYPE_GLOBAL_TRANSACTION;
< public final int UOW_TYPE_LOCAL_TRANSACTION;
<
< private final Properties properties;
< private final Method getLocalIdMethod;
<
< private UOWTransactionManagerAdapter(Properties props) {
< this.properties = props;
< try {
< uowManager = NamingHelper.getInitialContext(props).lookup(
< UOW_MANAGER_JNDI);
< uowManagerClass = Class.forName("com.ibm.wsspi.uow.UOWManager");
< setRollbackOnlyMethod = uowManagerClass.getMethod(
< "setRollbackOnly", new Class[] {});
< uowActionClass = Class.forName("com.ibm.wsspi.uow.UOWAction");
< runUnderUOWMethod = uowManagerClass.getMethod("runUnderUOW",
< new Class[] {int.class, boolean.class, uowActionClass});
< setUOWTimeoutMethod = uowManagerClass.getMethod(
< "setUOWTimeout", new Class[] { int.class, int.class });
< getUOWTimeoutMethod = uowManagerClass.getMethod(
< "getUOWTimeout", new Class[] {});
<
< uowSynchronizationRegistry = NamingHelper.getInitialContext(
< props).lookup(UOW_SYNCHRONIZATION_MANAGER_JNDI);
< uowSynchronizationRegistryClass = Class
< .forName("com.ibm.websphere.uow.UOWSynchronizationRegistry");
<
< UOW_TYPE_LOCAL_TRANSACTION = uowSynchronizationRegistryClass.getDeclaredField("UOW_TYPE_LOCAL_TRANSACTION").getInt(null);
<
< UOW_TYPE_GLOBAL_TRANSACTION = uowSynchronizationRegistryClass.getDeclaredField("UOW_TYPE_GLOBAL_TRANSACTION").getInt(null);
<
< registerSynchronizationMethod = uowSynchronizationRegistryClass
< .getMethod("registerInterposedSynchronization",
< new Class[] { Synchronization.class });
< getLocalIdMethod = uowSynchronizationRegistryClass.getMethod(
< "getLocalUOWId", new Class[] {});
<
< } catch (ClassNotFoundException cnfe) {
< throw new HibernateException(cnfe);
< } catch (NoSuchMethodException nsme) {
< throw new HibernateException(nsme);
< } catch (NamingException ne) {
< throw new HibernateException(ne);
< } catch (IllegalArgumentException e) {
< throw new HibernateException(e);
< } catch (SecurityException e) {
< throw new HibernateException(e);
< } catch (IllegalAccessException e) {
< throw new HibernateException(e);
< } catch (NoSuchFieldException e) {
< throw new HibernateException(e);
< }
< }
<
< public void doIsolatedWork(IsolatedWork work, Connection conn, boolean join, boolean global) throws SystemException, InvalidTransactionException, IllegalStateException {
< Object p = Proxy.newProxyInstance
< (uowActionClass.getClassLoader(),
< new Class[] { uowActionClass }, new UOWInvocationHandler(work, conn));
< try {
< runUnderUOWMethod.invoke(uowManager, new Object[]{global ? UOW_TYPE_GLOBAL_TRANSACTION : UOW_TYPE_LOCAL_TRANSACTION, Boolean.FALSE.booleanValue(),p});
< } catch (IllegalArgumentException e) {
< throw new HibernateException(e);
< } catch (IllegalAccessException e) {
< throw new HibernateException(e);
< } catch (InvocationTargetException e) {
< throw (HibernateException) e.getTargetException().getCause();
< }
< }
<
< public void begin() throws NotSupportedException, SystemException {
< throw new UnsupportedOperationException();
< }
<
< public void commit() throws RollbackException, HeuristicMixedException,
< HeuristicRollbackException, SecurityException,
< IllegalStateException, SystemException {
< throw new UnsupportedOperationException();
< }
<
< public int getStatus() throws SystemException {
< throw new UnsupportedOperationException();
< }
<
< public Transaction getTransaction() throws SystemException {
< return new UOWTransactionAdapter(properties);
< }
<
< public void resume(Transaction txn) throws InvalidTransactionException,
< IllegalStateException, SystemException {
< throw new UnsupportedOperationException();
< }
<
< public void rollback() throws IllegalStateException, SecurityException,
< SystemException {
< throw new UnsupportedOperationException();
< }
<
< public void setRollbackOnly() throws IllegalStateException,
< SystemException {
< try {
< setRollbackOnlyMethod.invoke(uowManager, new Object[] {});
< } catch (IllegalArgumentException e) {
< throw new RuntimeException(e);
< } catch (IllegalAccessException e) {
< throw new RuntimeException(e);
< } catch (InvocationTargetException e) {
< // is there any way to better handle this???
< if (e.getTargetException() instanceof IllegalStateException)
< throw ((IllegalStateException) e.getTargetException());
< else if (e.getTargetException() instanceof SystemException)
< throw ((SystemException) e.getTargetException());
< else
< throw new RuntimeException(e.getTargetException());
< }
< }
<
< public void setTransactionTimeout(int i) throws SystemException {
< try {
< setUOWTimeoutMethod.invoke(uowManager, new Object[] {
< UOW_TYPE_GLOBAL_TRANSACTION,
< i });
< } catch (IllegalArgumentException e) {
< throw new RuntimeException(e);
< } catch (IllegalAccessException e) {
< throw new RuntimeException(e);
< } catch (InvocationTargetException e) {
< // is there any way to better handle this???
< if (e.getTargetException() instanceof IllegalStateException)
< throw ((IllegalStateException) e.getTargetException());
< else if (e.getTargetException() instanceof SystemException)
< throw ((SystemException) e.getTargetException());
< else
< throw new RuntimeException(e.getTargetException());
< }
< }
<
< public Transaction suspend() throws SystemException {
< throw new UnsupportedOperationException();
< }
<
< public class UOWTransactionAdapter implements Transaction {
<
< private Object localId;
<
< private UOWTransactionAdapter(Properties props) {
< try {
< localId = getLocalIdMethod.invoke(
< uowSynchronizationRegistry, new Object[] {});
< } catch (Exception e) {
< throw new HibernateException(e);
< }
< }
<
< public void registerSynchronization(
< final Synchronization synchronization)
< throws RollbackException, IllegalStateException,
< SystemException {
<
< try {
< registerSynchronizationMethod.invoke(
< uowSynchronizationRegistry,
< new Object[] { synchronization });
< } catch (Exception e) {
< throw new HibernateException(e);
< }
<
< }
<
< public int hashCode() {
< return getLocalId().hashCode();
< }
<
< public boolean equals(Object other) {
< if (!(other instanceof UOWTransactionAdapter))
< return false;
< UOWTransactionAdapter that = (UOWTransactionAdapter) other;
< return getLocalId().equals(that.getLocalId());
< }
<
< private Object getLocalId() {
< return localId;
< }
<
< public void commit() throws RollbackException,
< HeuristicMixedException, HeuristicRollbackException,
< SecurityException, IllegalStateException, SystemException {
< throw new UnsupportedOperationException();
< }
<
< public boolean delistResource(XAResource resource, int i)
< throws IllegalStateException, SystemException {
< throw new UnsupportedOperationException();
< }
<
< public boolean enlistResource(XAResource resource)
< throws RollbackException, IllegalStateException,
< SystemException {
< throw new UnsupportedOperationException();
< }
<
< public int getStatus() throws SystemException {
< return new Integer(0).equals(getLocalId()) ? Status.STATUS_NO_TRANSACTION
< : Status.STATUS_ACTIVE;
< }
<
< public void rollback() throws IllegalStateException,
< SystemException {
< throw new UnsupportedOperationException();
< }
<
< public void setRollbackOnly() throws IllegalStateException,
< SystemException {
< try {
< setRollbackOnlyMethod.invoke(uowManager, new Object[] {});
< } catch (Exception e) {
< throw new HibernateException(e);
< }
< }
< }
<
< }
<
< public Object getTransactionIdentifier(Transaction txn) {
< if (!(txn instanceof UOWTransactionAdapter))
< throw new IllegalStateException("Invalid Transaction class");
< UOWTransactionAdapter tx = (UOWTransactionAdapter) txn;
< return tx.localId;
< }
<
< static class UOWInvocationHandler implements InvocationHandler {
<
< private IsolatedWork work;
< private Connection connection;
<
< public UOWInvocationHandler(IsolatedWork work, Connection connection) {
< super();
< this.work = work;
< this.connection = connection;
< }
< public Object invoke(Object proxy, Method method, Object[] args)
< throws Throwable {
< work.doWork(connection);
< return void.class;
< }
<
< }
<
< }
Like this:
Like Loading...