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.
   */
  package org.apache.xalan.lib.sql;
  
  import java.lang.reflect.Method;
  import java.sql.Connection;
  import java.sql.SQLException;
  import java.util.Properties;
  
  import javax.naming.InitialContext;
  import javax.naming.NamingException;
  
  
  /**
   * A Connection Pool that wraps a JDBC datasource to provide connections.
   * 
   * An instance of this class is created by <code>XConnection</code> when it
   * attempts to resolves a <code>ConnectionPool</code> name as a JNDI data source.
   * 
   * Most methods in this implementation do nothing since configuration is handled
   * by the underlying JDBC datasource.  Users should always call
   * <code>XConnection.close()</code> from their stylsheet to explicitely close
   * their connection.  However, since there is no way to enforce this
   * (Yikes!), it is recommended that a relatively short datasource timeout
   * be used to prevent dangling connections.
   */
  public class JNDIConnectionPool implements ConnectionPool
  {
    
    /**
     * Reference to the datasource
     */
    protected Object jdbcSource = null;
  
    /** 
     * To maintain Java 1.3 compatibility, we need to work with the
     * DataSource class through Reflection. The getConnection method
     * is one of the methods used, and there are two different flavors.
     * 
     */  
    private Method getConnectionWithArgs = null;
    private Method getConnection = null;
    
    
    /**
     * The unique jndi path for this datasource.
     */
    protected String jndiPath = null;
    
    /**
     * User name for protected datasources.
     */
    protected String user = null;
    
    /**
     * Password for protected datasources.
     */
    protected String pwd = null;
    
    /**
     * Use of the default constructor requires the jndi path to be set via
     * setJndiPath().
     */
    public JNDIConnectionPool() {  }
    
    /**
     * Creates a connection pool with a specified JNDI path. 
     * @param jndiDatasourcePath Complete path to the JNDI datasource
     */
    public JNDIConnectionPool(String jndiDatasourcePath)
    {
      jndiPath = jndiDatasourcePath.trim();
    }
    
    /**
     * Sets the path for the jndi datasource 
     * @param jndiPath 
     */
    public void setJndiPath(String jndiPath)
    {
      this.jndiPath = jndiPath;
    }
  
    /**
     * Returns the path for the jndi datasource 
     * @param jndiPath 
     */
    public String getJndiPath()
    {
      return jndiPath;
    }
  
    /**
     * Always returns true.
     * This method was intended to indicate if the pool was enabled, however, in
     * this implementation that is not relavant.
     * @return 
     */
    public boolean isEnabled()
    {
      return true;
    }
  
    /**
     * Not implemented and will throw an Error if called.
     * 
     * Connection configuration is handled by the underlying JNDI DataSource.
     * @param d 
     */
    public void setDriver(String d)
    {
      throw new Error(
        "This method is not supported. " +
        "All connection information is handled by the JDBC datasource provider");
    }
  
    /**
     * Not implemented and will throw an Error if called.
     * 
     * Connection configuration is handled by the underlying JNDI DataSource.
     * @param d 
     */
    public void setURL(String url)
    {
      throw new Error(
        "This method is not supported. " +
        "All connection information is handled by the JDBC datasource provider");
    }
  
    /**
     * Intended to release unused connections from the pool.
     * Does nothing in this implementation.
     */
    public void freeUnused()
    {
      //Do nothing - not an error to call this method
    }
  
    /**
     * Always returns false, indicating that this wrapper has no idea of what
     * connections the underlying JNDI source is maintaining.
     * @return 
     */
    public boolean hasActiveConnections()
    {
      return false;
    }
  
    /**
     * Sets the password for the connection.
     * If the jndi datasource does not require a password (which is typical),
     * this can be left null.
     * @param p the password
     */
    public void setPassword(String p)
    {
      
      if (p != null) p = p.trim();
      if (p != null && p.length() == 0) p = null;
      
      pwd = p;
    }
  
    /**
     * Sets the user name for the connection.
     * If the jndi datasource does not require a user name (which is typical),
     * this can be left null.
     * @param u the user name
     */
    public void setUser(String u)
    {
      
      if (u != null) u = u.trim();
      if (u != null && u.length() == 0) u = null;
      
      user = u;
    }
  
    /**
     * Returns a connection from the JDNI DataSource found at the JNDI Datasource
     * path.
     * 
     * @return 
     * @throws SQLException 
     */
    public Connection getConnection() throws SQLException
    {
      if (jdbcSource == null)
      {
        try
        {
          findDatasource();
        }
        catch (NamingException ne)
        {
          throw new SQLException(
            "Could not create jndi context for " + 
            jndiPath + " - " + ne.getLocalizedMessage());
        }
      }
      
      try
      {
        if (user != null || pwd != null)
        {
          Object arglist[] = { user, pwd }; 
          return (Connection) getConnectionWithArgs.invoke(jdbcSource, arglist);
        }
        else
        {
          Object arglist[] = {}; 
          return (Connection) getConnection.invoke(jdbcSource, arglist);
        }
      }
      catch (Exception e)
      {
        throw new SQLException(
          "Could not create jndi connection for " + 
          jndiPath + " - " + e.getLocalizedMessage());
      }
      
    }
    
    /**
     * Internal method used to look up the datasource. 
     * @throws NamingException 
     */
    protected void findDatasource() throws NamingException
    {
      try
      {
        InitialContext context = new InitialContext();
        jdbcSource =  context.lookup(jndiPath);
        
        Class withArgs[] = { String.class, String.class };
        getConnectionWithArgs = 
          jdbcSource.getClass().getDeclaredMethod("getConnection", withArgs);
        
        Class noArgs[] = { };
        getConnection = 
          jdbcSource.getClass().getDeclaredMethod("getConnection", noArgs);
        
      }
      catch (NamingException e)
      {
        throw e;
      }
      catch (NoSuchMethodException e)
      {
        // For simpleification, we will just throw a NamingException. We will only
        // use the message part of the exception anyway.
        throw new NamingException("Unable to resolve JNDI DataSource - " + e);
      }
    }
  
    public void releaseConnection(Connection con) throws SQLException
    {
      con.close();
    }
  
    public void releaseConnectionOnError(Connection con) throws SQLException
    {
      con.close();
    }
  
    /**
     * Releases the reference to the jndi datasource.
     * The original intention of this method was to actually turn the pool *off*.
     * Since we are not managing the pool, we simply release our reference to
     * the datasource.  Future calls to the getConnection will simply recreate
     * the datasource.
     * @param flag If false, the reference to the datasource is released.
     */
    public void setPoolEnabled(boolean flag)
    {
      if (! flag) jdbcSource = null;
    }
  
    /**
     * Ignored in this implementation b/c the pooling is determined by the jndi dataosource. 
     * @param p
     */
    public void setProtocol(Properties p)
    {
      /* ignore - properties are determined by datasource */
    }
    
    /**
     * Ignored in this implementation b/c the pooling is determined by the jndi dataosource. 
     * @param n 
     */
    public void setMinConnections(int n)
    {
      /* ignore - pooling is determined by datasource */
    }
  
    /**
     * A simple test to see if the jndi datasource exists.
     * 
     * Note that this test does not ensure that the datasource will return valid
     * connections.
     */
    public boolean testConnection()
    {
      if (jdbcSource == null)
      {
        try
        {
          findDatasource();
        }
        catch (NamingException ne)
        {
          return false;
        }
      }
      
      return true;
    }
  
  
  
  }
  