/* * Copyright 2013 Chris Pheby * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jadira.usertype.spi.jta; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import javax.naming.NamingException; import javax.transaction.Status; import javax.transaction.Synchronization; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import javax.transaction.xa.XAResource; import org.omg.CORBA.SystemException; import org.springframework.jndi.JndiTemplate; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionSystemException; import org.springframework.transaction.jta.WebSphereUowTransactionManager; /** * Spring provides an implementation of {@link PlatformTransactionManager} which works properly with WebSphere's Unit of Work (UoW) API. Unfortunately, this implementation has problems when you need * to work with the underlying transaction manager, as the UoW API is providing its own transaction management abstraction distinct from JTA. * <p> * This class subclasses Spring's implementation and provides an adapter that bridges the JTA TransactionManager API to the UoW API. * </p> * <p> * You should use this implementation, for example, when using the {@link LocalTransactionManagerPlatform} with Hibernate and WebSphere. It is compatible with WebSphere 6.1.0.9 and above. * </p> * For further documentation and configuration options refer to {@link WebSphereUowTransactionManager}. */ public class SpringWebSphereUowTransactionManager extends WebSphereUowTransactionManager { private static final long serialVersionUID = 4838070722625854290L; private static final String UOW_SYNCHRONIZATION_REGISTRY_JNDINAME = "java:comp/websphere/UOWSynchronizationRegistry"; private static final String USER_TRANASCTION_JNDINAME = "java:comp/UserTransaction"; private static final Field UOW_FIELD; static { try { UOW_FIELD = WebSphereUowTransactionManager.class.getDeclaredField("uowManager"); UOW_FIELD.setAccessible(true); } catch (SecurityException e) { throw new IllegalStateException( "Not permitted to access WebSphereUowTransactionManager: " + e.getMessage(), e); } catch (NoSuchFieldException e) { throw new IllegalStateException("Could not find WebSphereUowTransactionManager: " + e.getMessage(), e); } } /** * Creates a new instance */ public SpringWebSphereUowTransactionManager() { super(); } @Override public void afterPropertiesSet() throws TransactionSystemException { super.afterPropertiesSet(); setTransactionManager(new TransactionManagerAdapter(getJndiTemplate(), retrieveUowManager())); setUserTransactionName(USER_TRANASCTION_JNDINAME); } private Object retrieveUowManager() { try { Object uowManager = UOW_FIELD.get(this); return uowManager; } catch (SecurityException e) { throw new IllegalStateException( "Not permitted to access WebSphereUowTransactionManager: " + e.getMessage(), e); } catch (IllegalArgumentException e) { throw new IllegalStateException("Unexpected argument accessing WebSphereUowTransactionManager: " + e.getMessage(), e); } catch (IllegalAccessException e) { throw new IllegalStateException("Unexpected exception accessing WebSphereUowTransactionManager: " + e.getMessage(), e); } } /** * An adapter that fulfils the JTA {@link TransactionManager} by delegating to the WebSphereUOWTransactionManager */ public static class TransactionManagerAdapter implements TransactionManager { private final JndiTemplate jndiTemplate; private final Object uowManager; private final Class<?> uowManagerClass; private final Object uowSynchronizationRegistry; private final Class<?> uowSynchronizationRegistryClass; private final Method registerSynchronizationMethod; private final Method setRollbackOnlyMethod; private final Class<?> extendedJTATransactionClass; private final Method getLocalIdMethod; /** * Create a new instance * @param jndiTemplate An instance of Spring's JndiTemplate to use to look up resources * @param uowManager UOWManager to use */ private TransactionManagerAdapter(JndiTemplate jndiTemplate, Object uowManager) { try { this.uowManagerClass = Class.forName("com.ibm.ws.uow.UOWManager"); this.uowSynchronizationRegistry = jndiTemplate.lookup(UOW_SYNCHRONIZATION_REGISTRY_JNDINAME); this.uowSynchronizationRegistryClass = Class .forName("com.ibm.websphere.uow.UOWSynchronizationRegistry"); this.registerSynchronizationMethod = uowSynchronizationRegistryClass.getMethod( "registerInterposedSynchronization", new Class[] { Synchronization.class }); this.setRollbackOnlyMethod = uowManagerClass.getMethod("setRollbackOnly", new Class[] {}); this.extendedJTATransactionClass = Class .forName("com.ibm.websphere.jtaextensions.ExtendedJTATransaction"); this.getLocalIdMethod = extendedJTATransactionClass.getMethod("getLocalId", (Class[]) null); } catch (ClassNotFoundException e) { throw new IllegalStateException("Could not find required WebSphere class: " + e.getMessage(), e); } catch (NoSuchMethodException e) { throw new IllegalStateException("Could not find required method: " + e.getMessage(), e); } catch (NamingException e) { throw new IllegalStateException("Problem accessing JNDI: " + e.getMessage(), e); } this.jndiTemplate = jndiTemplate; this.uowManager = uowManager; } @Override public void begin() { throw new UnsupportedOperationException("begin() is not supported"); } @Override public void commit() { throw new UnsupportedOperationException("commit() is not supported"); } @Override public int getStatus() { throw new UnsupportedOperationException("getStatus() is not supported"); } @Override public void resume(Transaction txn) { throw new UnsupportedOperationException("resume() is not supported"); } @Override public void rollback() { throw new UnsupportedOperationException("rollback() is not supported"); } @Override public void setTransactionTimeout(int i) { throw new UnsupportedOperationException("setTransactionTimeout() is not supported"); } @Override public Transaction suspend() { throw new UnsupportedOperationException("suspend() is not supported"); } @Override public void setRollbackOnly() throws IllegalStateException { try { setRollbackOnlyMethod.invoke(uowManager, new Object[] {}); } catch (IllegalAccessException e) { throw new IllegalStateException("Could not access setRollbackOnly() on UOWManager: " + e.getMessage(), e); } catch (InvocationTargetException e) { throw new IllegalStateException("Could not invoke setRollbackOnly() on UOWManager: " + e.getMessage(), e); } } @Override public Transaction getTransaction() { return new TransactionAdapter(jndiTemplate); } /** * An adapter that fulfils the JTA transaction interface. */ public class TransactionAdapter implements Transaction { private final Object extendedJTATransaction; /** * Creates a new instance * @param template The JndiTemplate */ private TransactionAdapter(JndiTemplate template) { try { extendedJTATransaction = template.lookup("java:comp/websphere/ExtendedJTATransaction"); } catch (NamingException e) { throw new IllegalStateException("Could not find ExtendedJTATransaction in JNDI: " + e.getMessage(), e); } } @Override public void registerSynchronization(final Synchronization synchronization) { try { registerSynchronizationMethod.invoke(uowSynchronizationRegistry, new Object[] { synchronization }); } catch (IllegalArgumentException e) { throw new IllegalStateException("Unexpected argument accessing UOWSynchronizationRegistry: " + e.getMessage(), e); } catch (IllegalAccessException e) { throw new IllegalStateException("Unexpected exception accessing UOWSynchronizationRegistry: " + e.getMessage(), e); } catch (InvocationTargetException e) { throw new IllegalStateException( "Could not invoke registerSynchronization() on UOWSynchronizationRegistry: " + e.getMessage(), e); } } @Override public void commit() { throw new UnsupportedOperationException("commit() is not supported"); } @Override public boolean delistResource(XAResource resource, int i) { throw new UnsupportedOperationException("delistResource() is not supported"); } @Override public boolean enlistResource(XAResource resource) { throw new UnsupportedOperationException("enlistResource() is not supported"); } @Override public int getStatus() { if (0 == getLocalId()) { return Status.STATUS_NO_TRANSACTION; } else { return Status.STATUS_ACTIVE; } } @Override public void rollback() throws IllegalStateException, SystemException { throw new UnsupportedOperationException("rollback() is not supported"); } @Override public void setRollbackOnly() { try { setRollbackOnlyMethod.invoke(uowManager, new Object[] {}); } catch (IllegalArgumentException e) { throw new IllegalStateException("Unexpected argument accessing UOWManager: " + e.getMessage(), e); } catch (IllegalAccessException e) { throw new IllegalStateException("Unexpected exception accessing UOWManager: " + e.getMessage(), e); } catch (InvocationTargetException e) { throw new IllegalStateException("Could not invoke setRollbackOnly() on UOWManager: " + e.getMessage(), e); } } @Override public int hashCode() { return getLocalId(); } @Override public boolean equals(Object other) { if (!(other instanceof TransactionAdapter)) return false; TransactionAdapter that = (TransactionAdapter) other; return getLocalId() == that.getLocalId(); } private int getLocalId() { try { return ((Integer) (getLocalIdMethod.invoke(extendedJTATransaction, (Object[]) null))).intValue(); } catch (IllegalArgumentException e) { throw new IllegalStateException("Unexpected argument accessing ExtendedJTATransaction: " + e.getMessage(), e); } catch (IllegalAccessException e) { throw new IllegalStateException("Unexpected exception accessing ExtendedJTATransaction: " + e.getMessage(), e); } catch (InvocationTargetException e) { throw new IllegalStateException("Could not invoke getLocalId() on ExtendedJTATransaction: " + e.getMessage(), e); } } } } }