View Javadoc
1   /**
2    *    Copyright 2009-2019 the original author or authors.
3    *
4    *    Licensed under the Apache License, Version 2.0 (the "License");
5    *    you may not use this file except in compliance with the License.
6    *    You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *    Unless required by applicable law or agreed to in writing, software
11   *    distributed under the License is distributed on an "AS IS" BASIS,
12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *    See the License for the specific language governing permissions and
14   *    limitations under the License.
15   */
16  package org.apache.ibatis.datasource.pooled;
17  
18  import java.lang.reflect.InvocationHandler;
19  import java.lang.reflect.Method;
20  import java.lang.reflect.Proxy;
21  import java.sql.Connection;
22  import java.sql.SQLException;
23  
24  import org.apache.ibatis.reflection.ExceptionUtil;
25  
26  /**
27   * @author Clinton Begin
28   */
29  class PooledConnection implements InvocationHandler {
30  
31    private static final String CLOSE = "close";
32    private static final Class<?>[] IFACES = new Class<?>[] { Connection.class };
33  
34    private final int hashCode;
35    private final PooledDataSource dataSource;
36    private final Connection realConnection;
37    private final Connection proxyConnection;
38    private long checkoutTimestamp;
39    private long createdTimestamp;
40    private long lastUsedTimestamp;
41    private int connectionTypeCode;
42    private boolean valid;
43  
44    /**
45     * Constructor for SimplePooledConnection that uses the Connection and PooledDataSource passed in.
46     *
47     * @param connection - the connection that is to be presented as a pooled connection
48     * @param dataSource - the dataSource that the connection is from
49     */
50    public PooledConnection(Connection connection, PooledDataSource dataSource) {
51      this.hashCode = connection.hashCode();
52      this.realConnection = connection;
53      this.dataSource = dataSource;
54      this.createdTimestamp = System.currentTimeMillis();
55      this.lastUsedTimestamp = System.currentTimeMillis();
56      this.valid = true;
57      this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);
58    }
59  
60    /**
61     * Invalidates the connection.
62     */
63    public void invalidate() {
64      valid = false;
65    }
66  
67    /**
68     * Method to see if the connection is usable.
69     *
70     * @return True if the connection is usable
71     */
72    public boolean isValid() {
73      return valid && realConnection != null && dataSource.pingConnection(this);
74    }
75  
76    /**
77     * Getter for the *real* connection that this wraps.
78     *
79     * @return The connection
80     */
81    public Connection getRealConnection() {
82      return realConnection;
83    }
84  
85    /**
86     * Getter for the proxy for the connection.
87     *
88     * @return The proxy
89     */
90    public Connection getProxyConnection() {
91      return proxyConnection;
92    }
93  
94    /**
95     * Gets the hashcode of the real connection (or 0 if it is null).
96     *
97     * @return The hashcode of the real connection (or 0 if it is null)
98     */
99    public int getRealHashCode() {
100     return realConnection == null ? 0 : realConnection.hashCode();
101   }
102 
103   /**
104    * Getter for the connection type (based on url + user + password).
105    *
106    * @return The connection type
107    */
108   public int getConnectionTypeCode() {
109     return connectionTypeCode;
110   }
111 
112   /**
113    * Setter for the connection type.
114    *
115    * @param connectionTypeCode - the connection type
116    */
117   public void setConnectionTypeCode(int connectionTypeCode) {
118     this.connectionTypeCode = connectionTypeCode;
119   }
120 
121   /**
122    * Getter for the time that the connection was created.
123    *
124    * @return The creation timestamp
125    */
126   public long getCreatedTimestamp() {
127     return createdTimestamp;
128   }
129 
130   /**
131    * Setter for the time that the connection was created.
132    *
133    * @param createdTimestamp - the timestamp
134    */
135   public void setCreatedTimestamp(long createdTimestamp) {
136     this.createdTimestamp = createdTimestamp;
137   }
138 
139   /**
140    * Getter for the time that the connection was last used.
141    *
142    * @return - the timestamp
143    */
144   public long getLastUsedTimestamp() {
145     return lastUsedTimestamp;
146   }
147 
148   /**
149    * Setter for the time that the connection was last used.
150    *
151    * @param lastUsedTimestamp - the timestamp
152    */
153   public void setLastUsedTimestamp(long lastUsedTimestamp) {
154     this.lastUsedTimestamp = lastUsedTimestamp;
155   }
156 
157   /**
158    * Getter for the time since this connection was last used.
159    *
160    * @return - the time since the last use
161    */
162   public long getTimeElapsedSinceLastUse() {
163     return System.currentTimeMillis() - lastUsedTimestamp;
164   }
165 
166   /**
167    * Getter for the age of the connection.
168    *
169    * @return the age
170    */
171   public long getAge() {
172     return System.currentTimeMillis() - createdTimestamp;
173   }
174 
175   /**
176    * Getter for the timestamp that this connection was checked out.
177    *
178    * @return the timestamp
179    */
180   public long getCheckoutTimestamp() {
181     return checkoutTimestamp;
182   }
183 
184   /**
185    * Setter for the timestamp that this connection was checked out.
186    *
187    * @param timestamp the timestamp
188    */
189   public void setCheckoutTimestamp(long timestamp) {
190     this.checkoutTimestamp = timestamp;
191   }
192 
193   /**
194    * Getter for the time that this connection has been checked out.
195    *
196    * @return the time
197    */
198   public long getCheckoutTime() {
199     return System.currentTimeMillis() - checkoutTimestamp;
200   }
201 
202   @Override
203   public int hashCode() {
204     return hashCode;
205   }
206 
207   /**
208    * Allows comparing this connection to another.
209    *
210    * @param obj - the other connection to test for equality
211    * @see Object#equals(Object)
212    */
213   @Override
214   public boolean equals(Object obj) {
215     if (obj instanceof PooledConnection) {
216       return realConnection.hashCode() == ((PooledConnection) obj).realConnection.hashCode();
217     } else if (obj instanceof Connection) {
218       return hashCode == obj.hashCode();
219     } else {
220       return false;
221     }
222   }
223 
224   /**
225    * Required for InvocationHandler implementation.
226    *
227    * @param proxy  - not used
228    * @param method - the method to be executed
229    * @param args   - the parameters to be passed to the method
230    * @see java.lang.reflect.InvocationHandler#invoke(Object, java.lang.reflect.Method, Object[])
231    */
232   @Override
233   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
234     String methodName = method.getName();
235     if (CLOSE.equals(methodName)) {
236       dataSource.pushConnection(this);
237       return null;
238     }
239     try {
240       if (!Object.class.equals(method.getDeclaringClass())) {
241         // issue #579 toString() should never fail
242         // throw an SQLException instead of a Runtime
243         checkConnection();
244       }
245       return method.invoke(realConnection, args);
246     } catch (Throwable t) {
247       throw ExceptionUtil.unwrapThrowable(t);
248     }
249 
250   }
251 
252   private void checkConnection() throws SQLException {
253     if (!valid) {
254       throw new SQLException("Error accessing PooledConnection. Connection is invalid.");
255     }
256   }
257 
258 }