public void addStatementEventListener(StatementEventListener sel) {
    if (logger.isLoggable(MLevel.INFO))
      logger.info(
          "Per the JDBC4 spec, "
              + this.getClass().getName()
              + " accepts StatementListeners, but for now there is no circumstance under which they are notified!");

    ses.addStatementEventListener(sel);
  }
  boolean closeAndRemoveResourcesInSet(Set s, Method closeMethod) {
    boolean okay = true;

    Set temp;
    synchronized (s) {
      temp = new HashSet(s);
    }

    for (Iterator ii = temp.iterator(); ii.hasNext(); ) {
      Object rsrc = ii.next();
      try {
        closeMethod.invoke(rsrc, CLOSE_ARGS);
      } catch (Exception e) {
        Throwable t = e;
        if (t instanceof InvocationTargetException)
          t = ((InvocationTargetException) e).getTargetException();
        logger.log(MLevel.WARNING, "An exception occurred while cleaning up a resource.", t);
        // t.printStackTrace();
        okay = false;
      } finally {
        s.remove(rsrc);
      }
    }

    // We had to abandon the idea of simply iterating over s directly, because
    // our resource close methods sometimes try to remove the resource from
    // its parent Set. This is important (when the user closes the resources
    // directly), but leads to ConcurrenModificationExceptions while we are
    // iterating over the Set to close. So, now we iterate over a copy, but remove
    // from the original Set. Since removal is idempotent, it don't matter if
    // the close method already provoked a remove. Sucks that we have to copy
    // the set though.
    //
    // Original (direct iteration) version:
    //
    //  	synchronized (s)
    //  	    {
    //  		for (Iterator ii = s.iterator(); ii.hasNext(); )
    //  		    {
    //  			Object rsrc = ii.next();
    //  			try
    //  			    { closeMethod.invoke(rsrc, CLOSE_ARGS); }
    //  			catch (Exception e)
    //  			    {
    //  				Throwable t = e;
    //  				if (t instanceof InvocationTargetException)
    //  				    t = ((InvocationTargetException) e).getTargetException();
    //  				t.printStackTrace();
    //  				okay = false;
    //  			    }
    //  			finally
    //  			    { ii.remove(); }
    //  		    }
    //  	    }

    return okay;
  }
 synchronized void closeMaybeCheckedOut(boolean checked_out) throws SQLException {
   if (checked_out) {
     // reset transaction state
     try {
       C3P0ImplUtils.resetTxnState(
           physicalConnection, forceIgnoreUnresolvedTransactions, autoCommitOnClose, false);
     } catch (Exception e) {
       if (logger.isLoggable(MLevel.FINER))
         logger.log(
             MLevel.FINER,
             "Failed to reset the transaction state of  "
                 + physicalConnection
                 + "just prior to close(). "
                 + "Only relevant at all if this was a Connection being forced close()ed midtransaction.",
             e);
     }
   }
   close(false);
 }
  private void reset(boolean known_resolved_txn) throws SQLException {
    ensureOkay();
    C3P0ImplUtils.resetTxnState(
        physicalConnection,
        forceIgnoreUnresolvedTransactions,
        autoCommitOnClose,
        known_resolved_txn);
    if (isolation_lvl_nondefault) {
      physicalConnection.setTransactionIsolation(dflt_txn_isolation);
      isolation_lvl_nondefault = false;
    }
    if (catalog_nondefault) {
      physicalConnection.setCatalog(dflt_catalog);
      catalog_nondefault = false;
    }
    if (holdability_nondefault) // we don't test if holdability is supported, 'cuz it can never go
                                // nondefault if it's not.
    {
      physicalConnection.setHoldability(dflt_holdability);
      holdability_nondefault = false;
    }

    try {
      physicalConnection.setReadOnly(false);
    } catch (Throwable t) {
      if (logger.isLoggable(MLevel.FINE))
        logger.log(
            MLevel.FINE,
            "A Throwable occurred while trying to reset the readOnly property of our Connection to false!",
            t);
    }

    try {
      if (supports_setTypeMap) physicalConnection.setTypeMap(Collections.EMPTY_MAP);
    } catch (Throwable t) {
      if (logger.isLoggable(MLevel.FINE))
        logger.log(
            MLevel.FINE,
            "A Throwable occurred while trying to reset the typeMap property of our Connection to Collections.EMPTY_MAP!",
            t);
    }
  }
  // must be called from sync'ed method
  private void doBadUpdate(int new_status) {
    this.connection_status = new_status;
    try {
      this.close(true);
    } catch (SQLException e) {
      // 		System.err.print("Broken Connection Close Error: ");
      // 		e.printStackTrace();

      logger.log(MLevel.WARNING, "Broken Connection Close Error. ", e);
    }
  }
 boolean closeAndRemoveResultSets(Set rsSet) {
   boolean okay = true;
   synchronized (rsSet) {
     for (Iterator ii = rsSet.iterator(); ii.hasNext(); ) {
       ResultSet rs = (ResultSet) ii.next();
       try {
         rs.close();
       } catch (SQLException e) {
         if (Debug.DEBUG)
           logger.log(MLevel.WARNING, "An exception occurred while cleaning up a ResultSet.", e);
         // e.printStackTrace();
         okay = false;
       } finally {
         ii.remove();
       }
     }
   }
   return okay;
 }
  // must be called from sync'ed method to protecte
  // exposedProxy
  private Connection getCreateNewConnection() throws SQLException {
    try {
      // DEBUG
      // origGet = new Exception("[DOUBLE_GET_TESTER] -- Orig Get");

      ensureOkay();
      /*
       * we reset the physical connection when we close an exposed proxy
       * no need to do it again when we create one
       */
      // reset();
      return (exposedProxy = createProxyConnection());
    } catch (SQLException e) {
      throw e;
    } catch (Exception e) {
      // e.printStackTrace();
      logger.log(MLevel.WARNING, "Failed to acquire connection!", e);
      throw new SQLException("Failed to acquire connection!");
    }
  }
  // synchronized to protect exposedProxy
  public synchronized Connection getConnection() throws SQLException {
    if (exposedProxy != null) {
      // DEBUG
      // System.err.println("[DOUBLE_GET_TESTER] -- double getting a Connection from " + this );
      // new Exception("[DOUBLE_GET_TESTER] -- Double-Get Stack Trace").printStackTrace();
      // origGet.printStackTrace();

      // 		System.err.println("c3p0 -- Uh oh... getConnection() was called on a PooledConnection
      // when " +
      // 				   "it had already provided a client with a Connection that has not yet been " +
      // 				   "closed. This probably indicates a bug in the connection pool!!!");

      logger.warning(
          "c3p0 -- Uh oh... getConnection() was called on a PooledConnection when "
              + "it had already provided a client with a Connection that has not yet been "
              + "closed. This probably indicates a bug in the connection pool!!!");

      return exposedProxy;
    } else {
      return getCreateNewConnection();
    }
  }
  static {
    try {
      CON_PROXY_CTOR = createProxyConstructor(ProxyConnection.class);

      Class[] argClasses = new Class[0];
      RS_CLOSE_METHOD = ResultSet.class.getMethod("close", argClasses);
      STMT_CLOSE_METHOD = Statement.class.getMethod("close", argClasses);

      CLOSE_ARGS = new Object[0];

      OBJECT_METHODS =
          Collections.unmodifiableSet(new HashSet(Arrays.asList(Object.class.getMethods())));
    } catch (Exception e) {
      // e.printStackTrace();
      logger.log(
          MLevel.SEVERE,
          "An Exception occurred in static initializer of" + C3P0PooledConnection.class.getName(),
          e);
      throw new InternalError(
          "Something is very wrong, or this is a pre 1.3 JVM."
              + "We cannot set up dynamic proxies and/or methods!");
    }
  }
  /*
   * TODO: factor all this convolution out into
   *       C3P0Statement
   */
  Statement createProxyStatement( // final Method cachedStmtProducingMethod,
      // final Object[] cachedStmtProducingMethodArgs,
      final boolean inner_is_cached,
      final Statement innerStmt)
      throws Exception {
    final Set activeResultSets = Collections.synchronizedSet(new HashSet());
    final Connection parentConnection = exposedProxy;

    if (Debug.DEBUG && parentConnection == null) {
      // 		System.err.print("PROBABLE C3P0 BUG -- ");
      // 		System.err.println(this + ": created a proxy Statement when there is no active, exposed
      // proxy Connection???");

      logger.warning(
          "PROBABLE C3P0 BUG -- "
              + this
              + ": created a proxy Statement when there is no active, exposed proxy Connection???");
    }

    // we can use this one wrapper under all circumstances
    // except jdbc3 CallableStatement multiple open ResultSets...
    // avoid object allocation in statement methods where possible.
    final StatementProxyingSetManagedResultSet mainResultSet =
        new StatementProxyingSetManagedResultSet(activeResultSets);

    class WrapperStatementHelper {
      Statement wrapperStmt;
      Statement nakedInner;

      public WrapperStatementHelper(Statement wrapperStmt, Statement nakedInner) {
        this.wrapperStmt = wrapperStmt;
        this.nakedInner = nakedInner;

        if (!inner_is_cached) uncachedActiveStatements.add(wrapperStmt);
      }

      private boolean closeAndRemoveActiveResultSets() {
        return closeAndRemoveResultSets(activeResultSets);
      }

      public ResultSet wrap(ResultSet rs) {
        if (mainResultSet.getInner() == null) {
          mainResultSet.setInner(rs);
          mainResultSet.setProxyStatement(wrapperStmt);
          return mainResultSet;
        } else {
          // for the case of multiple open ResultSets
          StatementProxyingSetManagedResultSet out =
              new StatementProxyingSetManagedResultSet(activeResultSets);
          out.setInner(rs);
          out.setProxyStatement(wrapperStmt);
          return out;
        }
      }

      public void doClose() throws SQLException {
        boolean okay = closeAndRemoveActiveResultSets();

        if (inner_is_cached) // this statement was cached
        scache.checkinStatement(innerStmt);
        else {
          innerStmt.close();
          uncachedActiveStatements.remove(wrapperStmt);
        }

        if (!okay) throw new SQLException("Failed to close an orphaned ResultSet properly.");
      }

      public Object doRawStatementOperation(Method m, Object target, Object[] args)
          throws IllegalAccessException, InvocationTargetException, SQLException {
        if (target == C3P0ProxyStatement.RAW_STATEMENT) target = nakedInner;
        for (int i = 0, len = args.length; i < len; ++i)
          if (args[i] == C3P0ProxyStatement.RAW_STATEMENT) args[i] = nakedInner;

        Object out = m.invoke(target, args);

        if (out instanceof ResultSet) out = wrap((ResultSet) out);

        return out;
      }
    }

    if (innerStmt instanceof CallableStatement) {
      class ProxyCallableStatement extends FilterCallableStatement implements C3P0ProxyStatement {
        WrapperStatementHelper wsh;

        ProxyCallableStatement(CallableStatement is) {
          super(is);
          this.wsh = new WrapperStatementHelper(this, is);
        }

        public Connection getConnection() {
          return parentConnection;
        }

        public ResultSet getResultSet() throws SQLException {
          return wsh.wrap(super.getResultSet());
        }

        public ResultSet getGeneratedKeys() throws SQLException {
          return wsh.wrap(super.getGeneratedKeys());
        }

        public ResultSet executeQuery(String sql) throws SQLException {
          return wsh.wrap(super.executeQuery(sql));
        }

        public ResultSet executeQuery() throws SQLException {
          return wsh.wrap(super.executeQuery());
        }

        public Object rawStatementOperation(Method m, Object target, Object[] args)
            throws IllegalAccessException, InvocationTargetException, SQLException {
          return wsh.doRawStatementOperation(m, target, args);
        }

        public void close() throws SQLException {
          wsh.doClose();
        }
      }

      return new ProxyCallableStatement((CallableStatement) innerStmt);
    } else if (innerStmt instanceof PreparedStatement) {
      class ProxyPreparedStatement extends FilterPreparedStatement implements C3P0ProxyStatement {
        WrapperStatementHelper wsh;

        ProxyPreparedStatement(PreparedStatement ps) {
          super(ps);
          this.wsh = new WrapperStatementHelper(this, ps);
        }

        public Connection getConnection() {
          return parentConnection;
        }

        public ResultSet getResultSet() throws SQLException {
          return wsh.wrap(super.getResultSet());
        }

        public ResultSet getGeneratedKeys() throws SQLException {
          return wsh.wrap(super.getGeneratedKeys());
        }

        public ResultSet executeQuery(String sql) throws SQLException {
          return wsh.wrap(super.executeQuery(sql));
        }

        public ResultSet executeQuery() throws SQLException {
          return wsh.wrap(super.executeQuery());
        }

        public Object rawStatementOperation(Method m, Object target, Object[] args)
            throws IllegalAccessException, InvocationTargetException, SQLException {
          return wsh.doRawStatementOperation(m, target, args);
        }

        public void close() throws SQLException {
          wsh.doClose();
        }
      }

      return new ProxyPreparedStatement((PreparedStatement) innerStmt);
    } else {
      class ProxyStatement extends FilterStatement implements C3P0ProxyStatement {
        WrapperStatementHelper wsh;

        ProxyStatement(Statement s) {
          super(s);
          this.wsh = new WrapperStatementHelper(this, s);
        }

        public Connection getConnection() {
          return parentConnection;
        }

        public ResultSet getResultSet() throws SQLException {
          return wsh.wrap(super.getResultSet());
        }

        public ResultSet getGeneratedKeys() throws SQLException {
          return wsh.wrap(super.getGeneratedKeys());
        }

        public ResultSet executeQuery(String sql) throws SQLException {
          return wsh.wrap(super.executeQuery(sql));
        }

        public Object rawStatementOperation(Method m, Object target, Object[] args)
            throws IllegalAccessException, InvocationTargetException, SQLException {
          return wsh.doRawStatementOperation(m, target, args);
        }

        public void close() throws SQLException {
          wsh.doClose();
        }
      }

      return new ProxyStatement(innerStmt);
    }
  }
  // TODO: factor out repetitive debugging code
  private synchronized void close(boolean known_invalid) throws SQLException {
    // System.err.println("Closing " + this);
    if (physicalConnection != null) {
      try {
        StringBuffer debugOnlyLog = null;
        if (Debug.DEBUG && known_invalid) {
          debugOnlyLog = new StringBuffer();
          debugOnlyLog.append("[ exceptions: ");
        }

        Exception exc = cleanupUncachedActiveStatements();
        if (Debug.DEBUG && exc != null) {
          if (known_invalid) debugOnlyLog.append(exc.toString() + ' ');
          else
            logger.log(
                MLevel.WARNING,
                "An exception occurred while cleaning up uncached active Statements.",
                exc);
          // exc.printStackTrace();
        }

        try {
          // we've got to use silentClose() rather than close() here,
          // 'cuz if there's still an exposedProxy (say a user forgot to
          // close his Connection) before we close, and we use regular (loud)
          // close, we will try to check this dead or dying PooledConnection
          // back into the pool. We only want to do this when close is called
          // on user proxies, and the underlying PooledConnection might still
          // be good. The PooledConnection itself should only be closed by the
          // pool.
          if (exposedProxy != null) exposedProxy.silentClose(known_invalid);
        } catch (Exception e) {
          if (Debug.DEBUG) {
            if (known_invalid) debugOnlyLog.append(e.toString() + ' ');
            else logger.log(MLevel.WARNING, "An exception occurred.", exc);
            // e.printStackTrace();
          }
          exc = e;
        }
        try {
          this.closeAll();
        } catch (Exception e) {
          if (Debug.DEBUG) {
            if (known_invalid) debugOnlyLog.append(e.toString() + ' ');
            else logger.log(MLevel.WARNING, "An exception occurred.", exc);
            // e.printStackTrace();
          }
          exc = e;
        }

        try {
          physicalConnection.close();
        } catch (Exception e) {
          if (Debug.DEBUG) {
            if (known_invalid) debugOnlyLog.append(e.toString() + ' ');
            else logger.log(MLevel.WARNING, "An exception occurred.", exc);
            e.printStackTrace();
          }
          exc = e;
        }

        if (exc != null) {
          if (known_invalid) {
            debugOnlyLog.append(" ]");
            if (Debug.DEBUG) {
              // 						System.err.print("[DEBUG]" + this + ": while closing a PooledConnection known
              // to be invalid, ");
              // 						System.err.println("  some exceptions occurred. This is probably not a
              // problem:");
              // 						System.err.println( debugOnlyLog.toString() );

              logger.fine(
                  this
                      + ": while closing a PooledConnection known to be invalid, "
                      + "  some exceptions occurred. This is probably not a problem: "
                      + debugOnlyLog.toString());
            }
          } else
            throw new SQLException(
                "At least one error occurred while attempting "
                    + "to close() the PooledConnection: "
                    + exc);
        }
        if (Debug.TRACE == Debug.TRACE_MAX)
          logger.fine("C3P0PooledConnection closed. [" + this + ']');
        // System.err.println("C3P0PooledConnection closed. [" + this + ']');
      } finally {
        physicalConnection = null;
      }
    }
  }