1
2
3
4
5
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
43
44 public class ConnectionWrapper implements Connection, XaTransaction.MuleXaObject
45 {
46 private final XAConnection xaConnection;
47 private Connection connection;
48
49
50
51
52 private final Object enlistedXAResourceLock = new Object();
53
54
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 }