blob: 3e32c7ab8d34dd386f895bc1c11d0c1f21d78e26 [file] [log] [blame]
/*
* 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;
}
}