View Javadoc
1   /*
2    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
3    * The software in this package is published under the terms of the CPAL v1.0
4    * license, a copy of which has been included with this distribution in the
5    * LICENSE.txt file.
6    */
7   package org.mule.transport.jdbc.xa;
8   
9   import org.mule.api.transaction.Transaction;
10  import org.mule.api.transaction.TransactionException;
11  import org.mule.config.i18n.CoreMessages;
12  import org.mule.transaction.IllegalTransactionStateException;
13  import org.mule.transaction.TransactionCoordination;
14  import org.mule.transaction.XaTransaction;
15  
16  import java.lang.reflect.Proxy;
17  import java.sql.Array;
18  import java.sql.Blob;
19  import java.sql.CallableStatement;
20  import java.sql.Clob;
21  import java.sql.Connection;
22  import java.sql.DatabaseMetaData;
23  import java.sql.NClob;
24  import java.sql.PreparedStatement;
25  import java.sql.SQLClientInfoException;
26  import java.sql.SQLException;
27  import java.sql.SQLWarning;
28  import java.sql.SQLXML;
29  import java.sql.Savepoint;
30  import java.sql.Statement;
31  import java.sql.Struct;
32  import java.util.Map;
33  import java.util.Properties;
34  
35  import javax.sql.XAConnection;
36  import javax.transaction.xa.XAResource;
37  
38  import org.apache.commons.logging.Log;
39  import org.apache.commons.logging.LogFactory;
40  
41  /**
42   * Using for unification XAConnection and Connection
43   */
44  public class ConnectionWrapper implements Connection, XaTransaction.MuleXaObject
45  {
46      private final XAConnection xaConnection;
47      private Connection connection;
48  
49      /**
50       * This is the lock object that guards access to {@link #enlistedXAResource}.
51       */
52      private final Object enlistedXAResourceLock = new Object();
53      /**
54       * @GuardedBy {@link #enlistedXAResourceLock}
55       */
56      private XAResource enlistedXAResource;
57  
58      protected static final transient Log logger = LogFactory.getLog(ConnectionWrapper.class);
59      private volatile boolean reuseObject = false;
60  
61      public ConnectionWrapper(XAConnection xaCon) throws SQLException
62      {
63          this.xaConnection = xaCon;
64          this.connection = xaCon.getConnection();
65      }
66  
67      public int getHoldability() throws SQLException
68      {
69          return connection.getHoldability();
70      }
71  
72      public int getTransactionIsolation() throws SQLException
73      {
74          return connection.getTransactionIsolation();
75      }
76  
77      public void clearWarnings() throws SQLException
78      {
79          connection.clearWarnings();
80      }
81  
82      public void close() throws SQLException
83      {
84          connection.close();
85          try
86          {
87              xaConnection.close();
88          }
89          catch (SQLException e)
90          {
91              logger.info(
92                  "Exception while explicitely closing the xaConnection (some providers require this). "
93                                  + "The exception will be ignored and only logged: " + e.getMessage(), e);
94          }
95      }
96  
97      public void commit() throws SQLException
98      {
99          connection.commit();
100     }
101 
102     public void rollback() throws SQLException
103     {
104         connection.rollback();
105     }
106 
107     public boolean getAutoCommit() throws SQLException
108     {
109         return connection.getAutoCommit();
110     }
111 
112     public boolean isClosed() throws SQLException
113     {
114         return connection.isClosed();
115     }
116 
117     public boolean isReadOnly() throws SQLException
118     {
119         return connection.isReadOnly();
120     }
121 
122     public void setHoldability(int holdability) throws SQLException
123     {
124         connection.setHoldability(holdability);
125     }
126 
127     public void setTransactionIsolation(int level) throws SQLException
128     {
129         connection.setTransactionIsolation(level);
130     }
131 
132     public void setAutoCommit(boolean autoCommit) throws SQLException
133     {
134         connection.setAutoCommit(autoCommit);
135     }
136 
137     public void setReadOnly(boolean readOnly) throws SQLException
138     {
139         connection.setReadOnly(readOnly);
140     }
141 
142     public String getCatalog() throws SQLException
143     {
144         return connection.getCatalog();
145     }
146 
147     public void setCatalog(String catalog) throws SQLException
148     {
149         connection.setCatalog(catalog);
150     }
151 
152     public DatabaseMetaData getMetaData() throws SQLException
153     {
154         return connection.getMetaData();
155     }
156 
157     public SQLWarning getWarnings() throws SQLException
158     {
159         return connection.getWarnings();
160     }
161 
162     public Savepoint setSavepoint() throws SQLException
163     {
164         return connection.setSavepoint();
165     }
166 
167     public void releaseSavepoint(Savepoint savepoint) throws SQLException
168     {
169         connection.releaseSavepoint(savepoint);
170     }
171 
172     public void rollback(Savepoint savepoint) throws SQLException
173     {
174         connection.rollback();
175     }
176 
177     public Statement createStatement() throws SQLException
178     {
179         Statement st = connection.createStatement();
180         return (Statement) Proxy.newProxyInstance(Statement.class.getClassLoader(),
181                                                   new Class[]{Statement.class}, new StatementInvocationHandler(this, st));
182     }
183 
184     public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException
185     {
186         Statement st = connection.createStatement(resultSetType, resultSetConcurrency);
187         return (Statement) Proxy.newProxyInstance(Statement.class.getClassLoader(),
188                                                   new Class[]{Statement.class}, new StatementInvocationHandler(this, st));
189     }
190 
191     public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
192             throws SQLException
193     {
194         Statement st = connection.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
195         return (Statement) Proxy.newProxyInstance(Statement.class.getClassLoader(),
196                                                   new Class[]{Statement.class}, new StatementInvocationHandler(this, st));
197     }
198 
199     public Map getTypeMap() throws SQLException
200     {
201         return connection.getTypeMap();
202     }
203 
204     public void setTypeMap(Map<String, Class<?>> map) throws SQLException
205     {
206         connection.setTypeMap(map);
207     }
208 
209     public String nativeSQL(String sql) throws SQLException
210     {
211         return connection.nativeSQL(sql);
212     }
213 
214     public CallableStatement prepareCall(String sql) throws SQLException
215     {
216         CallableStatement cs = connection.prepareCall(sql);
217         return (CallableStatement) Proxy.newProxyInstance(CallableStatement.class.getClassLoader(),
218                                                           new Class[]{CallableStatement.class}, new StatementInvocationHandler(this, cs));
219     }
220 
221     public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency)
222             throws SQLException
223     {
224         CallableStatement cs = connection.prepareCall(sql, resultSetType, resultSetConcurrency);
225         return (CallableStatement) Proxy.newProxyInstance(CallableStatement.class.getClassLoader(),
226                                                           new Class[]{CallableStatement.class}, new StatementInvocationHandler(this, cs));
227     }
228 
229     public CallableStatement prepareCall(String sql,
230                                          int resultSetType,
231                                          int resultSetConcurrency,
232                                          int resultSetHoldability) throws SQLException
233     {
234         CallableStatement cs = connection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
235         return (CallableStatement) Proxy.newProxyInstance(CallableStatement.class.getClassLoader(),
236                                                           new Class[]{CallableStatement.class}, new StatementInvocationHandler(this, cs));
237     }
238 
239     public PreparedStatement prepareStatement(String sql) throws SQLException
240     {
241         PreparedStatement ps = connection.prepareStatement(sql);
242         return (PreparedStatement) Proxy.newProxyInstance(PreparedStatement.class.getClassLoader(),
243                                                           new Class[]{PreparedStatement.class}, new StatementInvocationHandler(this, ps));
244     }
245 
246     public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException
247     {
248         PreparedStatement ps = connection.prepareStatement(sql, autoGeneratedKeys);
249         return (PreparedStatement) Proxy.newProxyInstance(PreparedStatement.class.getClassLoader(),
250                                                           new Class[]{PreparedStatement.class}, new StatementInvocationHandler(this, ps));
251     }
252 
253     public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
254             throws SQLException
255     {
256         PreparedStatement ps = connection.prepareStatement(sql, resultSetType, resultSetConcurrency);
257         return (PreparedStatement) Proxy.newProxyInstance(PreparedStatement.class.getClassLoader(),
258                                                           new Class[]{PreparedStatement.class}, new StatementInvocationHandler(this, ps));
259     }
260 
261     public PreparedStatement prepareStatement(String sql,
262                                               int resultSetType,
263                                               int resultSetConcurrency,
264                                               int resultSetHoldability) throws SQLException
265     {
266         PreparedStatement ps = connection.prepareStatement(sql, resultSetType, resultSetConcurrency,
267                                                            resultSetHoldability);
268         return (PreparedStatement) Proxy.newProxyInstance(PreparedStatement.class.getClassLoader(),
269                                                           new Class[]{PreparedStatement.class}, new StatementInvocationHandler(this, ps));
270     }
271 
272     public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException
273     {
274         PreparedStatement ps = connection.prepareStatement(sql, columnIndexes);
275         return (PreparedStatement) Proxy.newProxyInstance(PreparedStatement.class.getClassLoader(),
276                                                           new Class[]{PreparedStatement.class}, new StatementInvocationHandler(this, ps));
277     }
278 
279     public Savepoint setSavepoint(String name) throws SQLException
280     {
281         return connection.setSavepoint(name);
282     }
283 
284     public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException
285     {
286         PreparedStatement ps = connection.prepareStatement(sql, columnNames);
287         return (PreparedStatement) Proxy.newProxyInstance(PreparedStatement.class.getClassLoader(),
288                                                           new Class[]{PreparedStatement.class}, new StatementInvocationHandler(this, ps));
289     }
290 
291     public boolean enlist() throws TransactionException
292     {
293         try
294         {
295             connection.setAutoCommit(false);
296         }
297         catch (SQLException e)
298         {
299             throw new TransactionException(e);
300         }
301         
302         if (isEnlisted())
303         {
304             return false;
305         }
306         if (logger.isDebugEnabled())
307         {
308             logger.debug("Enlistment request: " + this);
309         }
310 
311         Transaction transaction = TransactionCoordination.getInstance().getTransaction();
312         if (transaction == null)
313         {
314             throw new IllegalTransactionStateException(CoreMessages.noMuleTransactionAvailable());
315         }
316         if (!(transaction instanceof XaTransaction))
317         {
318             throw new IllegalTransactionStateException(CoreMessages.notMuleXaTransaction(transaction));
319         }
320 
321         synchronized (enlistedXAResourceLock)
322         {
323             if (!isEnlisted())
324             {
325                 final XAResource xaResource = getXAResourceFromXATransaction();
326                 boolean wasAbleToEnlist = ((XaTransaction) transaction).enlistResource(xaResource);
327                 if (wasAbleToEnlist)
328                 {
329                     enlistedXAResource = xaResource;
330                 }
331             }
332         }
333         
334         return isEnlisted();
335     }
336 
337     protected XAResource getXAResourceFromXATransaction() throws TransactionException
338     {
339         try
340         {
341             return xaConnection.getXAResource();
342         }
343         catch (SQLException e)
344         {
345             throw new TransactionException(e);
346         }
347     }
348 
349     public boolean delist() throws Exception
350     {
351         if (!isEnlisted())
352         {
353             return false;
354         }
355         if (logger.isDebugEnabled())
356         {
357             logger.debug("Delistment request: " + this);
358         }
359 
360         Transaction transaction = TransactionCoordination.getInstance().getTransaction();
361         if (transaction == null)
362         {
363             throw new IllegalTransactionStateException(CoreMessages.noMuleTransactionAvailable());
364         }
365         if (!(transaction instanceof XaTransaction))
366         {
367             throw new IllegalTransactionStateException(CoreMessages.notMuleXaTransaction(transaction));
368         }
369 
370         synchronized (enlistedXAResourceLock)
371         {
372             if (isEnlisted())
373             {
374                 boolean wasAbleToDelist = ((XaTransaction) transaction).delistResource(enlistedXAResource,
375                     XAResource.TMSUCCESS);
376                 if (wasAbleToDelist)
377                 {
378                     enlistedXAResource = null;
379                 }
380             }
381             return !isEnlisted();
382         }
383     }
384 
385 
386     public boolean isEnlisted()
387     {
388         synchronized (enlistedXAResourceLock)
389         {
390             return enlistedXAResource != null;
391         }
392     }
393 
394     public boolean isReuseObject()
395     {
396         return reuseObject;
397     }
398 
399     public void setReuseObject(boolean reuseObject)
400     {
401         this.reuseObject = reuseObject;
402     }
403 
404     public Object getTargetObject()
405     {
406         return xaConnection;
407     }
408 
409     public Array createArrayOf(String typeName, Object[] elements) throws SQLException
410     {
411         return connection.createArrayOf(typeName, elements);
412     }
413 
414     public Blob createBlob() throws SQLException
415     {
416         return connection.createBlob();
417     }
418 
419     public Clob createClob() throws SQLException
420     {
421         return connection.createClob();
422     }
423 
424     public NClob createNClob() throws SQLException
425     {
426         return connection.createNClob();
427     }
428 
429     public SQLXML createSQLXML() throws SQLException
430     {
431         return connection.createSQLXML();
432     }
433 
434     public Struct createStruct(String typeName, Object[] attributes) throws SQLException
435     {
436         return connection.createStruct(typeName, attributes);
437     }
438 
439     public Properties getClientInfo() throws SQLException
440     {
441         return connection.getClientInfo();
442     }
443 
444     public String getClientInfo(String name) throws SQLException
445     {
446         return connection.getClientInfo(name);
447     }
448 
449     public boolean isValid(int timeout) throws SQLException
450     {
451         return connection.isValid(timeout);
452     }
453 
454     public void setClientInfo(Properties properties) throws SQLClientInfoException
455     {
456         connection.setClientInfo(properties);
457     }
458 
459     public void setClientInfo(String name, String value) throws SQLClientInfoException
460     {
461         connection.setClientInfo(name, value);
462     }
463 
464     public boolean isWrapperFor(Class<?> iface) throws SQLException
465     {
466         return connection.isWrapperFor(iface);
467     }
468 
469     public <T> T unwrap(Class<T> iface) throws SQLException
470     {
471         return connection.unwrap(iface);
472     }
473 }