View Javadoc
1   /*
2   
3     Licensed to the Apache Software Foundation (ASF) under one or more
4     contributor license agreements.  See the NOTICE file distributed with
5     this work for additional information regarding copyright ownership.
6     The ASF licenses this file to You under the Apache License, Version 2.0
7     (the "License"); you may not use this file except in compliance with
8     the License.  You may obtain a copy of the License at
9   
10        http://www.apache.org/licenses/LICENSE-2.0
11  
12     Unless required by applicable law or agreed to in writing, software
13     distributed under the License is distributed on an "AS IS" BASIS,
14     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15     See the License for the specific language governing permissions and
16     limitations under the License.
17   */
18  package org.apache.commons.dbcp2.managed;
19  
20  import java.lang.reflect.InvocationHandler;
21  import java.lang.reflect.InvocationTargetException;
22  import java.lang.reflect.Method;
23  import java.lang.reflect.Proxy;
24  import java.sql.Connection;
25  import java.sql.SQLException;
26  import java.util.ArrayList;
27  import java.util.LinkedList;
28  import java.util.List;
29  import java.util.concurrent.atomic.AtomicInteger;
30  
31  import javax.sql.ConnectionEvent;
32  import javax.sql.ConnectionEventListener;
33  import javax.sql.StatementEventListener;
34  import javax.sql.XAConnection;
35  import javax.transaction.xa.XAResource;
36  
37  /**
38   * Basic XAConnection. getConnection() returns a handle on a physical
39   * Connection. Closing the handle does not close the physical connection, you
40   * have to close the XAConnection for that (PooledConnection behavior).
41   * XA behavior is implemented through a LocalXAResource.
42   */
43  public class TesterBasicXAConnection implements XAConnection {
44  
45      /**
46       * Delegates everything to a Connection, except for close() which just
47       * notifies the parent XAConnection.
48       */
49      public static class ConnectionHandle implements InvocationHandler {
50  
51          public Connection conn;
52  
53          public final TesterBasicXAConnection xaconn;
54  
55          public ConnectionHandle(final Connection conn, final TesterBasicXAConnection xaconn) {
56              this.conn = conn;
57              this.xaconn = xaconn;
58          }
59  
60          protected Object close() throws SQLException {
61              if (conn != null) {
62                  conn.clearWarnings();
63                  conn = null;
64                  xaconn.handle = null;
65                  xaconn.notifyConnectionClosed();
66              }
67              return null;
68          }
69  
70          public void closeHandle() {
71              conn = null;
72          }
73  
74          @Override
75          public Object invoke(final Object proxy, final Method method, final Object[] args)
76                  throws Throwable {
77              final String methodName = method.getName();
78              if (methodName.equals("hashCode")) {
79                  return System.identityHashCode(proxy);
80              }
81              if (methodName.equals("equals")) {
82                  return proxy == args[0];
83              }
84              if (methodName.equals("isClosed")) {
85                  return conn == null;
86              }
87              if (methodName.equals("close")) {
88                  return close();
89              }
90              if (conn == null) {
91                  throw new SQLException("Connection closed");
92              }
93              try {
94                  return method.invoke(conn, args);
95              } catch (final InvocationTargetException e) {
96                  final Throwable te = e.getTargetException();
97                  if (te instanceof SQLException) {
98                      xaconn.notifyConnectionErrorOccurred((SQLException) te);
99                  }
100                 throw te;
101             }
102         }
103     }
104 
105     public Connection conn;
106 
107     public ConnectionHandle handle;
108 
109     public final List<ConnectionEventListener> listeners = new LinkedList<>();
110 
111     public final AtomicInteger closeCounter;
112 
113     public TesterBasicXAConnection(final Connection conn) {
114         this(conn, null);
115     }
116 
117     public TesterBasicXAConnection(final Connection conn, final AtomicInteger closeCounter) {
118         this.conn = conn;
119         this.closeCounter = closeCounter;
120     }
121 
122     @Override
123     public void addConnectionEventListener(
124             final ConnectionEventListener connectionEventListener) {
125         listeners.add(connectionEventListener);
126     }
127 
128     @Override
129     public void addStatementEventListener(final StatementEventListener listener) {
130         throw new UnsupportedOperationException();
131     }
132 
133     @Override
134     public void close() throws SQLException {
135         if (handle != null) {
136             closeHandle();
137         }
138         try {
139             conn.close();
140             if (closeCounter != null) {
141                 closeCounter.incrementAndGet();
142             }
143         } finally {
144             conn = null;
145         }
146     }
147 
148     protected void closeHandle() throws SQLException {
149         handle.closeHandle();
150         if (!conn.getAutoCommit()) {
151             try {
152                 conn.rollback();
153             } catch (final SQLException e) {
154                 e.printStackTrace();
155             }
156         }
157         handle = null;
158     }
159 
160     @Override
161     public Connection getConnection() throws SQLException {
162         if (conn == null) {
163             final SQLException e = new SQLException("XAConnection closed");
164             notifyConnectionErrorOccurred(e);
165             throw e;
166         }
167         try {
168             if (handle != null) {
169                 // only one handle at a time on the XAConnection
170                 closeHandle();
171                 conn.clearWarnings();
172             }
173         } catch (final SQLException e) {
174             notifyConnectionErrorOccurred(e);
175             throw e;
176         }
177         handle = new ConnectionHandle(conn, this);
178         return (Connection) Proxy.newProxyInstance(
179                 getClass().getClassLoader(), new Class[] { Connection.class },
180                 handle);
181     }
182 
183     @Override
184     public XAResource getXAResource() throws SQLException {
185         return new LocalXAConnectionFactory.LocalXAResource(conn);
186     }
187 
188     protected void notifyConnectionClosed() {
189         final ConnectionEvent event = new ConnectionEvent(this);
190         final List<ConnectionEventListener> copy = new ArrayList<>(
191                 listeners);
192         for (final ConnectionEventListener listener : copy) {
193             listener.connectionClosed(event);
194         }
195     }
196 
197     protected void notifyConnectionErrorOccurred(final SQLException e) {
198         final ConnectionEvent event = new ConnectionEvent(this, e);
199         final List<ConnectionEventListener> copy = new ArrayList<>(
200                 listeners);
201         for (final ConnectionEventListener listener : copy) {
202             listener.connectionErrorOccurred(event);
203         }
204     }
205 
206     @Override
207     public void removeConnectionEventListener(
208             final ConnectionEventListener connectionEventListener) {
209         listeners.remove(connectionEventListener);
210     }
211 
212     @Override
213     public void removeStatementEventListener(final StatementEventListener listener) {
214         throw new UnsupportedOperationException();
215     }
216 }
217