1 /*
2 * JBoss, Home of Professional Open Source.
3 * Copyright 2006, Red Hat Middleware LLC, and individual contributors
4 * as indicated by the @author tags. See the copyright.txt file in the
5 * distribution for a full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */
22 package org.jboss.resource.adapter.jdbc.xa;
23
24 import java.sql.SQLException;
25 import java.util.Properties;
26
27 import javax.resource.ResourceException;
28 import javax.resource.spi.LocalTransaction;
29 import javax.sql.XAConnection;
30 import javax.transaction.xa.XAException;
31 import javax.transaction.xa.XAResource;
32 import javax.transaction.xa.Xid;
33
34 import org.jboss.resource.JBossResourceException;
35 import org.jboss.resource.adapter.jdbc.BaseWrapperManagedConnection;
36
37 /**
38 * XAManagedConnection
39 *
40 * @author <a href="mailto:d_jencks@users.sourceforge.net">David Jencks </a>
41 * @author <a href="mailto:adrian@jboss.com">Adrian Brock</a>
42 * @author <a href="weston.price@jboss.com">Weston Price</a>
43 * @version $Revision: 71788 $
44 */
45 public class XAManagedConnection extends BaseWrapperManagedConnection implements XAResource, LocalTransaction
46 {
47 protected final XAConnection xaConnection;
48
49 protected final XAResource xaResource;
50
51 protected Xid currentXid;
52
53 public XAManagedConnection(XAManagedConnectionFactory mcf, XAConnection xaConnection, Properties props,
54 int transactionIsolation, int psCacheSize) throws SQLException
55 {
56 super(mcf, xaConnection.getConnection(), props, transactionIsolation, psCacheSize);
57 this.xaConnection = xaConnection;
58 xaConnection.addConnectionEventListener(new javax.sql.ConnectionEventListener()
59 {
60 public void connectionClosed(javax.sql.ConnectionEvent ce)
61 {
62 //only we can do this, ignore
63 }
64
65 public void connectionErrorOccurred(javax.sql.ConnectionEvent ce)
66 {
67 SQLException ex = ce.getSQLException();
68 broadcastConnectionError(ex);
69 }
70 });
71 this.xaResource = xaConnection.getXAResource();
72 }
73
74 public void begin() throws ResourceException
75 {
76 lock();
77 try
78 {
79 synchronized (stateLock)
80 {
81 if (inManagedTransaction == false)
82 {
83 try
84 {
85 if (underlyingAutoCommit)
86 {
87 underlyingAutoCommit = false;
88 con.setAutoCommit(false);
89 }
90 checkState();
91 inManagedTransaction = true;
92 }
93 catch (SQLException e)
94 {
95 checkException(e);
96 }
97 }
98 else
99 throw new JBossResourceException("Trying to begin a nested local tx");
100 }
101 }
102 finally
103 {
104 unlock();
105 }
106 }
107 public void commit() throws ResourceException
108 {
109 lock();
110 try
111 {
112 synchronized (stateLock)
113 {
114 if (inManagedTransaction)
115 inManagedTransaction = false;
116 }
117 try
118 {
119 con.commit();
120 }
121 catch (SQLException e)
122 {
123 checkException(e);
124 }
125 }
126 finally
127 {
128 unlock();
129 }
130 }
131
132 public void rollback() throws ResourceException
133 {
134 lock();
135 try
136 {
137 synchronized (stateLock)
138 {
139 if (inManagedTransaction)
140 inManagedTransaction = false;
141 }
142 try
143 {
144 con.rollback();
145 }
146 catch (SQLException e)
147 {
148 try
149 {
150 checkException(e);
151 }
152 catch (Exception e2)
153 {
154 }
155 }
156 }
157 finally
158 {
159 unlock();
160 }
161 }
162
163 protected void broadcastConnectionError(SQLException e)
164 {
165 super.broadcastConnectionError(e);
166 }
167
168 public LocalTransaction getLocalTransaction() throws ResourceException
169 {
170 return this;
171 }
172
173 public XAResource getXAResource() throws ResourceException
174 {
175 return this;
176 }
177
178 public void destroy() throws ResourceException
179 {
180 try
181 {
182 super.destroy();
183 }
184 finally
185 {
186 try
187 {
188 xaConnection.close();
189 }
190 catch (SQLException e)
191 {
192 checkException(e);
193 }
194 }
195 }
196
197 public void start(Xid xid, int flags) throws XAException
198 {
199 lock();
200 try
201 {
202 try
203 {
204 checkState();
205 }
206 catch (SQLException e)
207 {
208 getLog().warn("Error setting state ", e);
209 }
210
211 try
212 {
213 xaResource.start(xid, flags);
214 }
215 catch(XAException e)
216 {
217 //JBAS-3336 Connections that fail in enlistment should not be returned
218 //to the pool
219 if(isFailedXA(e.errorCode))
220 {
221 broadcastConnectionError(e);
222 }
223
224 throw e;
225 }
226
227 synchronized (stateLock)
228 {
229 currentXid = xid;
230 inManagedTransaction = true;
231 }
232 }
233 finally
234 {
235 unlock();
236 }
237 }
238
239 public void end(Xid xid, int flags) throws XAException
240 {
241 lock();
242 try
243 {
244 try
245 {
246 xaResource.end(xid, flags);
247 }
248 catch(XAException e)
249 {
250 broadcastConnectionError(e);
251 throw e;
252 }
253
254
255 //we want to allow ending transactions that are not the current
256 //one. When one does this, inManagedTransaction is still true.
257 synchronized (stateLock)
258 {
259 if (currentXid != null && currentXid.equals(xid))
260 {
261 inManagedTransaction = false;
262 currentXid = null;
263 }
264 }
265 }
266 finally
267 {
268 unlock();
269 }
270 }
271
272 public int prepare(Xid xid) throws XAException
273 {
274 lock();
275 try
276 {
277 return xaResource.prepare(xid);
278 }
279 finally
280 {
281 unlock();
282 }
283 }
284
285 public void commit(Xid xid, boolean onePhase) throws XAException
286 {
287 lock();
288 try
289 {
290 xaResource.commit(xid, onePhase);
291 }
292 finally
293 {
294 unlock();
295 }
296 }
297
298 public void rollback(Xid xid) throws XAException
299 {
300 lock();
301 try
302 {
303 xaResource.rollback(xid);
304 }
305 finally
306 {
307 unlock();
308 }
309 }
310
311 public void forget(Xid xid) throws XAException
312 {
313 xaResource.forget(xid);
314 }
315
316 public Xid[] recover(int flag) throws XAException
317 {
318 return xaResource.recover(flag);
319 }
320
321 public boolean isSameRM(XAResource other) throws XAException
322 {
323 Boolean overrideValue = ((XAManagedConnectionFactory) mcf).getIsSameRMOverrideValue();
324 if (overrideValue != null)
325 {
326 return overrideValue.booleanValue();
327 }
328
329 // compare apples to apples
330 return (other instanceof XAManagedConnection)
331 ? xaResource.isSameRM(((XAManagedConnection) other).xaResource)
332 : xaResource.isSameRM(other);
333 }
334
335 public int getTransactionTimeout() throws XAException
336 {
337 return xaResource.getTransactionTimeout();
338 }
339
340 public boolean setTransactionTimeout(int seconds) throws XAException
341 {
342 return xaResource.setTransactionTimeout(seconds);
343 }
344
345 private boolean isFailedXA(int errorCode)
346 {
347
348 return (errorCode == XAException.XAER_RMERR || errorCode == XAException.XAER_RMFAIL);
349 }
350 }