code: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements. See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership. The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the  "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  /*
   * $Id$
   */
  package org.apache.xalan.lib.sql;
  
  import java.sql.Connection;
  import java.sql.DatabaseMetaData;
  import java.sql.Driver;
  import java.sql.DriverManager;
  import java.sql.SQLException;
  import java.util.Enumeration;
  import java.util.Iterator;
  import java.util.Properties;
  import java.util.Vector;
  
  import org.apache.xalan.res.XSLMessages;
  import org.apache.xalan.res.XSLTErrorResources;
  
  /**
   * For internal connectiones, i.e. Connection information supplies in the
   * Stylesheet. The Default Connection Pool will be used.
   */
  public class DefaultConnectionPool implements ConnectionPool
  {
    /**
     * A placeholder thast will keep the driver loaded
     * between calls.
     */
    private Driver m_Driver = null;
    /**
     */
    private static final boolean DEBUG = false;
  
    /**
     * The basic information to make a JDBC Connection
     */
    private String m_driver = new String("");
    /**
     */
    private String m_url = new String("");
  
  
    /**
     * The mimimum size of the connection pool, if the
     * number of available connections falls below this
     * mark, min connections will be allocated. The Connection
     * Pool will always be somewhere between MinSize and MinSize*2
     */
    private int m_PoolMinSize = 1;
  
  
    /**
     * Always implement the properties mechinism, if the Password
     * or Username is set seperatly then we will add them to the
     * property manually.
     */
    private Properties m_ConnectionProtocol = new Properties();
  
    /**
     * Storage for the PooledConnections
     */
    private Vector m_pool = new Vector();
  
    /**
     * Are we active ??
     */
    private boolean m_IsActive = false;
  
    /**
     */
    public DefaultConnectionPool( ) {}
  
  
    /**
     * Return our current Active state
     *
     */
    public boolean isEnabled( )
    {
      return m_IsActive;
    }
  
    /**
     * Set the driver call to be used to create connections
     * @param d
     *
     */
    public void setDriver( String d )
    {
      m_driver = d;
    }
  
    /**
     * Set the url used to connect to the database
     * @param url
     *
     */
    public void setURL( String url )
    {
      m_url = url;
    }
  
    /**
     * Go through the connection pool and release any connections
     * that are not InUse;
     *
     */
    public void freeUnused( )
    {
      // Iterate over the entire pool closing the
      // JDBC Connections.
      Iterator i = m_pool.iterator();
      while(i.hasNext())
      {
        PooledConnection pcon =
          (PooledConnection) i.next();
  
        // If the PooledConnection is not in use, close it
        if ( pcon.inUse() == false )
        {
          if (DEBUG)
          {
            System.err.println("Closing JDBC Connection ");
          }
          
          pcon.close();
          i.remove();        
        }
      }
  
    }
  
    /**
     * Is our ConnectionPool have any connections that are still in Use ??
     *
     */
    public boolean hasActiveConnections( )
    {
      return (m_pool.size() > 0);
    }
  
  
    /**
     * Set the password in the property set.
     * @param p
     *
     */
    public void setPassword( String p )
    {
      m_ConnectionProtocol.put("password", p);
    }
  
    /**
     * Set the user name in the property set
     * @param u
     *
     */
    public void setUser( String u )
    {
      m_ConnectionProtocol.put("user", u);
    }
  
    /**
     * The Protocol string is used to pass in other connection
     * properties. A properties file is a general purpose container
     *
     * @param p
     *
     */
    public void setProtocol( Properties p )
    {
      Enumeration e = p.keys();
      while (e.hasMoreElements())
      {
        String key = (String) e.nextElement();
        m_ConnectionProtocol.put(key, p.getProperty(key));
      }
    }
  
  
    /**
     * Override the current number of connections to keep in the pool. This
     * setting will only have effect on a new pool or when a new connection
     * is requested and there is less connections that this setting.
     * @param n
     *
     */
    public void setMinConnections( int n )
    {
      m_PoolMinSize = n;
    }
  
    /**
     * Try to aquire a new connection, if it succeeds then return
     * true, else return false.
     * Note: This method will cause the connection pool to be built.
     *
     */
    public boolean testConnection( )
    {
      try
      {
        if (DEBUG)
        {
          System.out.println("Testing Connection");
        }
  
        Connection conn = getConnection();
  
        if (DEBUG)
        {
          DatabaseMetaData dma = conn.getMetaData();
  
          System.out.println("\nConnected to " + dma.getURL());
          System.out.println("Driver   " + dma.getDriverName());
          System.out.println("Version  " + dma.getDriverVersion());
          System.out.println("");
        }
  
        if (conn == null) return false;
  
        releaseConnection(conn);
  
        if (DEBUG)
        {
          System.out.println("Testing Connection, SUCCESS");
        }
  
        return true;
      }
      catch(Exception e)
      {
        if (DEBUG)
        {
          System.out.println("Testing Connection, FAILED");
          e.printStackTrace();
        }
  
        return false;
      }
  
    }
  
  
    // Find an available connection
    /**
     * @return Connection
     * @throws SQLException
     * @throws IllegalArgumentException
     */
    public synchronized Connection getConnection( )throws IllegalArgumentException, SQLException
    {
  
      PooledConnection pcon = null;
  
      // We will fill up the pool any time it is less than the
      // Minimum. THis could be cause by the enableing and disabling
      // or the pool.
      //
      if ( m_pool.size() < m_PoolMinSize ) { initializePool(); }
  
      // find a connection not in use
      for ( int x = 0; x < m_pool.size(); x++ )
      {
  
        pcon = (PooledConnection) m_pool.elementAt(x);
  
        // Check to see if the Connection is in use
        if ( pcon.inUse() == false )
        {
          // Mark it as in use
          pcon.setInUse(true);
          // return the JDBC Connection stored in the
          // PooledConnection object
          return pcon.getConnection();
        }
      }
  
      // Could not find a free connection,
      // create and add a new one
  
      // Create a new JDBC Connection
      Connection con = createConnection();
  
      // Create a new PooledConnection, passing it the JDBC
      // Connection
      pcon = new PooledConnection(con);
  
      // Mark the connection as in use
      pcon.setInUse(true);
  
      // Add the new PooledConnection object to the pool
      m_pool.addElement(pcon);
  
      // return the new Connection
      return pcon.getConnection();
    }
  
    /**
     * @param con
     *
     * @throws SQLException
     */
    public synchronized void releaseConnection( Connection con )throws SQLException
    {
  
      // find the PooledConnection Object
      for ( int x = 0; x < m_pool.size(); x++ )
      {
  
        PooledConnection pcon =
          (PooledConnection) m_pool.elementAt(x);
  
        // Check for correct Connection
        if ( pcon.getConnection() == con )
        {
          if (DEBUG)
          {
            System.out.println("Releasing Connection " + x);
          }
  
          if (! isEnabled())
          {
            con.close();
            m_pool.removeElementAt(x);
            if (DEBUG)
            {
              System.out.println("-->Inactive Pool, Closing connection");
            }
  
          }
          else
          {
            // Set it's inuse attribute to false, which
            // releases it for use
            pcon.setInUse(false);
          }
  
          break;
        }
      }
    }
  
  
    /**
     * @param con
     *
     * @throws SQLException
     */
    public synchronized void releaseConnectionOnError( Connection con )throws SQLException
    {
  
      // find the PooledConnection Object
      for ( int x = 0; x < m_pool.size(); x++ )
      {
  
        PooledConnection pcon =
          (PooledConnection) m_pool.elementAt(x);
  
        // Check for correct Connection
        if ( pcon.getConnection() == con )
        {
          if (DEBUG)
          {
            System.out.println("Releasing Connection On Error" + x);
          }
  
          con.close();
          m_pool.removeElementAt(x);
          if (DEBUG)
          {
            System.out.println("-->Inactive Pool, Closing connection");
          }
          break;
        }
      }
    }
  
  
    /**
     *
     * @throws SQLException
     */
    private Connection createConnection( )throws SQLException
    {
      Connection con = null;
  
      // Create a Connection directly from the Driver that was loaded
      // with the context class loader. This is to support JDK1.4
      con = m_Driver.connect(m_url, m_ConnectionProtocol );
  
      return con;
    }
  
    // Initialize the pool
    /**
     *
     * @throws IllegalArgumentException
     * @throws SQLException
     */
    public synchronized void initializePool( )throws IllegalArgumentException, SQLException
    {
  
       // Check our initial values
       if ( m_driver == null )
       {
         throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_NO_DRIVER_NAME_SPECIFIED, null));
         // "No Driver Name Specified!");
       }
  
       if ( m_url == null )
       {
         throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_NO_URL_SPECIFIED, null));
         // "No URL Specified!");
       }
  
       if ( m_PoolMinSize < 1 )
       {
         throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_POOLSIZE_LESS_THAN_ONE, null));
         // "Pool size is less than 1!");
       }
  
       // Create the Connections
       // Load the Driver class file
  
       try
       {
          // We have also had problems with drivers unloading
          // load an instance that will get freed with the class.
          m_Driver = (Driver) ObjectFactory.newInstance(
            m_driver, ObjectFactory.findClassLoader(), true);
  
          // Register the Driver that was loaded with the Context Classloader
          // but we will ask for connections directly from the Driver
          // instance
          DriverManager.registerDriver(m_Driver);
       }
       catch(ObjectFactory.ConfigurationError e)
       {
         throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_DRIVER_NAME, null));
         // "Invalid Driver Name Specified!");
       }
       catch(Exception e)
       {
         throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_DRIVER_NAME, null));
       }
  
       // IF we are not active, don't actuall build a pool yet
       // Just set up the driver and periphal items.
       if ( !m_IsActive) return;
  
      // Create Connections based on the size member
      do
      {
  
        Connection con = createConnection();
  
        if ( con != null )
        {
  
          // Create a PooledConnection to encapsulate the
          // real JDBC Connection
          PooledConnection pcon = new PooledConnection(con);
  
          // Add the Connection the pool.
          addConnection(pcon);
  
          if (DEBUG) System.out.println("Adding DB Connection to the Pool");
        }
      }
      while (m_pool.size() < m_PoolMinSize);
    }
  
    // Adds the PooledConnection to the pool
    /**
     * @param value
     *
     */
    private void addConnection( PooledConnection value )
    {
      // Add the PooledConnection Object to the vector
      m_pool.addElement(value);
    }
  
  
    /**
     *
     * @throws Throwable
     */
    protected void finalize( )throws Throwable
    {
      if (DEBUG)
      {
        System.out.println("In Default Connection Pool, Finalize");
      }
  
      // Iterate over the entire pool closing the
      // JDBC Connections.
      for ( int x = 0; x < m_pool.size(); x++ )
      {
  
        if (DEBUG)
        {
          System.out.println("Closing JDBC Connection " + x);
        }
  
        PooledConnection pcon =
          (PooledConnection) m_pool.elementAt(x);
  
        // If the PooledConnection is not in use, close it
        if ( pcon.inUse() == false ) { pcon.close();  }
        else
        {
          if (DEBUG)
          {
            System.out.println("--> Force close");
          }
  
          // If it still in use, sleep for 30 seconds and
          // force close.
          try
          {
            java.lang.Thread.sleep(30000);
            pcon.close();
          }
          catch (InterruptedException ie)
          {
            if (DEBUG) System.err.println(ie.getMessage());
          }
        }
      }
  
      if (DEBUG)
      {
        System.out.println("Exit Default Connection Pool, Finalize");
      }
  
      super.finalize();
    }
  
    /**
     * The Pool can be Enabled and Disabled. Disabling the pool
     * closes all the outstanding Unused connections and any new
     * connections will be closed upon release.
     *
     * @param flag Control the Connection Pool.
     * If it is enabled then Connections will actuall be held
     * around. If disabled then all unused connections will be instantly
     * closed and as connections are released they are closed and removed
     * from the pool.
     *
     *
     */
    public void setPoolEnabled( boolean flag )
    {
       m_IsActive = flag;
       if ( ! flag )
        freeUnused();
    }
  
  }
  