StandardXADataSource.java :  » Database-JDBC-Connection-Pool » xapool » org » enhydra » jdbc » standard » Java Open Source

Java Open Source » Database JDBC Connection Pool » xapool 
xapool » org » enhydra » jdbc » standard » StandardXADataSource.java
/*
 * XAPool: Open Source XA JDBC Pool
 * Copyright (C) 2003 Objectweb.org
 * Initial Developer: Lutris Technologies Inc.
 * Contact: xapool-public@lists.debian-sf.objectweb.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 */
package org.enhydra.jdbc.standard;

import org.enhydra.jdbc.util.Logger;
import org.apache.commons.logging.LogFactory;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Iterator;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.StringRefAddr;

import javax.sql.XAConnection;
import javax.sql.XADataSource;
import javax.transaction.Status;
import javax.transaction.xa.XAException;
import javax.transaction.xa.Xid;
import javax.transaction.TransactionManager;

/**
 * Data source for creating StandardXAConnections.
 */
public class StandardXADataSource
  extends StandardConnectionPoolDataSource
  implements XADataSource {

  public int minCon; // minimum number of connections
  public int maxCon; // maximum number of connections
  public long deadLockMaxWait;
  // time (in ms) to wait before return an exception
  Vector freeConnections; // connections not currently associated with an XID
  Hashtable xidConnections; // connections currently associated with an XID
  Hashtable deadConnections;
  // connections which should be discarded when the transaction finishes
  public int connectionCount = 0; // total number of connections created
  public long deadLockRetryWait; // time to wait before 2 try of loop
  transient public TransactionManager transactionManager;
    private String transactionManagerName;

  public static final int DEFAULT_MIN_CON = 50;
  // minimum number of connections
  public static final int DEFAULT_MAX_CON = 0;
  // maximum number of connections
  public static final long DEFAULT_DEADLOCKMAXWAIT = 300000; // 5 minutes
  public static final int DEFAULT_DEADLOCKRETRYWAIT = 10000; // 10 seconds

  /**
   * Constructor
   */
  public StandardXADataSource() {
    super();
    minCon = DEFAULT_MIN_CON;
    maxCon = DEFAULT_MAX_CON;
    deadLockMaxWait = DEFAULT_DEADLOCKMAXWAIT;
    deadLockRetryWait = DEFAULT_DEADLOCKRETRYWAIT;
    freeConnections = new Vector(minCon, 1);
    // allow a reasonable size for free connections
    xidConnections = new Hashtable(minCon * 2, 0.5f);
    // ...and same for used connections

    log = new Logger(LogFactory.getLog("org.enhydra.jdbc.xapool"));
    log.debug("StandardXADataSource is created");
  }

  public int getConnectionCount() {
    return connectionCount;
  }

  public Hashtable getXidConnections() {
    return xidConnections;
  }

  /**
   * Creates an XA connection using the default username and password.
   */
  public XAConnection getXAConnection() throws SQLException {
    log.debug(
      "StandardXADataSource:getXAConnection(0) XA connection returned");
    return getXAConnection(user, password);
  }

  /**
   * Creates an XA connection using the supplied username and password.
   */
  public synchronized XAConnection getXAConnection(
    String user,
    String password)
    throws SQLException {
    log.debug("StandardXADataSource:getXAConnection(user, password)");
    StandardXAConnection xac =
      new StandardXAConnection(this, user, password);
    xac.setTransactionManager(transactionManager);
    xac.setLogger(log);
    connectionCount++;
    return xac;
  }

  public void setTransactionManager(TransactionManager tm) {
    log.debug("StandardXADataSource:setTransactionManager");
    this.transactionManager = tm;
  }

  public TransactionManager getTransactionManager() {
    return transactionManager;
  }

    public void setTransactionManagerName(String tmName) {
  log.debug("StandardXADataSource:setTransactionManagerName");
        transactionManagerName = tmName;
    }

  public void setUser(String user) {
    log.debug("StandardXADataSource:setUser");
    if (((user == null) || (getUser() == null))
      ? user != getUser()
      : !user.equals(getUser())) {
      super.setUser(user);
      resetCache();
    }
  }

  public void setPassword(String password) {
    log.debug("StandardXADataSource:setPassword");
    if (((password == null) || (getPassword() == null))
      ? password != getPassword()
      : !password.equals(getPassword())) {
      super.setPassword(password);
      resetCache();
    }
  }

  public void setUrl(String url) {
    if (((url == null) || (getUrl() == null))
      ? url != getUrl()
      : !url.equals(getUrl())) {
      super.setUrl(url);
      resetCache();
    }
  }

  public void setDriverName(String driverName) throws SQLException {
    if ((driverName == null && getDriverName() != null)
      || (!driverName.equals(getDriverName()))) {
      super.setDriverName(driverName);
      resetCache();
    }
  }

  private synchronized void resetCache() {
    log.debug("StandardXADataSource:resetCache");
    // deadConnections will temporarily hold pointers to the
    // current ongoing transactions.  These will be discarded when
    // freed
    deadConnections = (Hashtable) xidConnections.clone();
    deadConnections.putAll(xidConnections);

    // now we'll just clear out the freeConnections
    Enumeration enumeration = freeConnections.elements();
    while (enumeration.hasMoreElements()) {
      StandardXAStatefulConnection xasc =
        (StandardXAStatefulConnection) enumeration.nextElement();
      try {
        log.debug(
          "StandardXADataSource:resetCache closing Connection:"
            + xasc.con);
        xasc.con.close();
      } catch (SQLException e) {
        log.error(
          "StandardXADataSource:resetCache Error closing connection:"
            + xasc.con);
      }
      freeConnections.removeElement(xasc);
    }
  }



  /**
   * Called when an XA connection gets closed. When they have all
   * been closed then any remaining physical connections are also
   * closed.
   */
  synchronized void connectionClosed() throws SQLException {
    log.debug("StandardXADataSource:connectionClosed");
    connectionCount--; // one more connection closed
    if (connectionCount == 0) { // if no connections left

      // Close any connections still associated with XIDs.
      Enumeration cons = xidConnections.keys();
      // used to iterate through the used connections
      while (cons.hasMoreElements()) {
        // while there are more connections
        Object key = cons.nextElement(); // get the next connection
                                StandardXAStatefulConnection cur =
          (StandardXAStatefulConnection)xidConnections.remove(key);
                                if(cur != null)
                                {

                                    cur.con.close(); // close the physical connection
                                }
        // cast to something more convenient

        log.debug(
          "StandardXADataSource:connectionClosed close physical connection");
      }
                 
                        
                        Iterator connIterator = freeConnections.iterator();
                        while (connIterator.hasNext())
                        {
                            StandardXAStatefulConnection cur =(StandardXAStatefulConnection)connIterator.next();
                            cur.con.close();
                            connIterator.remove();
                            log.debug(
                                      "StandardXADataSource:connectionClosed close any free connections");
                        }
    }
  }



  /**
   * Returns the number of connections that are either
   * prepared or heuristically completed.
   */
  public int getXidCount() {
    int count = 0; // the return value
    Enumeration cons = xidConnections.elements();
    // used to iterate through the used connections
    while (cons.hasMoreElements()) { // while there are more connections
      Object o = cons.nextElement(); // get the next connection
      StandardXAStatefulConnection cur = (StandardXAStatefulConnection) o;
      // cast to something more convenient
      if ((cur.getState() == Status.STATUS_PREPARED)
        || // if prepared
       (
          cur.getState() == Status.STATUS_PREPARING)) {
        // ...or heuristically committed
        count++; // one more connection with a valid xid
      }
    }
    log.debug(
      "StandardXADataSource:getXidCount return XidCount=<" + count + ">");
    return count;
  }

  /**
   * Constructs a list of all prepared connections' xids.
   */
  Xid[] recover() {
    int nodeCount = getXidCount();
    // get number of connections in transactions
    Xid[] xids = new Xid[nodeCount]; // create the return array
    int i = 0; // used as xids index
    Enumeration cons = xidConnections.elements();
    // used to iterate through the used connections
    while (cons.hasMoreElements()) { // while there are more connections
      Object o = cons.nextElement(); // get the next connection
      StandardXAStatefulConnection cur = (StandardXAStatefulConnection) o;
      // cast to something more convenient
      if ((cur.getState() == Status.STATUS_PREPARED)
        || // if prepared
       (
          cur.getState() == Status.STATUS_PREPARING)) {
        // ...or heuristically committed
        xids[i++] = cur.xid; // save in list
      }
    }
    return xids;
  }

  /**
   * Frees a connection to make it eligible for reuse. The free list
   * is normally a last in, first out list (LIFO). This is efficient.
   * However, timed out connections are nice to hang onto for error
   * reporting, so they can be placed at the start. This is less
   * efficient, but hopefully is a rare occurence.
   *
   * Here, no need to verify the number of connections, we remove an
   * object from the xidConnections to put it in th freeConnections
   *
   */
  public synchronized void freeConnection(Xid id, boolean placeAtStart) {
    log.debug("StandardXADataSource:freeConnection");
    Object o = xidConnections.get(id); // lookup the connection by XID
    StandardXAStatefulConnection cur = (StandardXAStatefulConnection) o;
    // cast to something more convenient
    xidConnections.remove(id); // remove connection from in use list
    log.debug(
      "StandardXADataSource:freeConnection remove id from xidConnections");

    if (!deadConnections.containsKey(id)) {
      // if this isn't to be discarded
      /*
       try {
           log.debug("StandardXADataSource:freeConnection setAutoCommit(true):" + cur.id);
           log.debug("con='"+cur.con.toString()+"'");
           cur.con.setAutoCommit(acommit);
       } catch(SQLException e) {
           log.error("ERROR: Failed while autocommiting a connection: "+e);
       }
       */
      cur.setState(Status.STATUS_NO_TRANSACTION);
      // set its new internal state
      if (!freeConnections.contains(cur)) {
        if (placeAtStart) { // if we want to keep for as long as possible
          freeConnections.insertElementAt(cur, 0);
          // then place it at the start of the list
        } else {
          freeConnections.addElement(cur);
          // otherwise it's a LIFO list
        }
      }
    } else {
      deadConnections.remove(id);
      try {
        cur.con.close();
      } catch (SQLException e) {
        //ignore
      }
    }
    notify();

  }

  /**
   * Invoked by the timer thread to check all transactions
   * for timeouts. Returns the time of the next timeout event
   * after current timeouts have expired.
   */
  synchronized long checkTimeouts(long curTime) throws SQLException {
    //log.debug("StandardXADataSource:checkTimeouts");
    long nextTimeout = 0; // the earliest non-expired timeout in the list
    Enumeration cons = xidConnections.elements();
    // used to iterate through the used connections
    while (cons.hasMoreElements()) { // while there are more connections
      Object o = cons.nextElement(); // get the next connection
      StandardXAStatefulConnection cur = (StandardXAStatefulConnection) o;
      // cast to something more convenient
      if ((cur.timeout != 0)
        && // if connection has a timeout
       (
          curTime > cur.timeout)) {
        // ...and transaction has timed out
        //log.debug("StandardXADataSource:checkTimeouts connection timeout");
        cur.con.rollback();
        // undo everything to do with this transaction
        cur.timedOut = true; // flag that it has timed out
        //log.debug(cur.toString()+" timed out");
        freeConnection(cur.xid, true);
        // make the connection eligible for reuse
        // The timed out connection is eligible for reuse. The Xid and timedOut
        // flag will nevertheless remain valid until it is reallocated to another
        // global transaction. This gives the TM a *chance* to get a timeout
        // exception, but we won't hang on to it forever.
      } else { // transaction has not timed out
        if (cur.timeout != 0) { // but it has a timeout scheduled
          if ((cur.timeout < nextTimeout)
            || // and it's the next timeout to expire
           (
              nextTimeout == 0)) {
            // ...or first timeout we've found
            nextTimeout = cur.timeout; // set up next timeout
          }
        }
      }
    }
    return nextTimeout;
  }

  /**
   * Checks the start of the free list to see if the connection
   * previously associated with the supplied Xid has timed out.
   * <P>
   * Note that this can be an expensive operation as it has to
   * scan all free connections. so it should only be called in
   * the event of an error.
   */
  synchronized private void checkTimeouts(Xid xid) throws XAException {
    log.debug("StandardXADataSource:checkTimeouts");
    for (int i = 0;
      i < freeConnections.size();
      i++) { // check each free connection
      Object o = freeConnections.elementAt(i); // get next connection
      StandardXAStatefulConnection cur = (StandardXAStatefulConnection) o;
      // cast to something more convenient
      if (!cur.timedOut) { // if it hasn't timed out
        continue; // skip it
      }
      log.debug(
        "StandardXADataSource:checkTimeouts ("
          + i
          + "/"
          + freeConnections.size()
          + ") xid     = "
          + xid);
      log.debug(
        "StandardXADataSource:checkTimeouts cur.xid = " + cur.xid);
      if (xid.equals(cur.xid)) { // if we've found our xid
        cur.timedOut = false; // cancel time out
        throw new XAException(XAException.XA_RBTIMEOUT);
      }
    }
  }

  /**
   * Returns the connection associated with a given XID.
   * is reached, the Xid is found or an exception is thrown.
   */
  synchronized StandardXAStatefulConnection getConnection(
    Xid xid,
    boolean mustFind)
    throws XAException {
    log.debug(
      "StandardXADataSource:getConnection (xid="
        + xid
        + ", mustFind="
        + mustFind
        + ")");
    Object o = xidConnections.get(xid); // lookup the connection by XID
    log.debug("XID: " + o);
    StandardXAStatefulConnection cur = (StandardXAStatefulConnection) o;
    // cast to something more convenient
    if (mustFind) { // if we expected to find the connection
      if (cur == null) { // and we didn't
        log.debug(
          "StandardXADataSource:getConnection (StatefulConnection is null)");
        checkTimeouts(xid); // see if it's been freed during a timeout
        throw new XAException(XAException.XAER_NOTA);
        // not a valid XID
      }
    } else { // didn't expect to find the connection
      if (cur != null) { // but we found it anyway
        throw new XAException(XAException.XAER_DUPID); // duplicate XID
      }
    }
    log.debug(
      "StandardXADataSource:getConnection return connection associated with a given XID");
    return cur;
  }

  /**
   * Returns a connection from the free list, removing it
   * in the process. If none area available then a new
   * connection is created.
   */
  synchronized StandardXAStatefulConnection getFreeConnection()
    throws SQLException {
    log.debug("StandardXADataSource:getFreeConnection");
    StandardXAStatefulConnection cur = null;
    // this will be the return value
    int freeCount = freeConnections.size();
    // get number of free connections
    if (freeCount == 0) { // if there are no free connections
      log.debug(
        "StandardXADataSource:getFreeConnection  there are no free connections, get a new database connection");
      Connection con = super.getConnection(user, password);
      // get a new database connection
      cur = new StandardXAStatefulConnection(this, con);
      // make the connection stateful
    } else {
      Object o = freeConnections.lastElement(); // get the last element
      cur = (StandardXAStatefulConnection) o;
      // cast to something more convenient
      freeConnections.removeElementAt(freeCount - 1);
      // remove from free list
      cur.timeout = 0; // no timeout until start() called
      cur.timedOut = false; // cancel any time old out
    }
    log.debug(
      "StandardXADataSource:getFreeConnection return a connection from the free list");

    try {
      log.debug(
        "StandardXADataSource:getFreeConnection setAutoCommit(true)");
      // changed by cney - was false
                        cur.con.setAutoCommit(true);
    } catch (SQLException e) {
                  log.error(
                          "StandardXADataSource:getFreeConnection ERROR: Failed while autocommiting a connection: "
          + e);
    }

    return cur;
  }

  public void closeFreeConnection() {
    log.debug("StandardXADataSource:closeFreeConnection empty method TBD");
  }

  public void setMinCon(int min) {
    this.minCon = min;
  }

  public void setMaxCon(int max) {
    this.maxCon = max;
  }

  public void setDeadLockMaxWait(long deadLock) {
    this.deadLockMaxWait = deadLock;
  }

  public int getMinCon() {
    return this.minCon;
  }

  public int getMaxCon() {
    return this.maxCon;
  }

  public long getDeadLockMaxWait() {
    return this.deadLockMaxWait;
  }

  public int getAllConnections() {
    return xidConnections.size() + freeConnections.size();
  }

  public synchronized void processToWait() throws Exception {
    log.debug("StandardXADataSource:processToWait");
    int currentWait = 0;

    if (maxCon != 0) {
      while ((getAllConnections() >= maxCon)
        && (currentWait < getDeadLockMaxWait())) {
        dump();
        try {
          synchronized (this) {
            wait(getDeadLockRetryWait());
          }
        } catch (InterruptedException e) {
          log.error(
            "StandardXADataSource:processToWait ERROR: Failed while waiting for an object: "
              + e);
        }
        currentWait += getDeadLockRetryWait();
      }
      if (getAllConnections() >= getMaxCon())
        throw new Exception("StandardXADataSource:processToWait ERROR : impossible to obtain a new xa connection");
    }
  }

  public void dump() {
    for (int i = 0; i < freeConnections.size(); i++) {
      log.debug(
        "freeConnection:<"
          + freeConnections.elementAt(i).toString()
          + ">");
    }
    for (Enumeration enumeration = xidConnections.elements();
      enumeration.hasMoreElements();
      ) {
      log.debug("xidConnection:<" + enumeration.nextElement().toString() + ">");
    }

  }

  public void setDeadLockRetryWait(long deadLockRetryWait) {
    this.deadLockRetryWait = deadLockRetryWait;
  }

  public long getDeadLockRetryWait() {
    return this.deadLockRetryWait;
  }
  
  public String toString() {
    StringBuffer sb = new StringBuffer();
    sb.append("StandardXADataSource:\n");
    sb.append("     connection count=<"+this.connectionCount+">\n");
    if (deadConnections != null)
        sb.append("     number of dead connection=<"+this.deadConnections.size()+">\n");
    sb.append("     dead lock max wait=<"+this.deadLockMaxWait+">\n");
    sb.append("     dead lock retry wait=<"+this.deadLockRetryWait+">\n");
    if (driver != null)
      sb.append("     driver=<"+this.driver.toString()+">\n");
    sb.append("     driver name=<"+this.driverName+">\n");
    if (freeConnections != null) 
        sb.append("     number of *free* connections=<"+this.freeConnections.size()+">\n");
    sb.append("     max con=<"+this.maxCon+">\n");
    sb.append("     min con=<"+this.minCon+">\n");
    sb.append("     prepared stmt cache size=<"+this.preparedStmtCacheSize+">\n");
    sb.append("     transaction manager=<"+this.transactionManager+">\n");
    sb.append("     xid connection size=<"+this.xidConnections.size()+">\n");
    sb.append(super.toString());
    return sb.toString();
  }

    public Reference getReference() throws NamingException {
        log.debug("StandardXADataSource:getReference return a reference of the object");
        Reference ref = super.getReference();
  ref.add(new StringRefAddr("transactionManagerName", this.transactionManagerName));
        return ref;
    }

    public Object getObjectInstance(
        Object refObj,
        Name name,
        Context nameCtx,
        Hashtable env)
        throws Exception {

        super.getObjectInstance(refObj, name, nameCtx, env);
        Reference ref = (Reference) refObj;
        InitialContext ictx = new InitialContext(env);
        this.setTransactionManagerName((String) ref.get("transactionManagerName").getContent());
        if (this.transactionManagerName != null) {
            try {
                this.setTransactionManager(
                    (TransactionManager) ictx.lookup(this.transactionManagerName));
                } catch (NamingException e) {
                    // ignore, TransactionManager might be set later enlisting the XAResouce on the Transaction
                }
            }
            log.debug("StandardXADataSource:getObjectInstance: instance created");
            return this;
        }


}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.