/**
* Alipay.com Inc.
* Copyright (c) 2004-2012 All Rights Reserved.
*/
package com.alipay.zdal.datasource.resource.adapter.jdbc;
import java.io.PrintWriter;
import java.io.Serializable;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import javax.security.auth.Subject;
import org.apache.log4j.Logger;
import com.alipay.zdal.common.jdbc.sorter.ExceptionSorter;
import com.alipay.zdal.common.jdbc.sorter.NullExceptionSorter;
import com.alipay.zdal.datasource.ZDataSource;
import com.alipay.zdal.datasource.resource.JBossResourceException;
import com.alipay.zdal.datasource.resource.ResourceException;
import com.alipay.zdal.datasource.resource.spi.ConnectionManager;
import com.alipay.zdal.datasource.resource.spi.ConnectionRequestInfo;
import com.alipay.zdal.datasource.resource.spi.ManagedConnectionFactory;
import com.alipay.zdal.datasource.resource.spi.ValidatingManagedConnectionFactory;
import com.alipay.zdal.datasource.resource.spi.security.PasswordCredential;
/**
* BaseWrapperManagedConnectionFactory
*
* @author ����
* @version $Id: BaseWrapperManagedConnectionFactory.java, v 0.1 2014-1-6 ����05:27:24 Exp $
*/
public abstract class BaseWrapperManagedConnectionFactory implements ManagedConnectionFactory,
ValidatingManagedConnectionFactory,
Serializable {
/** @since 4.0.1 */
static final long serialVersionUID = -84923705377702088L;
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;
protected String encPassword;
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;
/** Whether to share cached prepared statements */
protected boolean sharePS = false;
protected boolean isTransactionQueryTimeout = false;
protected int queryTimeout = 0;
private boolean validateOnMatch;
//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 final Properties connectionProps = new Properties();
public Properties getConnectionProps() {
return connectionProps;
}
/**
*
*/
public BaseWrapperManagedConnectionFactory() {
}
public PrintWriter getLogWriter() throws ResourceException {
// TODO: implement this javax.resource.spi.ManagedConnectionFactory method
return null;
}
public void setLogWriter(PrintWriter param1) throws ResourceException {
// TODO: implement this javax.resource.spi.ManagedConnectionFactory method
}
/**
*
* @param cm
* @param dataSourceName
* @param zdatasource
* @return
* @throws ResourceException
*/
public Object createConnectionFactory(ConnectionManager cm, String dataSourceName,
ZDataSource zdatasource) throws ResourceException {
return new WrapperDataSource(this, cm, dataSourceName, zdatasource);
}
/**
* @see com.alipay.zdal.datasource.resource.spi.ManagedConnectionFactory#createConnectionFactory()
*/
public Object createConnectionFactory() throws ResourceException {
throw new JBossResourceException("NYI");
}
public String getUserName() {
return userName;
}
public void setUserName(final String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(final String password) {
this.password = password;
}
public int getPreparedStatementCacheSize() {
return preparedStatementCacheSize;
}
public void setPreparedStatementCacheSize(int size) {
preparedStatementCacheSize = size;
}
public boolean getSharePreparedStatements() {
return sharePS;
}
public void setSharePreparedStatements(boolean sharePS) {
this.sharePS = sharePS;
}
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);
}
}
public void setTransactionIsolation(String transactionIsolation) {
if (transactionIsolation.toUpperCase().equals("TRANSACTION_NONE")) {
this.transactionIsolation = Connection.TRANSACTION_NONE;
} else if (transactionIsolation.toUpperCase().equals("TRANSACTION_READ_COMMITTED")) {
this.transactionIsolation = Connection.TRANSACTION_READ_COMMITTED;
} else if (transactionIsolation.toUpperCase().equals("TRANSACTION_READ_UNCOMMITTED")) {
this.transactionIsolation = Connection.TRANSACTION_READ_UNCOMMITTED;
} else if (transactionIsolation.toUpperCase().equals("TRANSACTION_REPEATABLE_READ")) {
this.transactionIsolation = Connection.TRANSACTION_REPEATABLE_READ;
} else if (transactionIsolation.toUpperCase().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);
}
}
}
public String getNewConnectionSQL() {
return newConnectionSQL;
}
public void setNewConnectionSQL(String newConnectionSQL) {
this.newConnectionSQL = newConnectionSQL;
}
public String getCheckValidConnectionSQL() {
return checkValidConnectionSQL;
}
public void setCheckValidConnectionSQL(String checkValidConnectionSQL) {
this.checkValidConnectionSQL = checkValidConnectionSQL;
}
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;
}
public boolean getValidateOnMatch() {
return this.validateOnMatch;
}
public void setValidateOnMatch(boolean validateOnMatch) {
this.validateOnMatch = validateOnMatch;
}
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;
}
}
public String getExceptionSorterClassName() {
return exceptionSorterClassName;
}
public void setExceptionSorterClassName(String exceptionSorterClassName) {
this.exceptionSorterClassName = exceptionSorterClassName;
}
public String getValidConnectionCheckerClassName() {
return validConnectionCheckerClassName;
}
public void setValidConnectionCheckerClassName(String value) {
validConnectionCheckerClassName = value;
}
public boolean isTransactionQueryTimeout() {
return isTransactionQueryTimeout;
}
public void setTransactionQueryTimeout(boolean value) {
isTransactionQueryTimeout = value;
}
public int getQueryTimeout() {
return queryTimeout;
}
public void setQueryTimeout(int timeout) {
queryTimeout = timeout;
}
/**
* @see com.alipay.zdal.datasource.resource.spi.ValidatingManagedConnectionFactory#getInvalidConnections(java.util.Set)
*/
public Set getInvalidConnections(final Set connectionSet) throws ResourceException {
final Set invalid = new HashSet();
for (Iterator iter = connectionSet.iterator(); iter.hasNext();) {
Object anonymous = iter.next();
if (anonymous instanceof BaseWrapperManagedConnection) {
BaseWrapperManagedConnection mc = (BaseWrapperManagedConnection) anonymous;
if (!mc.checkValid()) {
invalid.add(mc);
}
}
}
return invalid;
}
/**
* 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());
}
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!");
}
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;
}
if (userName != null) {
props.setProperty("user", userName);
props.setProperty("password", (password == null) ? "" : password);
}
return props;
}
boolean isExceptionFatal(SQLException e) {
try {
if (exceptionSorter != null) {
return exceptionSorter.isExceptionFatal(e);
}
if (exceptionSorterClassName != null) {
try {
// ClassLoader cl = Thread.currentThread().getContextClassLoader();
ClassLoader cl = this.getClass().getClassLoader();
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();
}
}
} catch (Throwable t) {
log.warn("Error checking exception fatality: ", t);
}
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();
ClassLoader cl = this.getClass().getClassLoader();
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;
}
/**
* @see java.security.PrivilegedAction#run()
*/
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());
if (cred.getPassword() != null)
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();
}
}
public String getEncPassword() {
return encPassword;
}
public void setEncPassword(String encPassword) {
this.encPassword = encPassword;
}
}