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