View Javadoc
1   /*
2    *  Copyright 2013 Chris Pheby
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  package org.jadira.usertype.spi.jta;
17  
18  import java.lang.reflect.Field;
19  import java.lang.reflect.InvocationTargetException;
20  import java.lang.reflect.Method;
21  
22  import javax.naming.NamingException;
23  import javax.transaction.Status;
24  import javax.transaction.Synchronization;
25  import javax.transaction.Transaction;
26  import javax.transaction.TransactionManager;
27  import javax.transaction.xa.XAResource;
28  
29  import org.omg.CORBA.SystemException;
30  import org.springframework.jndi.JndiTemplate;
31  import org.springframework.transaction.PlatformTransactionManager;
32  import org.springframework.transaction.TransactionSystemException;
33  import org.springframework.transaction.jta.WebSphereUowTransactionManager;
34  
35  /**
36   * 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
37   * to work with the underlying transaction manager, as the UoW API is providing its own transaction management abstraction distinct from JTA.
38   * <p>
39   * This class subclasses Spring's implementation and provides an adapter that bridges the JTA TransactionManager API to the UoW API.
40   * </p>
41   * <p>
42   * 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.
43   * </p>
44   * For further documentation and configuration options refer to {@link WebSphereUowTransactionManager}.
45   */
46  public class SpringWebSphereUowTransactionManager extends WebSphereUowTransactionManager {
47  
48      private static final long serialVersionUID = 4838070722625854290L;
49  
50      private static final String UOW_SYNCHRONIZATION_REGISTRY_JNDINAME = "java:comp/websphere/UOWSynchronizationRegistry";
51      private static final String USER_TRANASCTION_JNDINAME = "java:comp/UserTransaction";
52  
53      private static final Field UOW_FIELD;
54  
55      static {
56          try {
57              UOW_FIELD = WebSphereUowTransactionManager.class.getDeclaredField("uowManager");
58              UOW_FIELD.setAccessible(true);
59          } catch (SecurityException e) {
60              throw new IllegalStateException(
61                      "Not permitted to access WebSphereUowTransactionManager: " + e.getMessage(), e);
62          } catch (NoSuchFieldException e) {
63              throw new IllegalStateException("Could not find WebSphereUowTransactionManager: " + e.getMessage(), e);
64          }
65      }
66  
67      /**
68       * Creates a new instance
69       */
70      public SpringWebSphereUowTransactionManager() {
71          super();
72      }
73  
74      @Override
75      public void afterPropertiesSet() throws TransactionSystemException {
76          super.afterPropertiesSet();
77          setTransactionManager(new TransactionManagerAdapter(getJndiTemplate(), retrieveUowManager()));
78          setUserTransactionName(USER_TRANASCTION_JNDINAME);
79      }
80  
81      private Object retrieveUowManager() {
82          try {
83              Object uowManager = UOW_FIELD.get(this);
84              return uowManager;
85          } catch (SecurityException e) {
86              throw new IllegalStateException(
87                      "Not permitted to access WebSphereUowTransactionManager: " + e.getMessage(), e);
88          } catch (IllegalArgumentException e) {
89              throw new IllegalStateException("Unexpected argument accessing WebSphereUowTransactionManager: "
90                      + e.getMessage(), e);
91          } catch (IllegalAccessException e) {
92              throw new IllegalStateException("Unexpected exception accessing WebSphereUowTransactionManager: "
93                      + e.getMessage(), e);
94          }
95      }
96  
97      /**
98       * An adapter that fulfils the JTA {@link TransactionManager} by delegating to the WebSphereUOWTransactionManager
99       */
100     public static class TransactionManagerAdapter implements TransactionManager {
101 
102         private final JndiTemplate jndiTemplate;
103 
104         private final Object uowManager;
105         private final Class<?> uowManagerClass;
106 
107         private final Object uowSynchronizationRegistry;
108         private final Class<?> uowSynchronizationRegistryClass;
109 
110         private final Method registerSynchronizationMethod;
111         private final Method setRollbackOnlyMethod;
112 
113         private final Class<?> extendedJTATransactionClass;
114         private final Method getLocalIdMethod;
115 
116         /**
117          * Create a new instance
118          * @param jndiTemplate An instance of Spring's JndiTemplate to use to look up resources
119          * @param uowManager UOWManager to use
120          */
121         private TransactionManagerAdapter(JndiTemplate jndiTemplate, Object uowManager) {
122 
123             try {
124                 this.uowManagerClass = Class.forName("com.ibm.ws.uow.UOWManager");
125 
126                 this.uowSynchronizationRegistry = jndiTemplate.lookup(UOW_SYNCHRONIZATION_REGISTRY_JNDINAME);
127                 this.uowSynchronizationRegistryClass = Class
128                         .forName("com.ibm.websphere.uow.UOWSynchronizationRegistry");
129 
130                 this.registerSynchronizationMethod = uowSynchronizationRegistryClass.getMethod(
131                         "registerInterposedSynchronization", new Class[] { Synchronization.class });
132                 this.setRollbackOnlyMethod = uowManagerClass.getMethod("setRollbackOnly", new Class[] {});
133 
134                 this.extendedJTATransactionClass = Class
135                         .forName("com.ibm.websphere.jtaextensions.ExtendedJTATransaction");
136                 this.getLocalIdMethod = extendedJTATransactionClass.getMethod("getLocalId", (Class[]) null);
137 
138             } catch (ClassNotFoundException e) {
139                 throw new IllegalStateException("Could not find required WebSphere class: " + e.getMessage(), e);
140             } catch (NoSuchMethodException e) {
141                 throw new IllegalStateException("Could not find required method: " + e.getMessage(), e);
142             } catch (NamingException e) {
143                 throw new IllegalStateException("Problem accessing JNDI: " + e.getMessage(), e);
144             }
145 
146             this.jndiTemplate = jndiTemplate;
147             this.uowManager = uowManager;
148         }
149 
150         @Override
151         public void begin() {
152             throw new UnsupportedOperationException("begin() is not supported");
153         }
154 
155         @Override
156         public void commit() {
157             throw new UnsupportedOperationException("commit() is not supported");
158         }
159 
160         @Override
161         public int getStatus() {
162             throw new UnsupportedOperationException("getStatus() is not supported");
163         }
164 
165         @Override
166         public void resume(Transaction txn) {
167             throw new UnsupportedOperationException("resume() is not supported");
168         }
169 
170         @Override
171         public void rollback() {
172             throw new UnsupportedOperationException("rollback() is not supported");
173         }
174 
175         @Override
176         public void setTransactionTimeout(int i) {
177             throw new UnsupportedOperationException("setTransactionTimeout() is not supported");
178         }
179 
180         @Override
181         public Transaction suspend() {
182             throw new UnsupportedOperationException("suspend() is not supported");
183         }
184 
185         @Override
186         public void setRollbackOnly() throws IllegalStateException {
187             try {
188                 setRollbackOnlyMethod.invoke(uowManager, new Object[] {});
189             } catch (IllegalAccessException e) {
190                 throw new IllegalStateException("Could not access setRollbackOnly() on UOWManager: " + e.getMessage(),
191                         e);
192             } catch (InvocationTargetException e) {
193                 throw new IllegalStateException("Could not invoke setRollbackOnly() on UOWManager: " + e.getMessage(),
194                         e);
195             }
196         }
197 
198         @Override
199         public Transaction getTransaction() {
200             return new TransactionAdapter(jndiTemplate);
201         }
202 
203         /**
204          * An adapter that fulfils the JTA transaction interface.
205          */
206         public class TransactionAdapter implements Transaction {
207 
208             private final Object extendedJTATransaction;
209 
210             /**
211              * Creates a new instance
212              * @param template The JndiTemplate
213              */
214             private TransactionAdapter(JndiTemplate template) {
215                 try {
216                     extendedJTATransaction = template.lookup("java:comp/websphere/ExtendedJTATransaction");
217 
218                 } catch (NamingException e) {
219                     throw new IllegalStateException("Could not find ExtendedJTATransaction in JNDI: " + e.getMessage(),
220                             e);
221                 }
222             }
223 
224             @Override
225             public void registerSynchronization(final Synchronization synchronization) {
226 
227                 try {
228                     registerSynchronizationMethod.invoke(uowSynchronizationRegistry, new Object[] { synchronization });
229                 } catch (IllegalArgumentException e) {
230                     throw new IllegalStateException("Unexpected argument accessing UOWSynchronizationRegistry: "
231                             + e.getMessage(), e);
232                 } catch (IllegalAccessException e) {
233                     throw new IllegalStateException("Unexpected exception accessing UOWSynchronizationRegistry: "
234                             + e.getMessage(), e);
235                 } catch (InvocationTargetException e) {
236                     throw new IllegalStateException(
237                             "Could not invoke registerSynchronization() on UOWSynchronizationRegistry: "
238                                     + e.getMessage(), e);
239                 }
240             }
241 
242             @Override
243             public void commit() {
244                 throw new UnsupportedOperationException("commit() is not supported");
245             }
246 
247             @Override
248             public boolean delistResource(XAResource resource, int i) {
249                 throw new UnsupportedOperationException("delistResource() is not supported");
250             }
251 
252             @Override
253             public boolean enlistResource(XAResource resource) {
254                 throw new UnsupportedOperationException("enlistResource() is not supported");
255             }
256 
257             @Override
258             public int getStatus() {
259                 if (0 == getLocalId()) {
260                     return Status.STATUS_NO_TRANSACTION;
261                 } else {
262                     return Status.STATUS_ACTIVE;
263                 }
264             }
265 
266             @Override
267             public void rollback() throws IllegalStateException, SystemException {
268                 throw new UnsupportedOperationException("rollback() is not supported");
269             }
270 
271             @Override
272             public void setRollbackOnly() {
273                 try {
274                     setRollbackOnlyMethod.invoke(uowManager, new Object[] {});
275                 } catch (IllegalArgumentException e) {
276                     throw new IllegalStateException("Unexpected argument accessing UOWManager: " + e.getMessage(), e);
277                 } catch (IllegalAccessException e) {
278                     throw new IllegalStateException("Unexpected exception accessing UOWManager: " + e.getMessage(), e);
279                 } catch (InvocationTargetException e) {
280                     throw new IllegalStateException("Could not invoke setRollbackOnly() on UOWManager: "
281                             + e.getMessage(), e);
282                 }
283             }
284 
285             @Override
286             public int hashCode() {
287                 return getLocalId();
288             }
289 
290             @Override
291             public boolean equals(Object other) {
292                 if (!(other instanceof TransactionAdapter))
293                     return false;
294                 TransactionAdapter that = (TransactionAdapter) other;
295                 return getLocalId() == that.getLocalId();
296             }
297 
298             private int getLocalId() {
299                 try {
300                     return ((Integer) (getLocalIdMethod.invoke(extendedJTATransaction, (Object[]) null))).intValue();
301                 } catch (IllegalArgumentException e) {
302                     throw new IllegalStateException("Unexpected argument accessing ExtendedJTATransaction: "
303                             + e.getMessage(), e);
304                 } catch (IllegalAccessException e) {
305                     throw new IllegalStateException("Unexpected exception accessing ExtendedJTATransaction: "
306                             + e.getMessage(), e);
307                 } catch (InvocationTargetException e) {
308                     throw new IllegalStateException("Could not invoke getLocalId() on ExtendedJTATransaction: "
309                             + e.getMessage(), e);
310                 }
311             }
312 
313         }
314     }
315 }