/**
  * Close the given Connection, obtained from the given DataSource, if it is not managed externally
  * (that is, not bound to the thread).
  *
  * @param con the Connection to close if necessary (if this is <code>null</code>, the call will be
  *     ignored)
  * @param dataSource the DataSource that the Connection was obtained from (may be <code>null
  *     </code>)
  * @see #getConnection
  */
 public static void releaseConnection(Connection con, DataSource dataSource) {
   try {
     doReleaseConnection(con, dataSource);
   } catch (SQLException ex) {
     logger.debug("Could not close JDBC Connection", ex);
   } catch (Throwable ex) {
     logger.debug("Unexpected exception on closing JDBC Connection", ex);
   }
 }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      // Invocation on ConnectionProxy interface coming in...

      if (method.getName().equals("equals")) {
        // Only considered as equal when proxies are identical.
        return (proxy == args[0]);
      } else if (method.getName().equals("hashCode")) {
        // Use hashCode of Connection proxy.
        return System.identityHashCode(proxy);
      } else if (method.getName().equals("toString")) {
        // Allow for differentiating between the proxy and the raw Connection.
        StringBuilder sb = new StringBuilder("Transaction-aware proxy for target Connection ");
        if (this.target != null) {
          sb.append("[").append(this.target.toString()).append("]");
        } else {
          sb.append(" from DataSource [").append(this.targetDataSource).append("]");
        }
        return sb.toString();
      } else if (method.getName().equals("isClosed")) {
        return this.closed;
      } else if (method.getName().equals("close")) {
        // Handle close method: only close if not within a transaction.
        DataSourceUtils.doReleaseConnection(this.target, this.targetDataSource);
        this.closed = true;
        return null;
      }

      if (this.target == null) {
        if (this.closed) {
          throw new SQLException("Connection handle already closed");
        }
        if (shouldObtainFixedConnection(this.targetDataSource)) {
          this.target = DataSourceUtils.doGetConnection(this.targetDataSource);
        }
      }
      Connection actualTarget = this.target;
      if (actualTarget == null) {
        actualTarget = DataSourceUtils.doGetConnection(this.targetDataSource);
      }

      if (method.getName().equals("getTargetConnection")) {
        // Handle getTargetConnection method: return underlying Connection.
        return actualTarget;
      }

      // Invoke method on target Connection.
      try {
        Object retVal = method.invoke(actualTarget, args);

        // If return value is a Statement, apply transaction timeout.
        // Applies to createStatement, prepareStatement, prepareCall.
        if (retVal instanceof Statement) {
          DataSourceUtils.applyTransactionTimeout((Statement) retVal, this.targetDataSource);
        }

        return retVal;
      } catch (InvocationTargetException ex) {
        throw ex.getTargetException();
      } finally {
        if (actualTarget != this.target) {
          DataSourceUtils.doReleaseConnection(actualTarget, this.targetDataSource);
        }
      }
    }