/*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*
*/
package org.jboss.resource.adapter.jdbc;
import java.io.PrintWriter;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.Properties;
import java.util.HashMap;
import java.security.AccessController;
import java.security.PrivilegedAction;
import javax.resource.ResourceException;
import javax.resource.spi.ConnectionManager;
import javax.resource.spi.ConnectionRequestInfo;
import javax.resource.spi.ManagedConnectionFactory;
import javax.resource.spi.security.PasswordCredential;
import javax.security.auth.Subject;
import org.jboss.logging.Logger;
import org.jboss.resource.JBossResourceException;
/**
* BaseWrapperManagedConnectionFactory.java
*
*
* Created: Fri Apr 19 13:33:08 2002
*
* @author <a href="mailto:d_jencks@users.sourceforge.net">David Jencks</a>
* @version
*/
public abstract class BaseWrapperManagedConnectionFactory implements ManagedConnectionFactory, Serializable
{
public static final int TRACK_STATEMENTS_FALSE_INT = 0;
public static final int TRACK_STATEMENTS_TRUE_INT = 1;
public static final int TRACK_STATEMENTS_NOWARN_INT = 2;
public static final String TRACK_STATEMENTS_FALSE = "false";
public static final String TRACK_STATEMENTS_TRUE = "true";
public static final String TRACK_STATEMENTS_NOWARN = "nowarn";
protected final Logger log = Logger.getLogger(getClass());
protected String userName;
protected String password;
//This is used by Local wrapper for all properties, and is left
//in this class for ease of writing getConnectionProperties,
//which always holds the user/pw.
protected HashMap connectionProps = new HashMap();
protected int transactionIsolation = -1;
protected int preparedStatementCacheSize = 0;
protected boolean doQueryTimeout = false;
/**
* The variable <code>newConnectionSQL</code> holds an SQL
* statement which if not null is executed when a new Connection is
* obtained for a new ManagedConnection.
*
*/
protected String newConnectionSQL;
/**
* The variable <code>checkValidConnectionSQL</code> holds an sql
* statement that may be executed whenever a managed connection is
* removed from the pool, to check that it is still valid. This
* requires setting up an mbean to execute it when notified by the
* ConnectionManager.
*
*/
protected String checkValidConnectionSQL;
/**
* The classname used to check whether a connection is valid
*/
protected String validConnectionCheckerClassName;
/**
* The instance of the valid connection checker
*/
protected ValidConnectionChecker connectionChecker;
private String exceptionSorterClassName;
private ExceptionSorter exceptionSorter;
protected int trackStatements = TRACK_STATEMENTS_NOWARN_INT;
public BaseWrapperManagedConnectionFactory()
{
}
// implementation of javax.resource.spi.ManagedConnectionFactory interface
/**
*
* @return <description>
* @exception javax.resource.ResourceException <description>
*/
public PrintWriter getLogWriter() throws ResourceException
{
// TODO: implement this javax.resource.spi.ManagedConnectionFactory method
return null;
}
/**
*
* @param param1 <description>
* @exception javax.resource.ResourceException <description>
*/
public void setLogWriter(PrintWriter param1) throws ResourceException
{
// TODO: implement this javax.resource.spi.ManagedConnectionFactory method
}
/**
*
* @param cm <description>
* @return <description>
* @exception javax.resource.ResourceException <description>
*/
public Object createConnectionFactory(ConnectionManager cm) throws ResourceException
{
return new WrapperDataSource(this, cm);
}
/**
*
* @return <description>
* @exception javax.resource.ResourceException <description>
*/
public Object createConnectionFactory() throws ResourceException
{
throw new JBossResourceException("NYI");
//return createConnectionFactory(new DefaultConnectionManager());
}
//-----------Property setting code
/**
* Get the value of userName.
* @return value of userName.
*/
public String getUserName()
{
return userName;
}
/**
* Set the value of userName.
* @param userName Value to assign to userName.
*/
public void setUserName(final String userName)
{
this.userName = userName;
}
/**
* Get the value of password.
* @return value of password.
*/
public String getPassword()
{
return password;
}
/**
* Set the value of password.
* @param password Value to assign to password.
*/
public void setPassword(final String password)
{
this.password = password;
}
public int getPreparedStatementCacheSize()
{
return preparedStatementCacheSize;
}
public void setPreparedStatementCacheSize(int size)
{
preparedStatementCacheSize = size;
}
public boolean getTxQueryTimeout()
{
return doQueryTimeout;
}
public void setTxQueryTimeout(boolean qt)
{
doQueryTimeout = qt;
}
/**
* Gets the TransactionIsolation attribute of the JDBCManagedConnectionFactory
* object
*
* @return The TransactionIsolation value
*/
public String getTransactionIsolation()
{
switch (this.transactionIsolation)
{
case Connection.TRANSACTION_NONE :
return "TRANSACTION_NONE";
case Connection.TRANSACTION_READ_COMMITTED :
return "TRANSACTION_READ_COMMITTED";
case Connection.TRANSACTION_READ_UNCOMMITTED :
return "TRANSACTION_READ_UNCOMMITTED";
case Connection.TRANSACTION_REPEATABLE_READ :
return "TRANSACTION_REPEATABLE_READ";
case Connection.TRANSACTION_SERIALIZABLE :
return "TRANSACTION_SERIALIZABLE";
case -1 :
return "DEFAULT";
default :
return Integer.toString(transactionIsolation);
}
}
/**
* Sets the TransactionIsolation attribute of the JDBCManagedConnectionFactory
* object
*
* @param transactionIsolation The new TransactionIsolation value
*/
public void setTransactionIsolation(String transactionIsolation)
{
if (transactionIsolation.equals("TRANSACTION_NONE"))
{
this.transactionIsolation = Connection.TRANSACTION_NONE;
}
else if (transactionIsolation.equals("TRANSACTION_READ_COMMITTED"))
{
this.transactionIsolation = Connection.TRANSACTION_READ_COMMITTED;
}
else if (transactionIsolation.equals("TRANSACTION_READ_UNCOMMITTED"))
{
this.transactionIsolation = Connection.TRANSACTION_READ_UNCOMMITTED;
}
else if (transactionIsolation.equals("TRANSACTION_REPEATABLE_READ"))
{
this.transactionIsolation = Connection.TRANSACTION_REPEATABLE_READ;
}
else if (transactionIsolation.equals("TRANSACTION_SERIALIZABLE"))
{
this.transactionIsolation = Connection.TRANSACTION_SERIALIZABLE;
}
else
{
try
{
this.transactionIsolation = Integer.parseInt(transactionIsolation);
}
catch (NumberFormatException nfe)
{
throw new IllegalArgumentException("Setting Isolation level to unknown state: " + transactionIsolation);
}
}
}
/**
* Get the NewConnectionSQL value.
* @return the NewConnectionSQL value.
*/
public String getNewConnectionSQL()
{
return newConnectionSQL;
}
/**
* Set the NewConnectionSQL value.
* @param newConnectionSQL The new NewConnectionSQL value.
*/
public void setNewConnectionSQL(String newConnectionSQL)
{
this.newConnectionSQL = newConnectionSQL;
}
/**
* Get the CheckValidConnectionSQL value.
* @return the CheckValidConnectionSQL value.
*/
public String getCheckValidConnectionSQL()
{
return checkValidConnectionSQL;
}
/**
* Set the CheckValidConnectionSQL value.
* @param checkValidConnectionSQL The new CheckValidConnectionSQL value.
*/
public void setCheckValidConnectionSQL(String checkValidConnectionSQL)
{
this.checkValidConnectionSQL = checkValidConnectionSQL;
}
/**
* Whether to track statements
* @return true when tracking statements
*/
public String getTrackStatements()
{
if (trackStatements == TRACK_STATEMENTS_FALSE_INT)
return TRACK_STATEMENTS_FALSE;
else if (trackStatements == TRACK_STATEMENTS_TRUE_INT)
return TRACK_STATEMENTS_TRUE;
return TRACK_STATEMENTS_NOWARN;
}
/**
* Set the track statements value.
* @param value true to track statements.
*/
public void setTrackStatements(String value)
{
if (value == null)
throw new IllegalArgumentException("Null value for trackStatements");
String trimmed = value.trim();
if (trimmed.equalsIgnoreCase(TRACK_STATEMENTS_FALSE))
trackStatements = TRACK_STATEMENTS_FALSE_INT;
else if (trimmed.equalsIgnoreCase(TRACK_STATEMENTS_TRUE))
trackStatements = TRACK_STATEMENTS_TRUE_INT;
else
trackStatements = TRACK_STATEMENTS_NOWARN_INT;
}
/**
* Get the ExceptionSorterClassname value.
* @return the ExceptionSorterClassname value.
*/
public String getExceptionSorterClassName()
{
return exceptionSorterClassName;
}
/**
* Set the ExceptionSorterClassname value.
* @param exceptionSorterClassName The new ExceptionSorterClassName value.
*/
public void setExceptionSorterClassName(String exceptionSorterClassName)
{
this.exceptionSorterClassName = exceptionSorterClassName;
}
/**
* Get the valid connection checker class name
*
* @return the class name
*/
public String getValidConnectionCheckerClassName()
{
return validConnectionCheckerClassName;
}
/**
* Set the valid connection checker class name
*
* @param value the class name
*/
public void setValidConnectionCheckerClassName(String value)
{
validConnectionCheckerClassName = value;
}
/**
* Gets full set of connection properties, i.e. whatever is provided
* in config plus "user" and "password" from subject/cri.
*
* <p>Note that the set is used to match connections to datasources as well
* as to create new managed connections.
*
* <p>In fact, we have a problem here. Theoretically, there is a possible
* name collision between config properties and "user"/"password".
*/
protected Properties getConnectionProperties(Subject subject, ConnectionRequestInfo cri) throws ResourceException
{
if (cri != null && cri.getClass() != WrappedConnectionRequestInfo.class)
{
throw new JBossResourceException("Wrong kind of ConnectionRequestInfo: " + cri.getClass());
} // end of if ()
// NOTE: we do this because we do not have to synchronize at all on the connectionProps
// AVOID GLOBAL Synchronizations. (Bill Burke)
Properties props = new Properties();
props.putAll(connectionProps);
if (subject != null)
{
if (SubjectActions.addMatchingProperties(subject, props, this) == true)
{
return props;
}
throw new JBossResourceException("No matching credentials in Subject!");
} // end of if ()
WrappedConnectionRequestInfo lcri = (WrappedConnectionRequestInfo) cri;
if (lcri != null)
{
props.setProperty("user", (lcri.getUserName() == null) ? "" : lcri.getUserName());
props.setProperty("password", (lcri.getPassword() == null) ? "" : lcri.getPassword());
return props;
} // end of if ()
if (userName != null)
{
props.setProperty("user", userName);
props.setProperty("password", (password == null) ? "" : password);
}
return props;
}
boolean isExceptionFatal(SQLException e)
{
if (exceptionSorter != null)
return exceptionSorter.isExceptionFatal(e);
if (exceptionSorterClassName != null)
{
try
{
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Class clazz = cl.loadClass(exceptionSorterClassName);
exceptionSorter = (ExceptionSorter) clazz.newInstance();
return exceptionSorter.isExceptionFatal(e);
}
catch (Exception e2)
{
log.warn("exception trying to create exception sorter (disabling):", e2);
exceptionSorter = new NullExceptionSorter();
}
}
return false;
}
/**
* Checks whether a connection is valid
*/
SQLException isValidConnection(Connection c)
{
// Already got a checker
if (connectionChecker != null)
return connectionChecker.isValidConnection(c);
// Class specified
if (validConnectionCheckerClassName != null)
{
try
{
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Class clazz = cl.loadClass(validConnectionCheckerClassName);
connectionChecker = (ValidConnectionChecker) clazz.newInstance();
return connectionChecker.isValidConnection(c);
}
catch (Exception e)
{
log.warn("Exception trying to create connection checker (disabling):", e);
connectionChecker = new NullValidConnectionChecker();
}
}
// SQL statement specified
if (checkValidConnectionSQL != null)
{
connectionChecker = new CheckValidConnectionSQL(checkValidConnectionSQL);
return connectionChecker.isValidConnection(c);
}
// No Check
return null;
}
static class SubjectActions implements PrivilegedAction
{
Subject subject;
Properties props;
ManagedConnectionFactory mcf;
SubjectActions(Subject subject, Properties props, ManagedConnectionFactory mcf)
{
this.subject = subject;
this.props = props;
this.mcf = mcf;
}
public Object run()
{
Iterator i = subject.getPrivateCredentials().iterator();
while (i.hasNext())
{
Object o = i.next();
if (o instanceof PasswordCredential)
{
PasswordCredential cred = (PasswordCredential) o;
if (cred.getManagedConnectionFactory().equals(mcf))
{
props.setProperty("user", (cred.getUserName() == null) ? "" : cred.getUserName());
props.setProperty("password", new String(cred.getPassword()));
return Boolean.TRUE;
}
}
}
return Boolean.FALSE;
}
static boolean addMatchingProperties(Subject subject, Properties props, ManagedConnectionFactory mcf)
{
SubjectActions action = new SubjectActions(subject, props, mcf);
Boolean matched = (Boolean) AccessController.doPrivileged(action);
return matched.booleanValue();
}
}
}// BaseWrapperManagedConnectionFactory