PooledConnection.java

  1. /*
  2.  *    Copyright 2009-2024 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.  *       https://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. import java.lang.reflect.InvocationHandler;
  18. import java.lang.reflect.Method;
  19. import java.lang.reflect.Proxy;
  20. import java.sql.Connection;
  21. import java.sql.SQLException;

  22. import org.apache.ibatis.reflection.ExceptionUtil;

  23. /**
  24.  * @author Clinton Begin
  25.  */
  26. class PooledConnection implements InvocationHandler {

  27.   private static final String CLOSE = "close";
  28.   private static final Class<?>[] IFACES = { Connection.class };

  29.   private final int hashCode;
  30.   private final PooledDataSource dataSource;
  31.   private final Connection realConnection;
  32.   private final Connection proxyConnection;
  33.   private long checkoutTimestamp;
  34.   private long createdTimestamp;
  35.   private long lastUsedTimestamp;
  36.   private int connectionTypeCode;
  37.   private boolean valid;

  38.   /**
  39.    * Constructor for SimplePooledConnection that uses the Connection and PooledDataSource passed in.
  40.    *
  41.    * @param connection
  42.    *          - the connection that is to be presented as a pooled connection
  43.    * @param dataSource
  44.    *          - the dataSource that the connection is from
  45.    */
  46.   public PooledConnection(Connection connection, PooledDataSource dataSource) {
  47.     this.hashCode = connection.hashCode();
  48.     this.realConnection = connection;
  49.     this.dataSource = dataSource;
  50.     this.createdTimestamp = System.currentTimeMillis();
  51.     this.lastUsedTimestamp = System.currentTimeMillis();
  52.     this.valid = true;
  53.     this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);
  54.   }

  55.   /**
  56.    * Invalidates the connection.
  57.    */
  58.   public void invalidate() {
  59.     valid = false;
  60.   }

  61.   /**
  62.    * Method to see if the connection is usable.
  63.    *
  64.    * @return True if the connection is usable
  65.    */
  66.   public boolean isValid() {
  67.     return valid && realConnection != null && dataSource.pingConnection(this);
  68.   }

  69.   /**
  70.    * Getter for the *real* connection that this wraps.
  71.    *
  72.    * @return The connection
  73.    */
  74.   public Connection getRealConnection() {
  75.     return realConnection;
  76.   }

  77.   /**
  78.    * Getter for the proxy for the connection.
  79.    *
  80.    * @return The proxy
  81.    */
  82.   public Connection getProxyConnection() {
  83.     return proxyConnection;
  84.   }

  85.   /**
  86.    * Gets the hashcode of the real connection (or 0 if it is null).
  87.    *
  88.    * @return The hashcode of the real connection (or 0 if it is null)
  89.    */
  90.   public int getRealHashCode() {
  91.     return realConnection == null ? 0 : realConnection.hashCode();
  92.   }

  93.   /**
  94.    * Getter for the connection type (based on url + user + password).
  95.    *
  96.    * @return The connection type
  97.    */
  98.   public int getConnectionTypeCode() {
  99.     return connectionTypeCode;
  100.   }

  101.   /**
  102.    * Setter for the connection type.
  103.    *
  104.    * @param connectionTypeCode
  105.    *          - the connection type
  106.    */
  107.   public void setConnectionTypeCode(int connectionTypeCode) {
  108.     this.connectionTypeCode = connectionTypeCode;
  109.   }

  110.   /**
  111.    * Getter for the time that the connection was created.
  112.    *
  113.    * @return The creation timestamp
  114.    */
  115.   public long getCreatedTimestamp() {
  116.     return createdTimestamp;
  117.   }

  118.   /**
  119.    * Setter for the time that the connection was created.
  120.    *
  121.    * @param createdTimestamp
  122.    *          - the timestamp
  123.    */
  124.   public void setCreatedTimestamp(long createdTimestamp) {
  125.     this.createdTimestamp = createdTimestamp;
  126.   }

  127.   /**
  128.    * Getter for the time that the connection was last used.
  129.    *
  130.    * @return - the timestamp
  131.    */
  132.   public long getLastUsedTimestamp() {
  133.     return lastUsedTimestamp;
  134.   }

  135.   /**
  136.    * Setter for the time that the connection was last used.
  137.    *
  138.    * @param lastUsedTimestamp
  139.    *          - the timestamp
  140.    */
  141.   public void setLastUsedTimestamp(long lastUsedTimestamp) {
  142.     this.lastUsedTimestamp = lastUsedTimestamp;
  143.   }

  144.   /**
  145.    * Getter for the time since this connection was last used.
  146.    *
  147.    * @return - the time since the last use
  148.    */
  149.   public long getTimeElapsedSinceLastUse() {
  150.     return System.currentTimeMillis() - lastUsedTimestamp;
  151.   }

  152.   /**
  153.    * Getter for the age of the connection.
  154.    *
  155.    * @return the age
  156.    */
  157.   public long getAge() {
  158.     return System.currentTimeMillis() - createdTimestamp;
  159.   }

  160.   /**
  161.    * Getter for the timestamp that this connection was checked out.
  162.    *
  163.    * @return the timestamp
  164.    */
  165.   public long getCheckoutTimestamp() {
  166.     return checkoutTimestamp;
  167.   }

  168.   /**
  169.    * Setter for the timestamp that this connection was checked out.
  170.    *
  171.    * @param timestamp
  172.    *          the timestamp
  173.    */
  174.   public void setCheckoutTimestamp(long timestamp) {
  175.     this.checkoutTimestamp = timestamp;
  176.   }

  177.   /**
  178.    * Getter for the time that this connection has been checked out.
  179.    *
  180.    * @return the time
  181.    */
  182.   public long getCheckoutTime() {
  183.     return System.currentTimeMillis() - checkoutTimestamp;
  184.   }

  185.   @Override
  186.   public int hashCode() {
  187.     return hashCode;
  188.   }

  189.   /**
  190.    * Allows comparing this connection to another.
  191.    *
  192.    * @param obj
  193.    *          - the other connection to test for equality
  194.    *
  195.    * @see Object#equals(Object)
  196.    */
  197.   @Override
  198.   public boolean equals(Object obj) {
  199.     if (obj instanceof PooledConnection) {
  200.       return realConnection.hashCode() == ((PooledConnection) obj).realConnection.hashCode();
  201.     }
  202.     if (obj instanceof Connection) {
  203.       return hashCode == obj.hashCode();
  204.     } else {
  205.       return false;
  206.     }
  207.   }

  208.   /**
  209.    * Required for InvocationHandler implementation.
  210.    *
  211.    * @param proxy
  212.    *          - not used
  213.    * @param method
  214.    *          - the method to be executed
  215.    * @param args
  216.    *          - the parameters to be passed to the method
  217.    *
  218.    * @see java.lang.reflect.InvocationHandler#invoke(Object, java.lang.reflect.Method, Object[])
  219.    */
  220.   @Override
  221.   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  222.     String methodName = method.getName();
  223.     if (CLOSE.equals(methodName)) {
  224.       dataSource.pushConnection(this);
  225.       return null;
  226.     }
  227.     try {
  228.       if (!Object.class.equals(method.getDeclaringClass())) {
  229.         // issue #579 toString() should never fail
  230.         // throw an SQLException instead of a Runtime
  231.         checkConnection();
  232.       }
  233.       return method.invoke(realConnection, args);
  234.     } catch (Throwable t) {
  235.       throw ExceptionUtil.unwrapThrowable(t);
  236.     }

  237.   }

  238.   private void checkConnection() throws SQLException {
  239.     if (!valid) {
  240.       throw new SQLException("Error accessing PooledConnection. Connection is invalid.");
  241.     }
  242.   }

  243. }