You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ibatis.apache.org by cb...@apache.org on 2008/08/08 01:21:58 UTC

svn commit: r683745 [17/22] - in /ibatis/trunk/java/ibatis-3: ./ ibatis-3-compat/ ibatis-3-compat/src/ ibatis-3-compat/src/main/ ibatis-3-compat/src/main/java/ ibatis-3-compat/src/main/java/com/ ibatis-3-compat/src/main/java/com/ibatis/ ibatis-3-compat...

Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/jdbc/SimpleDataSource.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/jdbc/SimpleDataSource.java?rev=683745&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/jdbc/SimpleDataSource.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/jdbc/SimpleDataSource.java Thu Aug  7 16:21:46 2008
@@ -0,0 +1,1086 @@
+package org.apache.ibatis.jdbc;
+
+import org.apache.ibatis.logging.*;
+import org.apache.ibatis.reflection.ExceptionUtil;
+
+import javax.sql.DataSource;
+import java.io.PrintWriter;
+import java.lang.reflect.*;
+import java.sql.*;
+import java.util.*;
+
+/**
+ * This is a simple, synchronous, thread-safe database connection pool.
+ */
+public class SimpleDataSource implements DataSource {
+
+  private static final Log log = LogFactory.getLog(SimpleDataSource.class);
+
+  private int expectedConnectionTypeCode;
+
+  // FIELDS LOCKED BY POOL_LOCK
+  private final Object POOL_LOCK = new Object();
+  private List idleConnections = new ArrayList();
+  private List activeConnections = new ArrayList();
+  private long requestCount = 0;
+  private long accumulatedRequestTime = 0;
+  private long accumulatedCheckoutTime = 0;
+  private long claimedOverdueConnectionCount = 0;
+  private long accumulatedCheckoutTimeOfOverdueConnections = 0;
+  private long accumulatedWaitTime = 0;
+  private long hadToWaitCount = 0;
+  private long badConnectionCount = 0;
+
+  // REQUIRED CONFIGURATION FIELDS
+  private String jdbcDriver;
+  private String jdbcUrl;
+  private String jdbcUsername;
+  private String jdbcPassword;
+
+  // OPTIONAL CONFIGURATION FIELDS
+  private boolean jdbcDefaultAutoCommit = false;
+  private Properties jdbcDriverProperties;
+  private int poolMaximumActiveConnections = 10;
+  private int poolMaximumIdleConnections = 5;
+  private int poolMaximumCheckoutTime = 20000;
+  private int poolTimeToWait = 20000;
+  private String poolPingQuery = "NO PING QUERY SET";
+  private boolean poolPingEnabled = false;
+  private int poolPingConnectionsNotUsedFor = 0;
+
+  /**
+   * @see javax.sql.DataSource#getConnection()
+   */
+  public Connection getConnection() throws SQLException {
+    return popConnection(jdbcUsername, jdbcPassword).getProxyConnection();
+  }
+
+  /**
+   * @see javax.sql.DataSource#getConnection(String, String)
+   */
+  public Connection getConnection(String username, String password) throws SQLException {
+    return popConnection(username, password).getProxyConnection();
+  }
+
+  /**
+   * @see javax.sql.DataSource#setLoginTimeout(int)
+   */
+  public void setLoginTimeout(int loginTimeout) throws SQLException {
+    DriverManager.setLoginTimeout(loginTimeout);
+  }
+
+  /**
+   * @see javax.sql.DataSource#getLoginTimeout()
+   */
+  public int getLoginTimeout() throws SQLException {
+    return DriverManager.getLoginTimeout();
+  }
+
+  /**
+   * @see javax.sql.DataSource#setLogWriter(java.io.PrintWriter)
+   */
+  public void setLogWriter(PrintWriter logWriter) throws SQLException {
+    DriverManager.setLogWriter(logWriter);
+  }
+
+  /**
+   * @see javax.sql.DataSource#getLogWriter()
+   */
+  public PrintWriter getLogWriter() throws SQLException {
+    return DriverManager.getLogWriter();
+  }
+
+  /**
+   * Name of the JDBC driver class to be used.
+   *
+   * @param jdbcDriver The string name of the class
+   */
+  public void setJdbcDriver(String jdbcDriver) {
+    this.jdbcDriver = jdbcDriver;
+    try {
+      Class driverType = Class.forName(jdbcDriver);
+      DriverManager.registerDriver((Driver) driverType.newInstance());
+    } catch (Exception e) {
+      throw new RuntimeException("Error setting driver on SimpleDataSource. Cause: " + e, e);
+    }
+    forceCloseAll();
+  }
+
+  /**
+   * The JDBC URL to use.
+   *
+   * @param jdbcUrl The JDBC URL
+   */
+  public void setJdbcUrl(String jdbcUrl) {
+    this.jdbcUrl = jdbcUrl;
+    forceCloseAll();
+  }
+
+  /**
+   * The JDBC user name to use
+   *
+   * @param jdbcUsername The user name
+   */
+  public void setJdbcUsername(String jdbcUsername) {
+    this.jdbcUsername = jdbcUsername;
+    forceCloseAll();
+  }
+
+  /**
+   * The JDBC password to use
+   *
+   * @param jdbcPassword The password
+   */
+  public void setJdbcPassword(String jdbcPassword) {
+    this.jdbcPassword = jdbcPassword;
+    forceCloseAll();
+  }
+
+  /**
+   * Recommend leaving this false.  Basically disables transactions if set to true.
+   *
+   * @param jdbcDefaultAutoCommit
+   */
+
+  public void setJdbcDefaultAutoCommit(boolean jdbcDefaultAutoCommit) {
+    this.jdbcDefaultAutoCommit = jdbcDefaultAutoCommit;
+    forceCloseAll();
+  }
+
+  /**
+   * Use alternate connection initializer with properties that may be specific
+   * to your driver.
+   *
+   * @param driverProps The properties for your driver
+   */
+  public void setJdbcDriverProperties(Properties driverProps) {
+    this.jdbcDriverProperties = driverProps;
+    forceCloseAll();
+  }
+
+  /**
+   * The maximum number of active connections
+   *
+   * @param poolMaximumActiveConnections The maximum number of active connections
+   */
+  public void setPoolMaximumActiveConnections(int poolMaximumActiveConnections) {
+    this.poolMaximumActiveConnections = poolMaximumActiveConnections;
+    forceCloseAll();
+  }
+
+  /**
+   * The maximum number of idle connections
+   *
+   * @param poolMaximumIdleConnections The maximum number of idle connections
+   */
+  public void setPoolMaximumIdleConnections(int poolMaximumIdleConnections) {
+    this.poolMaximumIdleConnections = poolMaximumIdleConnections;
+    forceCloseAll();
+  }
+
+  /**
+   * The maximum time a connection can be used before it *may* be
+   * given away again.
+   *
+   * @param poolMaximumCheckoutTime The maximum time
+   */
+  public void setPoolMaximumCheckoutTime(int poolMaximumCheckoutTime) {
+    this.poolMaximumCheckoutTime = poolMaximumCheckoutTime;
+    forceCloseAll();
+  }
+
+  /**
+   * The time to wait before retrying to get a connection
+   *
+   * @param poolTimeToWait The time to wait
+   */
+  public void setPoolTimeToWait(int poolTimeToWait) {
+    this.poolTimeToWait = poolTimeToWait;
+    forceCloseAll();
+  }
+
+  /**
+   * The query to be used to check a connection
+   *
+   * @param poolPingQuery The query
+   */
+  public void setPoolPingQuery(String poolPingQuery) {
+    this.poolPingQuery = poolPingQuery;
+    forceCloseAll();
+  }
+
+  /**
+   * Determines if the ping query should be used.
+   *
+   * @param poolPingEnabled True if we need to check a connection before using it
+   */
+  public void setPoolPingEnabled(boolean poolPingEnabled) {
+    this.poolPingEnabled = poolPingEnabled;
+    forceCloseAll();
+  }
+
+  /**
+   * If a connection has not been used in this many milliseconds, ping the
+   * database to make sure the connection is still good.
+   *
+   * @param milliseconds the number of milliseconds of inactivity that will trigger a ping
+   */
+  public void setPoolPingConnectionsNotUsedFor(int milliseconds) {
+    this.poolPingConnectionsNotUsedFor = milliseconds;
+    forceCloseAll();
+  }
+
+  public String getJdbcDriver() {
+    return jdbcDriver;
+  }
+
+  public String getJdbcUrl() {
+    return jdbcUrl;
+  }
+
+  public String getJdbcUsername() {
+    return jdbcUsername;
+  }
+
+  public String getJdbcPassword() {
+    return jdbcPassword;
+  }
+
+  public boolean isJdbcDefaultAutoCommit() {
+    return jdbcDefaultAutoCommit;
+  }
+
+  public Properties getJdbcDriverProperties() {
+    return jdbcDriverProperties;
+  }
+
+  public int getPoolMaximumActiveConnections() {
+    return poolMaximumActiveConnections;
+  }
+
+  public int getPoolMaximumIdleConnections() {
+    return poolMaximumIdleConnections;
+  }
+
+  public int getPoolMaximumCheckoutTime() {
+    return poolMaximumCheckoutTime;
+  }
+
+  public int getPoolTimeToWait() {
+    return poolTimeToWait;
+  }
+
+  public String getPoolPingQuery() {
+    return poolPingQuery;
+  }
+
+  public boolean isPoolPingEnabled() {
+    return poolPingEnabled;
+  }
+
+  public int getPoolPingConnectionsNotUsedFor() {
+    return poolPingConnectionsNotUsedFor;
+  }
+
+  /**
+   * Getter for the number of connection requests made
+   *
+   * @return The number of connection requests made
+   */
+  public long getRequestCount() {
+    synchronized (POOL_LOCK) {
+      return requestCount;
+    }
+  }
+
+  /**
+   * Getter for the average time required to get a connection to the database
+   *
+   * @return The average time
+   */
+  public long getAverageRequestTime() {
+    synchronized (POOL_LOCK) {
+      return requestCount == 0 ? 0 : accumulatedRequestTime / requestCount;
+    }
+  }
+
+  /**
+   * Getter for the average time spent waiting for connections that were in use
+   *
+   * @return The average time
+   */
+  public long getAverageWaitTime() {
+    synchronized (POOL_LOCK) {
+      return hadToWaitCount == 0 ? 0 : accumulatedWaitTime / hadToWaitCount;
+    }
+  }
+
+  /**
+   * Getter for the number of requests that had to wait for connections that were in use
+   *
+   * @return The number of requests that had to wait
+   */
+  public long getHadToWaitCount() {
+    synchronized (POOL_LOCK) {
+      return hadToWaitCount;
+    }
+  }
+
+  /**
+   * Getter for the number of invalid connections that were found in the pool
+   *
+   * @return The number of invalid connections
+   */
+  public long getBadConnectionCount() {
+    synchronized (POOL_LOCK) {
+      return badConnectionCount;
+    }
+  }
+
+  /**
+   * Getter for the number of connections that were claimed before they were returned
+   *
+   * @return The number of connections
+   */
+  public long getClaimedOverdueConnectionCount() {
+    synchronized (POOL_LOCK) {
+      return claimedOverdueConnectionCount;
+    }
+  }
+
+  /**
+   * Getter for the average age of overdue connections
+   *
+   * @return The average age
+   */
+  public long getAverageOverdueCheckoutTime() {
+    synchronized (POOL_LOCK) {
+      return claimedOverdueConnectionCount == 0 ? 0 : accumulatedCheckoutTimeOfOverdueConnections / claimedOverdueConnectionCount;
+    }
+  }
+
+
+  /**
+   * Getter for the average age of a connection checkout
+   *
+   * @return The average age
+   */
+  public long getAverageCheckoutTime() {
+    synchronized (POOL_LOCK) {
+      return requestCount == 0 ? 0 : accumulatedCheckoutTime / requestCount;
+    }
+  }
+
+
+  public int getIdleConnectionCount() {
+    synchronized (POOL_LOCK) {
+      return idleConnections.size();
+    }
+  }
+
+  public int getActiveConnectionCount() {
+    synchronized (POOL_LOCK) {
+      return activeConnections.size();
+    }
+  }
+
+
+  /**
+   * Returns the status of the connection pool
+   *
+   * @return The status
+   */
+  public String getStatus() {
+    StringBuffer buffer = new StringBuffer();
+    buffer.append("\n===CONFINGURATION==============================================");
+    buffer.append("\n jdbcDriver                     ").append(jdbcDriver);
+    buffer.append("\n jdbcUrl                        ").append(jdbcUrl);
+    buffer.append("\n jdbcUsername                   ").append(jdbcUsername);
+    buffer.append("\n jdbcPassword                   ").append((jdbcPassword == null ? "NULL" : "************"));
+    buffer.append("\n poolMaxActiveConnections       ").append(poolMaximumActiveConnections);
+    buffer.append("\n poolMaxIdleConnections         ").append(poolMaximumIdleConnections);
+    buffer.append("\n poolMaxCheckoutTime            ").append(poolMaximumCheckoutTime);
+    buffer.append("\n poolTimeToWait                 ").append(poolTimeToWait);
+    buffer.append("\n poolPingEnabled                ").append(poolPingEnabled);
+    buffer.append("\n poolPingQuery                  ").append(poolPingQuery);
+    buffer.append("\n poolPingConnectionsNotUsedFor  ").append(poolPingConnectionsNotUsedFor);
+    buffer.append("\n ---STATUS-----------------------------------------------------");
+    synchronized (POOL_LOCK) {
+      buffer.append("\n activeConnections              ").append(getActiveConnectionCount());
+      buffer.append("\n idleConnections                ").append(getIdleConnectionCount());
+      buffer.append("\n requestCount                   ").append(getRequestCount());
+      buffer.append("\n averageRequestTime             ").append(getAverageRequestTime());
+      buffer.append("\n averageCheckoutTime            ").append(getAverageCheckoutTime());
+      buffer.append("\n claimedOverdue                 ").append(getClaimedOverdueConnectionCount());
+      buffer.append("\n averageOverdueCheckoutTime     ").append(getAverageOverdueCheckoutTime());
+      buffer.append("\n hadToWait                      ").append(getHadToWaitCount());
+      buffer.append("\n averageWaitTime                ").append(getAverageWaitTime());
+      buffer.append("\n badConnectionCount             ").append(getBadConnectionCount());
+    }
+    buffer.append("\n===============================================================");
+    return buffer.toString();
+  }
+
+  /**
+   * Closes all active and idle connections in the pool
+   */
+  public void forceCloseAll() {
+    synchronized (POOL_LOCK) {
+      expectedConnectionTypeCode = assembleConnectionTypeCode(jdbcUrl, jdbcUsername, jdbcPassword);
+      for (int i = activeConnections.size(); i > 0; i--) {
+        try {
+          SimplePooledConnection conn = (SimplePooledConnection) activeConnections.remove(i - 1);
+          conn.invalidate();
+
+          Connection realConn = conn.getRealConnection();
+          if (!realConn.getAutoCommit()) {
+            realConn.rollback();
+          }
+          realConn.close();
+        } catch (Exception e) {
+          // ignore
+        }
+      }
+      for (int i = idleConnections.size(); i > 0; i--) {
+        try {
+          SimplePooledConnection conn = (SimplePooledConnection) idleConnections.remove(i - 1);
+          conn.invalidate();
+
+          Connection realConn = conn.getRealConnection();
+          if (!realConn.getAutoCommit()) {
+            realConn.rollback();
+          }
+          realConn.close();
+        } catch (Exception e) {
+          // ignore
+        }
+      }
+    }
+    if (log.isDebugEnabled()) {
+      log.debug("SimpleDataSource forcefully closed/removed all connections.");
+    }
+  }
+
+  private int assembleConnectionTypeCode(String url, String username, String password) {
+    return ("" + url + username + password).hashCode();
+  }
+
+  private void pushConnection(SimplePooledConnection conn)
+      throws SQLException {
+
+    synchronized (POOL_LOCK) {
+      activeConnections.remove(conn);
+      if (conn.isValid()) {
+        if (idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) {
+          accumulatedCheckoutTime += conn.getCheckoutTime();
+          if (!conn.getRealConnection().getAutoCommit()) {
+            conn.getRealConnection().rollback();
+          }
+          SimplePooledConnection newConn = new SimplePooledConnection(conn.getRealConnection(), this);
+          idleConnections.add(newConn);
+          newConn.setCreatedTimestamp(conn.getCreatedTimestamp());
+          newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());
+          conn.invalidate();
+          if (log.isDebugEnabled()) {
+            log.debug("Returned connection " + newConn.getRealHashCode() + " to pool.");
+          }
+          POOL_LOCK.notifyAll();
+        } else {
+          accumulatedCheckoutTime += conn.getCheckoutTime();
+          if (!conn.getRealConnection().getAutoCommit()) {
+            conn.getRealConnection().rollback();
+          }
+          conn.getRealConnection().close();
+          if (log.isDebugEnabled()) {
+            log.debug("Closed connection " + conn.getRealHashCode() + ".");
+          }
+          conn.invalidate();
+        }
+      } else {
+        if (log.isDebugEnabled()) {
+          log.debug("A bad connection (" + conn.getRealHashCode() + ") attempted to return to the pool, discarding connection.");
+        }
+        badConnectionCount++;
+      }
+    }
+  }
+
+  private SimplePooledConnection popConnection(String username, String password)
+      throws SQLException {
+    boolean countedWait = false;
+    SimplePooledConnection conn = null;
+    long t = System.currentTimeMillis();
+    int localBadConnectionCount = 0;
+
+    while (conn == null) {
+      synchronized (POOL_LOCK) {
+        if (idleConnections.size() > 0) {
+          // Pool has available connection
+          conn = (SimplePooledConnection) idleConnections.remove(0);
+          if (log.isDebugEnabled()) {
+            log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
+          }
+        } else {
+          // Pool does not have available connection
+          if (activeConnections.size() < poolMaximumActiveConnections) {
+            // Can create new connection
+            if (jdbcDriverProperties != null) {
+              jdbcDriverProperties.put("user", jdbcUsername);
+              jdbcDriverProperties.put("password", jdbcPassword);
+              conn = new SimplePooledConnection(DriverManager.getConnection(jdbcUrl, jdbcDriverProperties), this);
+            } else {
+              conn = new SimplePooledConnection(DriverManager.getConnection(jdbcUrl, jdbcUsername, jdbcPassword), this);
+            }
+            Connection realConn = conn.getRealConnection();
+            if (realConn.getAutoCommit() != jdbcDefaultAutoCommit) {
+              realConn.setAutoCommit(jdbcDefaultAutoCommit);
+            }
+            if (log.isDebugEnabled()) {
+              log.debug("Created connection " + conn.getRealHashCode() + ".");
+            }
+          } else {
+            // Cannot create new connection
+            SimplePooledConnection oldestActiveConnection = (SimplePooledConnection) activeConnections.get(0);
+            long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
+            if (longestCheckoutTime > poolMaximumCheckoutTime) {
+              // Can claim overdue connection
+              claimedOverdueConnectionCount++;
+              accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
+              accumulatedCheckoutTime += longestCheckoutTime;
+              activeConnections.remove(oldestActiveConnection);
+              if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
+                oldestActiveConnection.getRealConnection().rollback();
+              }
+              conn = new SimplePooledConnection(oldestActiveConnection.getRealConnection(), this);
+              oldestActiveConnection.invalidate();
+              if (log.isDebugEnabled()) {
+                log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
+              }
+            } else {
+              // Must wait
+              try {
+                if (!countedWait) {
+                  hadToWaitCount++;
+                  countedWait = true;
+                }
+                if (log.isDebugEnabled()) {
+                  log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection.");
+                }
+                long wt = System.currentTimeMillis();
+                POOL_LOCK.wait(poolTimeToWait);
+                accumulatedWaitTime += System.currentTimeMillis() - wt;
+              } catch (InterruptedException e) {
+                break;
+              }
+            }
+          }
+        }
+        if (conn != null) {
+          if (conn.isValid()) {
+            if (!conn.getRealConnection().getAutoCommit()) {
+              conn.getRealConnection().rollback();
+            }
+            conn.setConnectionTypeCode(assembleConnectionTypeCode(jdbcUrl, username, password));
+            conn.setCheckoutTimestamp(System.currentTimeMillis());
+            conn.setLastUsedTimestamp(System.currentTimeMillis());
+            activeConnections.add(conn);
+            requestCount++;
+            accumulatedRequestTime += System.currentTimeMillis() - t;
+          } else {
+            if (log.isDebugEnabled()) {
+              log.debug("A bad connection (" + conn.getRealHashCode() + ") was returned from the pool, getting another connection.");
+            }
+            badConnectionCount++;
+            localBadConnectionCount++;
+            conn = null;
+            if (localBadConnectionCount > (poolMaximumIdleConnections + 3)) {
+              if (log.isDebugEnabled()) {
+                log.debug("SimpleDataSource: Could not get a good connection to the database.");
+              }
+              throw new SQLException("SimpleDataSource: Could not get a good connection to the database.");
+            }
+          }
+        }
+      }
+
+    }
+
+    if (conn == null) {
+      if (log.isDebugEnabled()) {
+        log.debug("SimpleDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
+      }
+      throw new SQLException("SimpleDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
+    }
+
+    return conn;
+  }
+
+  /**
+   * Method to check to see if a connection is still usable
+   *
+   * @param conn - the connection to check
+   * @return True if the connection is still usable
+   */
+  private boolean pingConnection(SimplePooledConnection conn) {
+    boolean result = true;
+
+    try {
+      result = !conn.getRealConnection().isClosed();
+    } catch (SQLException e) {
+      if (log.isDebugEnabled()) {
+        log.debug("Connection " + conn.getRealHashCode() + " is BAD: " + e.getMessage());
+      }
+      result = false;
+    }
+
+    if (result) {
+      if (poolPingEnabled) {
+        if (poolPingConnectionsNotUsedFor > 0 && conn.getTimeElapsedSinceLastUse() > poolPingConnectionsNotUsedFor) {
+          try {
+            if (log.isDebugEnabled()) {
+              log.debug("Testing connection " + conn.getRealHashCode() + " ...");
+            }
+            Connection realConn = conn.getRealConnection();
+            Statement statement = realConn.createStatement();
+            ResultSet rs = statement.executeQuery(poolPingQuery);
+            rs.close();
+            statement.close();
+            if (!realConn.getAutoCommit()) {
+              realConn.rollback();
+            }
+            result = true;
+            if (log.isDebugEnabled()) {
+              log.debug("Connection " + conn.getRealHashCode() + " is GOOD!");
+            }
+          } catch (Exception e) {
+            log.warn("Execution of ping query '" + poolPingQuery + "' failed: " + e.getMessage());
+            try {
+              conn.getRealConnection().close();
+            } catch (Exception e2) {
+              //ignore
+            }
+            result = false;
+            if (log.isDebugEnabled()) {
+              log.debug("Connection " + conn.getRealHashCode() + " is BAD: " + e.getMessage());
+            }
+          }
+        }
+      }
+    }
+    return result;
+  }
+
+  /**
+   * Unwraps a pooled connection to get to the 'real' connection
+   *
+   * @param conn - the pooled connection to unwrap
+   * @return The 'real' connection
+   */
+  public static Connection unwrapConnection(Connection conn) {
+    if (conn instanceof SimplePooledConnection) {
+      return ((SimplePooledConnection) conn).getRealConnection();
+    } else {
+      return conn;
+    }
+  }
+
+  protected void finalize() throws Throwable {
+    forceCloseAll();
+  }
+
+  /**
+   * ---------------------------------------------------------------------------------------
+   * SimplePooledConnection
+   * ---------------------------------------------------------------------------------------
+   */
+  private static class SimplePooledConnection implements InvocationHandler {
+
+    private static final String CLOSE = "close";
+    private static final Class[] IFACES = new Class[]{Connection.class};
+
+    private int hashCode = 0;
+    private SimpleDataSource dataSource;
+    private Connection realConnection;
+    private Connection proxyConnection;
+    private long checkoutTimestamp;
+    private long createdTimestamp;
+    private long lastUsedTimestamp;
+    private int connectionTypeCode;
+    private boolean valid;
+
+    /**
+     * Constructor for SimplePooledConnection that uses the Connection and SimpleDataSource passed in
+     *
+     * @param connection - the connection that is to be presented as a pooled connection
+     * @param dataSource - the dataSource that the connection is from
+     */
+    public SimplePooledConnection(Connection connection, SimpleDataSource dataSource) {
+      this.hashCode = connection.hashCode();
+      this.realConnection = connection;
+      this.dataSource = dataSource;
+      this.createdTimestamp = System.currentTimeMillis();
+      this.lastUsedTimestamp = System.currentTimeMillis();
+      this.valid = true;
+
+      proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);
+    }
+
+    /**
+     * Invalidates the connection
+     */
+    public void invalidate() {
+      valid = false;
+    }
+
+    /**
+     * Method to see if the connection is usable
+     *
+     * @return True if the connection is usable
+     */
+    public boolean isValid() {
+      return valid && realConnection != null && dataSource.pingConnection(this);
+    }
+
+    /**
+     * Getter for the *real* connection that this wraps
+     *
+     * @return The connection
+     */
+    public Connection getRealConnection() {
+      return realConnection;
+    }
+
+    /**
+     * Getter for the proxy for the connection
+     *
+     * @return The proxy
+     */
+    public Connection getProxyConnection() {
+      return proxyConnection;
+    }
+
+    /**
+     * Gets the hashcode of the real connection (or 0 if it is null)
+     *
+     * @return The hashcode of the real connection (or 0 if it is null)
+     */
+    public int getRealHashCode() {
+      if (realConnection == null) {
+        return 0;
+      } else {
+        return realConnection.hashCode();
+      }
+    }
+
+    /**
+     * Getter for the connection type (based on url + user + password)
+     *
+     * @return The connection type
+     */
+    public int getConnectionTypeCode() {
+      return connectionTypeCode;
+    }
+
+    /**
+     * Setter for the connection type
+     *
+     * @param connectionTypeCode - the connection type
+     */
+    public void setConnectionTypeCode(int connectionTypeCode) {
+      this.connectionTypeCode = connectionTypeCode;
+    }
+
+    /**
+     * Getter for the time that the connection was created
+     *
+     * @return The creation timestamp
+     */
+    public long getCreatedTimestamp() {
+      return createdTimestamp;
+    }
+
+    /**
+     * Setter for the time that the connection was created
+     *
+     * @param createdTimestamp - the timestamp
+     */
+    public void setCreatedTimestamp(long createdTimestamp) {
+      this.createdTimestamp = createdTimestamp;
+    }
+
+    /**
+     * Getter for the time that the connection was last used
+     *
+     * @return - the timestamp
+     */
+    public long getLastUsedTimestamp() {
+      return lastUsedTimestamp;
+    }
+
+    /**
+     * Setter for the time that the connection was last used
+     *
+     * @param lastUsedTimestamp - the timestamp
+     */
+    public void setLastUsedTimestamp(long lastUsedTimestamp) {
+      this.lastUsedTimestamp = lastUsedTimestamp;
+    }
+
+    /**
+     * Getter for the time since this connection was last used
+     *
+     * @return - the time since the last use
+     */
+    public long getTimeElapsedSinceLastUse() {
+      return System.currentTimeMillis() - lastUsedTimestamp;
+    }
+
+    /**
+     * Getter for the age of the connection
+     *
+     * @return the age
+     */
+    public long getAge() {
+      return System.currentTimeMillis() - createdTimestamp;
+    }
+
+    /**
+     * Getter for the timestamp that this connection was checked out
+     *
+     * @return the timestamp
+     */
+    public long getCheckoutTimestamp() {
+      return checkoutTimestamp;
+    }
+
+    /**
+     * Setter for the timestamp that this connection was checked out
+     *
+     * @param timestamp the timestamp
+     */
+    public void setCheckoutTimestamp(long timestamp) {
+      this.checkoutTimestamp = timestamp;
+    }
+
+    /**
+     * Getter for the time that this connection has been checked out
+     *
+     * @return the time
+     */
+    public long getCheckoutTime() {
+      return System.currentTimeMillis() - checkoutTimestamp;
+    }
+
+    private Connection getValidConnection() {
+      if (!valid) {
+        throw new RuntimeException("Error accessing SimplePooledConnection. Connection is invalid.");
+      }
+      return realConnection;
+    }
+
+    public int hashCode() {
+      return hashCode;
+    }
+
+    /**
+     * Allows comparing this connection to another
+     *
+     * @param obj - the other connection to test for equality
+     * @see Object#equals(Object)
+     */
+    public boolean equals(Object obj) {
+      if (obj instanceof SimplePooledConnection) {
+        return realConnection.hashCode() == (((SimplePooledConnection) obj).realConnection.hashCode());
+      } else if (obj instanceof Connection) {
+        return hashCode == obj.hashCode();
+      } else {
+        return false;
+      }
+    }
+
+    // **********************************
+    // Implemented Connection Methods -- Now handled by proxy
+    // **********************************
+
+    /**
+     * Required for InvocationHandler implementation.
+     *
+     * @param proxy  - not used
+     * @param method - the method to be executed
+     * @param args   - the parameters to be passed to the method
+     * @see java.lang.reflect.InvocationHandler#invoke(Object, java.lang.reflect.Method, Object[])
+     */
+    public Object invoke(Object proxy, Method method, Object[] args)
+        throws Throwable {
+      String methodName = method.getName();
+      if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) {
+        dataSource.pushConnection(this);
+        return null;
+      } else {
+        try {
+          return method.invoke(getValidConnection(), args);
+        } catch (Throwable t) {
+          throw ExceptionUtil.unwrapThrowable(t);
+        }
+      }
+    }
+
+    public Statement createStatement() throws SQLException {
+      return getValidConnection().createStatement();
+    }
+
+    public PreparedStatement prepareStatement(String sql) throws SQLException {
+      return getValidConnection().prepareStatement(sql);
+    }
+
+    public CallableStatement prepareCall(String sql) throws SQLException {
+      return getValidConnection().prepareCall(sql);
+    }
+
+    public String nativeSQL(String sql) throws SQLException {
+      return getValidConnection().nativeSQL(sql);
+    }
+
+    public void setAutoCommit(boolean autoCommit) throws SQLException {
+      getValidConnection().setAutoCommit(autoCommit);
+    }
+
+    public boolean getAutoCommit() throws SQLException {
+      return getValidConnection().getAutoCommit();
+    }
+
+    public void commit() throws SQLException {
+      getValidConnection().commit();
+    }
+
+    public void rollback() throws SQLException {
+      getValidConnection().rollback();
+    }
+
+    public void close() throws SQLException {
+      dataSource.pushConnection(this);
+    }
+
+    public boolean isClosed() throws SQLException {
+      return getValidConnection().isClosed();
+    }
+
+    public DatabaseMetaData getMetaData() throws SQLException {
+      return getValidConnection().getMetaData();
+    }
+
+    public void setReadOnly(boolean readOnly) throws SQLException {
+      getValidConnection().setReadOnly(readOnly);
+    }
+
+    public boolean isReadOnly() throws SQLException {
+      return getValidConnection().isReadOnly();
+    }
+
+    public void setCatalog(String catalog) throws SQLException {
+      getValidConnection().setCatalog(catalog);
+    }
+
+    public String getCatalog() throws SQLException {
+      return getValidConnection().getCatalog();
+    }
+
+    public void setTransactionIsolation(int level) throws SQLException {
+      getValidConnection().setTransactionIsolation(level);
+    }
+
+    public int getTransactionIsolation() throws SQLException {
+      return getValidConnection().getTransactionIsolation();
+    }
+
+    public SQLWarning getWarnings() throws SQLException {
+      return getValidConnection().getWarnings();
+    }
+
+    public void clearWarnings() throws SQLException {
+      getValidConnection().clearWarnings();
+    }
+
+    public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
+      return getValidConnection().createStatement(resultSetType, resultSetConcurrency);
+    }
+
+    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
+      return getValidConnection().prepareCall(sql, resultSetType, resultSetConcurrency);
+    }
+
+    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
+      return getValidConnection().prepareCall(sql, resultSetType, resultSetConcurrency);
+    }
+
+    public Map getTypeMap() throws SQLException {
+      return getValidConnection().getTypeMap();
+    }
+
+    public void setTypeMap(Map map) throws SQLException {
+      getValidConnection().setTypeMap(map);
+    }
+
+    // **********************************
+    // JDK 1.4 JDBC 3.0 Methods below
+    // **********************************
+
+    public void setHoldability(int holdability) throws SQLException {
+      getValidConnection().setHoldability(holdability);
+    }
+
+    public int getHoldability() throws SQLException {
+      return getValidConnection().getHoldability();
+    }
+
+    public Savepoint setSavepoint() throws SQLException {
+      return getValidConnection().setSavepoint();
+    }
+
+    public Savepoint setSavepoint(String name) throws SQLException {
+      return getValidConnection().setSavepoint(name);
+    }
+
+    public void rollback(Savepoint savepoint) throws SQLException {
+      getValidConnection().rollback(savepoint);
+    }
+
+    public void releaseSavepoint(Savepoint savepoint) throws SQLException {
+      getValidConnection().releaseSavepoint(savepoint);
+    }
+
+    public Statement createStatement(int resultSetType, int resultSetConcurrency,
+                                     int resultSetHoldability) throws SQLException {
+      return getValidConnection().createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
+    }
+
+    public PreparedStatement prepareStatement(String sql, int resultSetType,
+                                              int resultSetConcurrency, int resultSetHoldability)
+        throws SQLException {
+      return getValidConnection().prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
+    }
+
+    public CallableStatement prepareCall(String sql, int resultSetType,
+                                         int resultSetConcurrency,
+                                         int resultSetHoldability) throws SQLException {
+      return getValidConnection().prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
+    }
+
+    public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)
+        throws SQLException {
+      return getValidConnection().prepareStatement(sql, autoGeneratedKeys);
+    }
+
+    public PreparedStatement prepareStatement(String sql, int columnIndexes[])
+        throws SQLException {
+      return getValidConnection().prepareStatement(sql, columnIndexes);
+    }
+
+    public PreparedStatement prepareStatement(String sql, String columnNames[])
+        throws SQLException {
+      return getValidConnection().prepareStatement(sql, columnNames);
+    }
+
+
+  }
+}

Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/Log.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/Log.java?rev=683745&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/Log.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/Log.java Thu Aug  7 16:21:46 2008
@@ -0,0 +1,15 @@
+package org.apache.ibatis.logging;
+
+public interface Log {
+
+  boolean isDebugEnabled();
+
+  void error(String s, Throwable e);
+
+  void error(String s);
+
+  public void debug(String s);
+
+  public void warn(String s);
+
+}

Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/LogFactory.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/LogFactory.java?rev=683745&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/LogFactory.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/LogFactory.java Thu Aug  7 16:21:46 2008
@@ -0,0 +1,84 @@
+package org.apache.ibatis.logging;
+
+import java.lang.reflect.Constructor;
+
+public class LogFactory {
+
+  private static Constructor logConstructor;
+
+  static {
+    tryImplementation(new Runnable() {
+      public void run() {
+        useCommonsLogging();
+      }
+    });
+    tryImplementation(new Runnable() {
+      public void run() {
+        useLog4JLogging();
+      }
+    });
+    tryImplementation(new Runnable() {
+      public void run() {
+        useJdkLogging();
+      }
+    });
+    tryImplementation(new Runnable() {
+      public void run() {
+        useNoLogging();
+      }
+    });
+  }
+
+  public static Log getLog(Class aClass) {
+    try {
+      return (Log) logConstructor.newInstance(new Object[]{aClass});
+    } catch (Throwable t) {
+      throw new RuntimeException("Error creating logger for class " + aClass + ".  Cause: " + t, t);
+    }
+  }
+
+  public static synchronized void useCommonsLogging() {
+    setImplementation("org.apache.commons.logging.LogFactory", "org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl");
+  }
+
+  public static synchronized void useLog4JLogging() {
+    setImplementation("org.apache.log4j.Logger", "org.apache.ibatis.logging.log4j.Log4jImpl");
+  }
+
+  public static synchronized void useJdkLogging() {
+    setImplementation("java.util.logging.Logger", "org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl");
+  }
+
+  public static synchronized void useStdOutLogging() {
+    setImplementation("org.apache.ibatis.logging.stdout.StdOutImpl");
+  }
+
+  public static synchronized void useNoLogging() {
+    setImplementation("org.apache.ibatis.logging.nologging.NoLoggingImpl");
+  }
+
+  private static void tryImplementation(Runnable runnable) {
+    if (logConstructor == null) {
+      try {
+        runnable.run();
+      } catch (Throwable t) {
+        //ignore
+      }
+    }
+  }
+
+  private static void setImplementation(String implClassName) {
+    setImplementation(implClassName, implClassName);
+  }
+
+  private static void setImplementation(String testClassName, String implClassName) {
+    try {
+      Class.forName(testClassName);
+      Class implClass = Class.forName(implClassName);
+      logConstructor = implClass.getConstructor(new Class[]{Class.class});
+    } catch (Throwable t) {
+      throw new RuntimeException("Error setting Log implementation.  Cause: " + t, t);
+    }
+  }
+
+}

Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/commons/JakartaCommonsLoggingImpl.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/commons/JakartaCommonsLoggingImpl.java?rev=683745&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/commons/JakartaCommonsLoggingImpl.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/commons/JakartaCommonsLoggingImpl.java Thu Aug  7 16:21:46 2008
@@ -0,0 +1,33 @@
+package org.apache.ibatis.logging.commons;
+
+import org.apache.commons.logging.*;
+
+public class JakartaCommonsLoggingImpl implements org.apache.ibatis.logging.Log {
+
+  private Log log;
+
+  public JakartaCommonsLoggingImpl(Class clazz) {
+    log = LogFactory.getLog(clazz);
+  }
+
+  public boolean isDebugEnabled() {
+    return log.isDebugEnabled();
+  }
+
+  public void error(String s, Throwable e) {
+    log.error(s, e);
+  }
+
+  public void error(String s) {
+    log.error(s);
+  }
+
+  public void debug(String s) {
+    log.debug(s);
+  }
+
+  public void warn(String s) {
+    log.warn(s);
+  }
+
+}

Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/jdbc/BaseJdbcLogger.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/jdbc/BaseJdbcLogger.java?rev=683745&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/jdbc/BaseJdbcLogger.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/jdbc/BaseJdbcLogger.java Thu Aug  7 16:21:46 2008
@@ -0,0 +1,116 @@
+package org.apache.ibatis.logging.jdbc;
+
+import java.util.*;
+
+/**
+ * Base class for proxies to do logging
+ */
+public class BaseJdbcLogger {
+
+  protected static final Set<String> SET_METHODS = new HashSet<String>();
+  protected static final Set<String> GET_METHODS = new HashSet<String>();
+  protected static final Set<String> EXECUTE_METHODS = new HashSet<String>();
+
+  private Map<Object, Object> columnMap = new HashMap<Object, Object>();
+
+  private List<Object> columnNames = new ArrayList<Object>();
+  private List<Object> columnValues = new ArrayList<Object>();
+
+  /**
+   * Default constructor
+   */
+  public BaseJdbcLogger() {
+  }
+
+  static {
+    SET_METHODS.add("setString");
+    SET_METHODS.add("setInt");
+    SET_METHODS.add("setByte");
+    SET_METHODS.add("setShort");
+    SET_METHODS.add("setLong");
+    SET_METHODS.add("setDouble");
+    SET_METHODS.add("setFloat");
+    SET_METHODS.add("setTimestamp");
+    SET_METHODS.add("setDate");
+    SET_METHODS.add("setTime");
+    SET_METHODS.add("setArray");
+    SET_METHODS.add("setBigDecimal");
+    SET_METHODS.add("setAsciiStream");
+    SET_METHODS.add("setBinaryStream");
+    SET_METHODS.add("setBlob");
+    SET_METHODS.add("setBoolean");
+    SET_METHODS.add("setBytes");
+    SET_METHODS.add("setCharacterStream");
+    SET_METHODS.add("setClob");
+    SET_METHODS.add("setObject");
+    SET_METHODS.add("setNull");
+
+    GET_METHODS.add("getString");
+    GET_METHODS.add("getInt");
+    GET_METHODS.add("getByte");
+    GET_METHODS.add("getShort");
+    GET_METHODS.add("getLong");
+    GET_METHODS.add("getDouble");
+    GET_METHODS.add("getFloat");
+    GET_METHODS.add("getTimestamp");
+    GET_METHODS.add("getDate");
+    GET_METHODS.add("getTime");
+    GET_METHODS.add("getArray");
+    GET_METHODS.add("getBigDecimal");
+    GET_METHODS.add("getAsciiStream");
+    GET_METHODS.add("getBinaryStream");
+    GET_METHODS.add("getBlob");
+    GET_METHODS.add("getBoolean");
+    GET_METHODS.add("getBytes");
+    GET_METHODS.add("getCharacterStream");
+    GET_METHODS.add("getClob");
+    GET_METHODS.add("getObject");
+    GET_METHODS.add("getNull");
+
+    EXECUTE_METHODS.add("execute");
+    EXECUTE_METHODS.add("executeUpdate");
+    EXECUTE_METHODS.add("executeQuery");
+
+  }
+
+  protected void setColumn(Object key, Object value) {
+    columnMap.put(key, value);
+    columnNames.add(key);
+    columnValues.add(value);
+  }
+
+  protected Object getColumn(Object key) {
+    return columnMap.get(key);
+  }
+
+  protected String getValueString() {
+    return columnValues.toString();
+  }
+
+  protected String getTypeString() {
+    List<Object> typeList = new ArrayList<Object>(columnValues.size());
+    for (Object value : columnValues) {
+      if (value == null) {
+        typeList.add("null");
+      } else {
+        typeList.add(value.getClass().getName());
+      }
+    }
+    return typeList.toString();
+  }
+
+  protected String getColumnString() {
+    return columnNames.toString();
+  }
+
+  protected void clearColumnInfo() {
+    columnMap.clear();
+    columnNames.clear();
+    columnValues.clear();
+  }
+
+  protected String removeBreakingWhitespace(String original) {
+    return original.replace('\n', ' ').replace('\r', ' ').replace('\t', ' ');
+  }
+
+}

Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/jdbc/ConnectionLogger.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/jdbc/ConnectionLogger.java?rev=683745&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/jdbc/ConnectionLogger.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/jdbc/ConnectionLogger.java Thu Aug  7 16:21:46 2008
@@ -0,0 +1,73 @@
+package org.apache.ibatis.logging.jdbc;
+
+import org.apache.ibatis.logging.*;
+import org.apache.ibatis.reflection.ExceptionUtil;
+
+import java.lang.reflect.*;
+import java.sql.*;
+
+/**
+ * Connection proxy to add logging
+ */
+public class ConnectionLogger extends BaseJdbcLogger implements InvocationHandler {
+
+  private static final Log log = LogFactory.getLog(Connection.class);
+
+  private Connection connection;
+
+  private ConnectionLogger(Connection conn) {
+    super();
+    this.connection = conn;
+    if (log.isDebugEnabled()) {
+      log.debug("Connection Opened");
+    }
+  }
+
+  public Object invoke(Object proxy, Method method, Object[] params)
+      throws Throwable {
+    try {
+      if ("prepareStatement".equals(method.getName())) {
+        PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
+        stmt = PreparedStatementLogger.newInstance(stmt, (String) params[0]);
+        return stmt;
+      } else if ("prepareCall".equals(method.getName())) {
+        PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
+        stmt = PreparedStatementLogger.newInstance(stmt, (String) params[0]);
+        return stmt;
+      } else if ("createStatement".equals(method.getName())) {
+        Statement stmt = (Statement) method.invoke(connection, params);
+        stmt = StatementLogger.newInstance(stmt);
+        return stmt;
+      } else {
+        return method.invoke(connection, params);
+      }
+    } catch (Throwable t) {
+      Throwable t1 = ExceptionUtil.unwrapThrowable(t);
+      log.error("Error calling Connection." + method.getName() + ':', t1);
+      throw t1;
+    }
+
+  }
+
+  /**
+   * Creates a logging version of a connection
+   *
+   * @param conn - the original connection
+   * @return - the connection with logging
+   */
+  public static Connection newInstance(Connection conn) {
+    InvocationHandler handler = new ConnectionLogger(conn);
+    ClassLoader cl = Connection.class.getClassLoader();
+    return (Connection) Proxy.newProxyInstance(cl, new Class[]{Connection.class}, handler);
+  }
+
+  /**
+   * return the wrapped connection
+   *
+   * @return the connection
+   */
+  public Connection getConnection() {
+    return connection;
+  }
+
+}

Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/jdbc/PreparedStatementLogger.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/jdbc/PreparedStatementLogger.java?rev=683745&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/jdbc/PreparedStatementLogger.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/jdbc/PreparedStatementLogger.java Thu Aug  7 16:21:46 2008
@@ -0,0 +1,92 @@
+package org.apache.ibatis.logging.jdbc;
+
+import org.apache.ibatis.logging.*;
+import org.apache.ibatis.reflection.ExceptionUtil;
+
+import java.lang.reflect.*;
+import java.sql.*;
+
+/**
+ * PreparedStatement proxy to add logging
+ */
+public class PreparedStatementLogger extends BaseJdbcLogger implements InvocationHandler {
+
+  private static final Log log = LogFactory.getLog(PreparedStatement.class);
+
+  private PreparedStatement statement;
+  private String sql;
+
+  private PreparedStatementLogger(PreparedStatement stmt, String sql) {
+    this.statement = stmt;
+    this.sql = sql;
+  }
+
+  public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
+    try {
+      if (EXECUTE_METHODS.contains(method.getName())) {
+        if (log.isDebugEnabled()) {
+          log.debug("Executing Statement: " + removeBreakingWhitespace(sql));
+          log.debug("Parameter Types: " + getTypeString());
+          log.debug("Parameters: " + getValueString());
+        }
+        clearColumnInfo();
+        if ("executeQuery".equals(method.getName())) {
+          ResultSet rs = (ResultSet) method.invoke(statement, params);
+          if (rs != null) {
+            return ResultSetLogger.newInstance(rs);
+          } else {
+            return null;
+          }
+        } else {
+          return method.invoke(statement, params);
+        }
+      } else if (SET_METHODS.contains(method.getName())) {
+        if ("setNull".equals(method.getName())) {
+          setColumn(params[0], null);
+        } else {
+          setColumn(params[0], params[1]);
+        }
+        return method.invoke(statement, params);
+      } else if ("getResultSet".equals(method.getName())) {
+        ResultSet rs = (ResultSet) method.invoke(statement, params);
+        if (rs != null) {
+          return ResultSetLogger.newInstance(rs);
+        } else {
+          return null;
+        }
+      } else if ("equals".equals(method.getName())) {
+        Object ps = params[0];
+        return ps instanceof Proxy && proxy == ps;
+      } else if ("hashCode".equals(method.getName())) {
+        return proxy.hashCode();
+      } else {
+        return method.invoke(statement, params);
+      }
+    } catch (Throwable t) {
+      throw ExceptionUtil.unwrapThrowable(t);
+    }
+  }
+
+  /**
+   * Creates a logging version of a PreparedStatement
+   *
+   * @param stmt - the statement
+   * @param sql  - the sql statement
+   * @return - the proxy
+   */
+  public static PreparedStatement newInstance(PreparedStatement stmt, String sql) {
+    InvocationHandler handler = new PreparedStatementLogger(stmt, sql);
+    ClassLoader cl = PreparedStatement.class.getClassLoader();
+    return (PreparedStatement) Proxy.newProxyInstance(cl, new Class[]{PreparedStatement.class, CallableStatement.class}, handler);
+  }
+
+  /**
+   * Return the wrapped prepared statement
+   *
+   * @return the PreparedStatement
+   */
+  public PreparedStatement getPreparedStatement() {
+    return statement;
+  }
+
+}

Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/jdbc/ResultSetLogger.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/jdbc/ResultSetLogger.java?rev=683745&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/jdbc/ResultSetLogger.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/jdbc/ResultSetLogger.java Thu Aug  7 16:21:46 2008
@@ -0,0 +1,79 @@
+package org.apache.ibatis.logging.jdbc;
+
+import org.apache.ibatis.logging.*;
+import org.apache.ibatis.reflection.ExceptionUtil;
+
+import java.lang.reflect.*;
+import java.sql.ResultSet;
+
+/**
+ * ResultSet proxy to add logging
+ */
+public class ResultSetLogger extends BaseJdbcLogger implements InvocationHandler {
+
+  private static final Log log = LogFactory.getLog(ResultSet.class);
+
+  boolean first = true;
+  private ResultSet rs;
+
+  private ResultSetLogger(ResultSet rs) {
+    super();
+    this.rs = rs;
+    if (log.isDebugEnabled()) {
+      log.debug("ResultSet Returned");
+    }
+  }
+
+  public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
+    try {
+      Object o = method.invoke(rs, params);
+      if (GET_METHODS.contains(method.getName())) {
+        if (params[0] instanceof String) {
+          setColumn(params[0], o);
+          //        setColumn(params[0], rs.getObject((String) params[0]));
+          //      } else {
+          //        setColumn(params[0], rs.getObject(((Integer) params[0]).intValue()));
+        }
+      } else if ("next".equals(method.getName()) || "close".equals(method.getName())) {
+        String s = getValueString();
+        if (!"[]".equals(s)) {
+          if (first) {
+            first = false;
+            if (log.isDebugEnabled()) {
+              log.debug("Columns: " + getColumnString());
+            }
+          }
+          if (log.isDebugEnabled()) {
+            log.debug("Row: " + s);
+          }
+        }
+        clearColumnInfo();
+      }
+      return o;
+    } catch (Throwable t) {
+      throw ExceptionUtil.unwrapThrowable(t);
+    }
+  }
+
+  /**
+   * Creates a logging version of a ResultSet
+   *
+   * @param rs - the ResultSet to proxy
+   * @return - the ResultSet with logging
+   */
+  public static ResultSet newInstance(ResultSet rs) {
+    InvocationHandler handler = new ResultSetLogger(rs);
+    ClassLoader cl = ResultSet.class.getClassLoader();
+    return (ResultSet) Proxy.newProxyInstance(cl, new Class[]{ResultSet.class}, handler);
+  }
+
+  /**
+   * Get the wrapped result set
+   *
+   * @return the resultSet
+   */
+  public ResultSet getRs() {
+    return rs;
+  }
+
+}

Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/jdbc/StatementLogger.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/jdbc/StatementLogger.java?rev=683745&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/jdbc/StatementLogger.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/jdbc/StatementLogger.java Thu Aug  7 16:21:46 2008
@@ -0,0 +1,80 @@
+package org.apache.ibatis.logging.jdbc;
+
+import org.apache.ibatis.logging.*;
+import org.apache.ibatis.reflection.ExceptionUtil;
+
+import java.lang.reflect.*;
+import java.sql.*;
+
+/**
+ * Statement proxy to add logging
+ */
+public class StatementLogger extends BaseJdbcLogger implements InvocationHandler {
+
+  private static final Log log = LogFactory.getLog(Statement.class);
+
+  private Statement statement;
+
+  private StatementLogger(Statement stmt) {
+    super();
+    this.statement = stmt;
+  }
+
+  public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
+    try {
+      if (EXECUTE_METHODS.contains(method.getName())) {
+        if (log.isDebugEnabled()) {
+          log.debug("Statement: " + removeBreakingWhitespace((String) params[0]));
+        }
+        if ("executeQuery".equals(method.getName())) {
+          ResultSet rs = (ResultSet) method.invoke(statement, params);
+          if (rs != null) {
+            return ResultSetLogger.newInstance(rs);
+          } else {
+            return null;
+          }
+        } else {
+          return method.invoke(statement, params);
+        }
+      } else if ("getResultSet".equals(method.getName())) {
+        ResultSet rs = (ResultSet) method.invoke(statement, params);
+        if (rs != null) {
+          return ResultSetLogger.newInstance(rs);
+        } else {
+          return null;
+        }
+      } else if ("equals".equals(method.getName())) {
+        Object ps = params[0];
+        return ps instanceof Proxy && proxy == ps;
+      } else if ("hashCode".equals(method.getName())) {
+        return proxy.hashCode();
+      } else {
+        return method.invoke(statement, params);
+      }
+    } catch (Throwable t) {
+      throw ExceptionUtil.unwrapThrowable(t);
+    }
+  }
+
+  /**
+   * Creates a logging version of a Statement
+   *
+   * @param stmt - the statement
+   * @return - the proxy
+   */
+  public static Statement newInstance(Statement stmt) {
+    InvocationHandler handler = new StatementLogger(stmt);
+    ClassLoader cl = Statement.class.getClassLoader();
+    return (Statement) Proxy.newProxyInstance(cl, new Class[]{Statement.class}, handler);
+  }
+
+  /**
+   * return the wrapped statement
+   *
+   * @return the statement
+   */
+  public Statement getStatement() {
+    return statement;
+  }
+
+}

Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/jdk14/Jdk14LoggingImpl.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/jdk14/Jdk14LoggingImpl.java?rev=683745&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/jdk14/Jdk14LoggingImpl.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/jdk14/Jdk14LoggingImpl.java Thu Aug  7 16:21:46 2008
@@ -0,0 +1,36 @@
+package org.apache.ibatis.logging.jdk14;
+
+import org.apache.ibatis.logging.Log;
+
+import java.util.logging.*;
+
+public class Jdk14LoggingImpl implements Log {
+
+  private Logger log;
+
+  public Jdk14LoggingImpl(Class clazz) {
+    log = Logger.getLogger(clazz.getName());
+  }
+
+  public boolean isDebugEnabled() {
+    return log.isLoggable(Level.FINE);
+  }
+
+  public void error(String s, Throwable e) {
+    log.log(Level.SEVERE, s, e);
+  }
+
+  public void error(String s) {
+    log.log(Level.SEVERE, s);
+  }
+
+  public void debug(String s) {
+    log.log(Level.FINE, s);
+  }
+
+  public void warn(String s) {
+    log.log(Level.WARNING, s);
+  }
+
+
+}

Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/log4j/Log4jImpl.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/log4j/Log4jImpl.java?rev=683745&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/log4j/Log4jImpl.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/log4j/Log4jImpl.java Thu Aug  7 16:21:46 2008
@@ -0,0 +1,35 @@
+package org.apache.ibatis.logging.log4j;
+
+import org.apache.ibatis.logging.Log;
+import org.apache.log4j.Logger;
+
+public class Log4jImpl implements Log {
+
+  private Logger log;
+
+  public Log4jImpl(Class clazz) {
+    log = Logger.getLogger(clazz);
+  }
+
+  public boolean isDebugEnabled() {
+    return log.isDebugEnabled();
+  }
+
+  public void error(String s, Throwable e) {
+    log.error(s, e);
+  }
+
+  public void error(String s) {
+    log.error(s);
+  }
+
+  public void debug(String s) {
+    log.debug(s);
+  }
+
+  public void warn(String s) {
+    log.warn(s);
+  }
+
+
+}

Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/nologging/NoLoggingImpl.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/nologging/NoLoggingImpl.java?rev=683745&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/nologging/NoLoggingImpl.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/nologging/NoLoggingImpl.java Thu Aug  7 16:21:46 2008
@@ -0,0 +1,26 @@
+package org.apache.ibatis.logging.nologging;
+
+import org.apache.ibatis.logging.Log;
+
+public class NoLoggingImpl implements Log {
+
+  public NoLoggingImpl(Class clazz) {
+  }
+
+  public boolean isDebugEnabled() {
+    return false;
+  }
+
+  public void error(String s, Throwable e) {
+  }
+
+  public void error(String s) {
+  }
+
+  public void debug(String s) {
+  }
+
+  public void warn(String s) {
+  }
+
+}

Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/stdout/StdOutImpl.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/stdout/StdOutImpl.java?rev=683745&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/stdout/StdOutImpl.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/logging/stdout/StdOutImpl.java Thu Aug  7 16:21:46 2008
@@ -0,0 +1,30 @@
+package org.apache.ibatis.logging.stdout;
+
+import org.apache.ibatis.logging.Log;
+
+public class StdOutImpl implements Log {
+
+  public StdOutImpl(Class clazz) {
+  }
+
+  public boolean isDebugEnabled() {
+    return true;
+  }
+
+  public void error(String s, Throwable e) {
+    System.err.println(s);
+    e.printStackTrace(System.err);
+  }
+
+  public void error(String s) {
+    System.err.println(s);
+  }
+
+  public void debug(String s) {
+    System.out.println(s);
+  }
+
+  public void warn(String s) {
+    System.out.println(s);
+  }
+}

Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/BasicSqlSource.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/BasicSqlSource.java?rev=683745&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/BasicSqlSource.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/BasicSqlSource.java Thu Aug  7 16:21:46 2008
@@ -0,0 +1,20 @@
+package org.apache.ibatis.mapping;
+
+import java.util.List;
+
+public class BasicSqlSource implements SqlSource {
+
+  private String sql;
+
+  public BasicSqlSource(String sql) {
+    this.sql = sql;
+  }
+
+  public String getSql(Object parameterObject) {
+    return sql;
+  }
+
+  public List<ParameterMapping> getParameterMappings(Object parameterObject) {
+    return null;
+  }
+}

Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/Configuration.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/Configuration.java?rev=683745&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/Configuration.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/Configuration.java Thu Aug  7 16:21:46 2008
@@ -0,0 +1,224 @@
+package org.apache.ibatis.mapping;
+
+import org.apache.ibatis.cache.Cache;
+import org.apache.ibatis.executor.*;
+import org.apache.ibatis.executor.parameter.*;
+import org.apache.ibatis.executor.result.ResultHandler;
+import org.apache.ibatis.executor.resultset.*;
+import org.apache.ibatis.executor.statement.*;
+import org.apache.ibatis.mapping.*;
+import org.apache.ibatis.plugin.*;
+import org.apache.ibatis.reflection.*;
+import org.apache.ibatis.type.*;
+
+import java.sql.Connection;
+import java.util.*;
+
+public class Configuration {
+
+  private boolean lazyLoadingEnabled = true;
+  private boolean enhancementEnabled = false;
+  private boolean multipleResultSetsEnabled = true;
+  private boolean generatedKeysEnabled = true;
+  private boolean useColumnLabel = true;
+  private boolean cacheEnabled;
+
+  //TODO:  Make enum for reuse/batch/simple executors
+  private boolean statementCachingEnabled;
+  private boolean batchUpdatesEnabled;
+
+  private Properties variables = new Properties();
+  private ObjectFactory objectFactory = new DefaultObjectFactory();
+
+  private final InterceptorChain interceptorChain = new InterceptorChain();
+  private final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
+  private final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
+  private final Map<String, MappedStatement> mappedStatements = new HashMap<String, MappedStatement>();
+  private final Map<String, Cache> caches = new HashMap<String, Cache>();
+  private final Map<String, ResultMap> resultMaps = new HashMap<String, ResultMap>();
+  private final Map<String, ParameterMap> parameterMaps = new HashMap<String, ParameterMap>();
+  private Integer defaultStatementTimeout;
+
+  public boolean isLazyLoadingEnabled() {
+    return lazyLoadingEnabled;
+  }
+
+  public void setLazyLoadingEnabled(boolean lazyLoadingEnabled) {
+    this.lazyLoadingEnabled = lazyLoadingEnabled;
+  }
+
+  public boolean isEnhancementEnabled() {
+    return enhancementEnabled;
+  }
+
+  public void setEnhancementEnabled(boolean enhancementEnabled) {
+    this.enhancementEnabled = enhancementEnabled;
+  }
+
+  public boolean isMultipleResultSetsEnabled() {
+    return multipleResultSetsEnabled;
+  }
+
+  public void setMultipleResultSetsEnabled(boolean multipleResultSetsEnabled) {
+    this.multipleResultSetsEnabled = multipleResultSetsEnabled;
+  }
+
+  public boolean isGeneratedKeysEnabled() {
+    return generatedKeysEnabled;
+  }
+
+  public void setGeneratedKeysEnabled(boolean generatedKeysEnabled) {
+    this.generatedKeysEnabled = generatedKeysEnabled;
+  }
+
+  public boolean isStatementCachingEnabled() {
+    return statementCachingEnabled;
+  }
+
+  public void setStatementCachingEnabled(boolean statementCachingEnabled) {
+    this.statementCachingEnabled = statementCachingEnabled;
+  }
+
+  public boolean isBatchUpdatesEnabled() {
+    return batchUpdatesEnabled;
+  }
+
+  public void setBatchUpdatesEnabled(boolean batchUpdatesEnabled) {
+    this.batchUpdatesEnabled = batchUpdatesEnabled;
+  }
+
+  public boolean isCacheEnabled() {
+    return cacheEnabled;
+  }
+
+  public void setCacheEnabled(boolean cacheEnabled) {
+    this.cacheEnabled = cacheEnabled;
+  }
+
+  public Integer getDefaultStatementTimeout() {
+    return defaultStatementTimeout;
+  }
+
+  public void setDefaultStatementTimeout(Integer defaultStatementTimeout) {
+    this.defaultStatementTimeout = defaultStatementTimeout;
+  }
+
+  public boolean isUseColumnLabel() {
+    return useColumnLabel;
+  }
+
+  public void setUseColumnLabel(boolean useColumnLabel) {
+    this.useColumnLabel = useColumnLabel;
+  }
+
+  public Properties getVariables() {
+    return variables;
+  }
+
+  public void setVariables(Properties variables) {
+    this.variables = variables;
+  }
+
+  public TypeHandlerRegistry getTypeHandlerRegistry() {
+    return typeHandlerRegistry;
+  }
+
+  public TypeAliasRegistry getTypeAliasRegistry() {
+    return typeAliasRegistry;
+  }
+
+  public ObjectFactory getObjectFactory() {
+    return objectFactory;
+  }
+
+  public void setObjectFactory(ObjectFactory objectFactory) {
+    this.objectFactory = objectFactory;
+  }
+
+  public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject) {
+    ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject);
+    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
+    return parameterHandler;
+  }
+
+  public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, int rowOffset, int rowLimit, ParameterHandler parameterHandler, ResultHandler resultHandler) {
+    ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, rowOffset, rowLimit, resultHandler);
+    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
+    return resultSetHandler;
+  }
+
+  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, int rowOffset, int rowLimit, ResultHandler resultHandler) {
+    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowOffset, rowLimit, resultHandler);
+    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
+    return statementHandler;
+  }
+
+  public Executor newExecutor(Connection conn) {
+    Executor executor;
+    if (batchUpdatesEnabled) {
+      executor = new BatchExecutor(conn);
+    } else if (statementCachingEnabled) {
+      executor = new ReuseExecutor(conn);
+    } else {
+      executor = new SimpleExecutor(conn);
+    }
+    if (cacheEnabled) {
+      executor = new CachingExecutor(executor);
+    }
+    executor = (Executor) interceptorChain.pluginAll(executor);
+    return executor;
+  }
+
+  public void addCache(Cache cache) {
+    caches.put(cache.getId(), cache);
+  }
+
+  public Collection<Cache> getCaches() {
+    return caches.values();
+  }
+
+  public Cache getCache(String id) {
+    return caches.get(id);
+  }
+
+  public void addResultMap(ResultMap rm) {
+    resultMaps.put(rm.getId(), rm);
+  }
+
+  public Collection<ResultMap> getResultMaps() {
+    return resultMaps.values();
+  }
+
+  public ResultMap getResultMap(String id) {
+    return resultMaps.get(id);
+  }
+
+  public void addParameterMap(ParameterMap pm) {
+    parameterMaps.put(pm.getId(), pm);
+  }
+
+  public Collection<ParameterMap> getParameterMaps() {
+    return parameterMaps.values();
+  }
+
+  public ParameterMap getParameterMap(String id) {
+    return parameterMaps.get(id);
+  }
+
+  public void addMappedStatement(MappedStatement ms) {
+    mappedStatements.put(ms.getId(), ms);
+  }
+
+  public Collection<MappedStatement> getMappedStatements() {
+    return mappedStatements.values();
+  }
+
+  public MappedStatement getMappedStatement(String id) {
+    return mappedStatements.get(id);
+  }
+
+  public void addInterceptor(Interceptor interceptor) {
+    interceptorChain.addInterceptor(interceptor);
+  }
+
+}

Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/Discriminator.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/Discriminator.java?rev=683745&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/Discriminator.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/Discriminator.java Thu Aug  7 16:21:46 2008
@@ -0,0 +1,43 @@
+package org.apache.ibatis.mapping;
+
+import java.util.*;
+
+public class Discriminator {
+
+  private ResultMapping resultMapping;
+  private Map<String, String> discriminatorMap;
+
+  private Discriminator() {
+  }
+
+  public static class Builder {
+    private Discriminator discriminator = new Discriminator();
+
+    public Builder(ResultMapping resultMapping, Map<String, String> discriminatorMap) {
+      discriminator.resultMapping = resultMapping;
+      discriminator.discriminatorMap = discriminatorMap;
+    }
+
+    public Discriminator build() {
+      assert discriminator.resultMapping != null;
+      assert discriminator.discriminatorMap != null;
+      assert discriminator.discriminatorMap.size() > 0;
+      //lock down map
+      discriminator.discriminatorMap = Collections.unmodifiableMap(discriminator.discriminatorMap);
+      return discriminator;
+    }
+  }
+
+  public ResultMapping getResultMapping() {
+    return resultMapping;
+  }
+
+  public Map<String, String> getDiscriminatorMap() {
+    return discriminatorMap;
+  }
+
+  public String getMapIdFor(String s) {
+    return discriminatorMap.get(s);
+  }
+
+}

Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/MappedStatement.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/MappedStatement.java?rev=683745&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/MappedStatement.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/MappedStatement.java Thu Aug  7 16:21:46 2008
@@ -0,0 +1,133 @@
+package org.apache.ibatis.mapping;
+
+import org.apache.ibatis.cache.Cache;
+import org.apache.ibatis.mapping.Configuration;
+
+import java.util.*;
+
+public class MappedStatement {
+
+  private Configuration configuration;
+  private String id;
+  private Integer fetchSize;
+  private Integer timeout;
+  private StatementType statementType;
+  private ResultSetType resultSetType;
+  private SqlSource sqlSource;
+  private Cache cache;
+  private ParameterMap parameterMap;
+  private List<ResultMap> resultMaps;
+
+  private MappedStatement() {
+  }
+
+  public static class Builder {
+    private MappedStatement mappedStatement = new MappedStatement();
+
+    public Builder(Configuration configuration, String id, SqlSource sqlSource) {
+      mappedStatement.configuration = configuration;
+      mappedStatement.id = id;
+      mappedStatement.sqlSource = sqlSource;
+      mappedStatement.statementType = StatementType.PREPARED;
+      mappedStatement.parameterMap = ParameterMap.EMPTY;
+      mappedStatement.resultMaps = new ArrayList<ResultMap>();
+      mappedStatement.timeout = configuration.getDefaultStatementTimeout();
+    }
+
+    public Builder parameterMap(ParameterMap parameterMap) {
+      mappedStatement.parameterMap = parameterMap;
+      return this;
+    }
+
+    public Builder resultMaps(List<ResultMap> resultMaps) {
+      mappedStatement.resultMaps = resultMaps;
+      return this;
+    }
+
+    public Builder fetchSize(Integer fetchSize) {
+      mappedStatement.fetchSize = fetchSize;
+      return this;
+    }
+
+    public Builder timeout(Integer timeout) {
+      mappedStatement.timeout = timeout;
+      return this;
+    }
+
+    public Builder statementType(StatementType statementType) {
+      mappedStatement.statementType = statementType;
+      return this;
+    }
+
+    public Builder resultSetType(ResultSetType resultSetType) {
+      mappedStatement.resultSetType = resultSetType;
+      return this;
+    }
+
+    public Builder cache(Cache cache) {
+      mappedStatement.cache = cache;
+      return this;
+    }
+
+    public MappedStatement build() {
+      assert mappedStatement.configuration != null;
+      assert mappedStatement.id != null;
+      assert mappedStatement.sqlSource != null;
+      mappedStatement.resultMaps = Collections.unmodifiableList(mappedStatement.resultMaps);
+      return mappedStatement;
+    }
+  }
+
+  public Configuration getConfiguration() {
+    return configuration;
+  }
+
+  public String getId() {
+    return id;
+  }
+
+  public Integer getFetchSize() {
+    return fetchSize;
+  }
+
+  public Integer getTimeout() {
+    return timeout;
+  }
+
+  public StatementType getStatementType() {
+    return statementType;
+  }
+
+  public ResultSetType getResultSetType() {
+    return resultSetType;
+  }
+
+  public SqlSource getSqlSource() {
+    return sqlSource;
+  }
+
+  public ParameterMap getParameterMap() {
+    return parameterMap;
+  }
+
+  public List<ResultMap> getResultMaps() {
+    return resultMaps;
+  }
+
+  public Cache getCache() {
+    return cache;
+  }
+
+  public String getSql(Object parameterObject) {
+    return sqlSource.getSql(parameterObject);
+  }
+
+  public List<ParameterMapping> getDynamicParameterMappings(Object parameterObject) {
+    List<ParameterMapping> parameterMappings = sqlSource.getParameterMappings(parameterObject);
+    if (parameterMappings == null || parameterMappings.size() <= 0) {
+      parameterMappings = parameterMap.getParameterMappings();
+    }
+    return parameterMappings;
+  }
+
+}

Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/ParameterMap.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/ParameterMap.java?rev=683745&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/ParameterMap.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/ParameterMap.java Thu Aug  7 16:21:46 2008
@@ -0,0 +1,48 @@
+package org.apache.ibatis.mapping;
+
+import java.util.*;
+
+public class ParameterMap {
+
+  public static final ParameterMap EMPTY = new Builder("defaultParameterMap", Object.class, new ArrayList<ParameterMapping>()).build();
+
+  private String id;
+  private Class type;
+  private List<ParameterMapping> parameterMappings;
+
+  private ParameterMap() {
+  }
+
+  public static class Builder {
+    private ParameterMap parameterMap = new ParameterMap();
+
+    public Builder(String id, Class type, List<ParameterMapping> parameterMappings) {
+      parameterMap.id = id;
+      parameterMap.type = type;
+      parameterMap.parameterMappings = parameterMappings;
+    }
+
+    public Class type() {
+      return parameterMap.type;
+    }
+
+    public ParameterMap build() {
+      //lock down collections
+      parameterMap.parameterMappings = Collections.unmodifiableList(parameterMap.parameterMappings);
+      return parameterMap;
+    }
+  }
+
+  public String getId() {
+    return id;
+  }
+
+  public Class getType() {
+    return type;
+  }
+
+  public List<ParameterMapping> getParameterMappings() {
+    return parameterMappings;
+  }
+
+}

Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java?rev=683745&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java Thu Aug  7 16:21:46 2008
@@ -0,0 +1,91 @@
+package org.apache.ibatis.mapping;
+
+import org.apache.ibatis.type.*;
+
+public class ParameterMapping {
+
+  private String property;
+  private ParameterMode mode;
+  private Class javaType = Object.class;
+  private JdbcType jdbcType;
+  private Integer numericScale;
+  private TypeHandler typeHandler;
+  private String resultMapId;
+
+  private ParameterMapping() {
+  }
+
+  public static class Builder {
+    private ParameterMapping parameterMapping = new ParameterMapping();
+
+    public Builder(String property, TypeHandler typeHandler) {
+      parameterMapping.property = property;
+      parameterMapping.typeHandler = typeHandler;
+      parameterMapping.mode = ParameterMode.IN;
+    }
+
+    public Builder mode(ParameterMode mode) {
+      parameterMapping.mode = mode;
+      return this;
+    }
+
+    public Builder javaType(Class javaType) {
+      parameterMapping.javaType = javaType;
+      return this;
+    }
+
+    public Builder jdbcType(JdbcType jdbcType) {
+      parameterMapping.jdbcType = jdbcType;
+      return this;
+    }
+
+    public Builder numericScale(Integer numericScale) {
+      parameterMapping.numericScale = numericScale;
+      return this;
+    }
+
+    public Builder resultMapId(String resultMapId) {
+      parameterMapping.resultMapId = resultMapId;
+      return this;
+    }
+
+    public Builder typeHandler(TypeHandler typeHandler) {
+      parameterMapping.typeHandler = typeHandler;
+      return this;
+    }
+
+    public ParameterMapping build() {
+      return parameterMapping;
+    }
+
+  }
+
+  public String getProperty() {
+    return property;
+  }
+
+  public ParameterMode getMode() {
+    return mode;
+  }
+
+  public Class getJavaType() {
+    return javaType;
+  }
+
+  public JdbcType getJdbcType() {
+    return jdbcType;
+  }
+
+  public Integer getNumericScale() {
+    return numericScale;
+  }
+
+  public TypeHandler getTypeHandler() {
+    return typeHandler;
+  }
+
+  public String getResultMapId() {
+    return resultMapId;
+  }
+
+}

Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/ParameterMode.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/ParameterMode.java?rev=683745&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/ParameterMode.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/ParameterMode.java Thu Aug  7 16:21:46 2008
@@ -0,0 +1,5 @@
+package org.apache.ibatis.mapping;
+
+public enum ParameterMode {
+  IN, OUT, INOUT
+}

Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/ResultFlag.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/ResultFlag.java?rev=683745&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/ResultFlag.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/ResultFlag.java Thu Aug  7 16:21:46 2008
@@ -0,0 +1,5 @@
+package org.apache.ibatis.mapping;
+
+public enum ResultFlag {
+  ID, CONSTRUCTOR
+}

Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/ResultMap.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/ResultMap.java?rev=683745&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/ResultMap.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/mapping/ResultMap.java Thu Aug  7 16:21:46 2008
@@ -0,0 +1,91 @@
+package org.apache.ibatis.mapping;
+
+import java.util.*;
+
+public class ResultMap {
+
+  private String id;
+  private Class type;
+  private List<ResultMapping> resultMappings;
+  private List<ResultMapping> idResultMappings;
+  private List<ResultMapping> constructorResultMappings;
+  private List<ResultMapping> propertyResultMappings;
+  private Discriminator discriminator;
+
+  private ResultMap() {
+  }
+
+  public static class Builder {
+    private ResultMap resultMap = new ResultMap();
+
+    public Builder(String id, Class type, List<ResultMapping> resultMappings) {
+      resultMap.id = id;
+      resultMap.type = type;
+      resultMap.resultMappings = resultMappings;
+    }
+
+    public Builder discriminator(Discriminator discriminator) {
+      resultMap.discriminator = discriminator;
+      return this;
+    }
+
+    public Class type() {
+      return resultMap.type;
+    }
+
+    public ResultMap build() {
+      resultMap.idResultMappings = new ArrayList<ResultMapping>();
+      resultMap.constructorResultMappings = new ArrayList<ResultMapping>();
+      resultMap.propertyResultMappings = new ArrayList<ResultMapping>();
+      for (ResultMapping resultMapping : resultMap.resultMappings) {
+        if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
+          resultMap.constructorResultMappings.add(resultMapping);
+        } else {
+          resultMap.propertyResultMappings.add(resultMapping);
+        }
+        if (resultMapping.getFlags().contains(ResultFlag.ID)) {
+          resultMap.idResultMappings.add(resultMapping);
+        }
+      }
+      if (resultMap.idResultMappings.isEmpty()) {
+        resultMap.idResultMappings.addAll(resultMap.resultMappings);
+      }
+      // lock down collections
+      resultMap.resultMappings = Collections.unmodifiableList(resultMap.resultMappings);
+      resultMap.idResultMappings = Collections.unmodifiableList(resultMap.idResultMappings);
+      resultMap.constructorResultMappings = Collections.unmodifiableList(resultMap.constructorResultMappings);
+      resultMap.propertyResultMappings = Collections.unmodifiableList(resultMap.propertyResultMappings);
+      return resultMap;
+    }
+  }
+
+  public String getId() {
+    return id;
+  }
+
+  public Class getType() {
+    return type;
+  }
+
+
+  public List<ResultMapping> getResultMappings() {
+    return resultMappings;
+  }
+
+  public List<ResultMapping> getConstructorResultMappings() {
+    return constructorResultMappings;
+  }
+
+  public List<ResultMapping> getPropertyResultMappings() {
+    return propertyResultMappings;
+  }
+
+  public List<ResultMapping> getIdResultMappings() {
+    return idResultMappings;
+  }
+
+  public Discriminator getDiscriminator() {
+    return discriminator;
+  }
+
+}