blob: 9a61c4534cf01078a25a525cfae0ed0ea026ff4f [file] [log] [blame]
/*
* Copyright (c) 2021, 2022 Contributors to the Eclipse Foundation.
* Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package com.sun.gjc.spi;
import static com.sun.gjc.common.DataSourceSpec.CONNECTIONVALIDATIONREQUIRED;
import static com.sun.gjc.common.DataSourceSpec.GUARANTEEISOLATIONLEVEL;
import static com.sun.gjc.common.DataSourceSpec.TRANSACTIONISOLATION;
import static com.sun.gjc.common.DataSourceSpec.VALIDATIONCLASSNAME;
import static com.sun.gjc.common.DataSourceSpec.VALIDATIONMETHOD;
import static com.sun.gjc.util.SecurityUtils.getPasswordCredential;
import static com.sun.gjc.util.SecurityUtils.isPasswordCredentialEqual;
import static java.util.logging.Level.FINE;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.SEVERE;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.PrintWriter;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.Timer;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.security.auth.Subject;
import javax.sql.DataSource;
import javax.sql.PooledConnection;
import org.glassfish.api.jdbc.ConnectionValidation;
import org.glassfish.api.jdbc.SQLTraceListener;
import org.glassfish.external.probe.provider.PluginPoint;
import org.glassfish.external.probe.provider.StatsProviderManager;
import org.glassfish.resourcebase.resources.api.PoolInfo;
import com.sun.appserv.connectors.internal.spi.MCFLifecycleListener;
import com.sun.enterprise.util.i18n.StringManager;
import com.sun.gjc.common.DataSourceObjectBuilder;
import com.sun.gjc.common.DataSourceSpec;
import com.sun.gjc.monitoring.JdbcStatsProvider;
import com.sun.gjc.util.SQLTraceDelegator;
import com.sun.logging.LogDomains;
import jakarta.resource.ResourceException;
import jakarta.resource.spi.ConfigProperty;
import jakarta.resource.spi.ConnectionManager;
import jakarta.resource.spi.ConnectionRequestInfo;
import jakarta.resource.spi.LazyEnlistableConnectionManager;
import jakarta.resource.spi.ManagedConnection;
import jakarta.resource.spi.ManagedConnectionFactory;
import jakarta.resource.spi.ResourceAdapter;
import jakarta.resource.spi.ResourceAdapterAssociation;
import jakarta.resource.spi.ResourceAllocationException;
import jakarta.resource.spi.ValidatingManagedConnectionFactory;
import jakarta.resource.spi.security.PasswordCredential;
/**
* <code>ManagedConnectionFactory</code> implementation for Generic JDBC
* Connector. This class is extended by the DataSource specific
* <code>ManagedConnection</code> factories and the
* <code>ManagedConnectionFactory</code> for the <code>DriverManager</code>.
*
* @author Evani Sai Surya Kiran, Aditya Gore
* @version 1.0, 02/08/03
*/
public abstract class ManagedConnectionFactoryImpl
implements ManagedConnectionFactory, ValidatingManagedConnectionFactory, MCFLifecycleListener,
ResourceAdapterAssociation, Serializable, Externalizable {
private static Logger _logger = LogDomains.getLogger(ManagedConnectionFactoryImpl.class, LogDomains.RSR_LOGGER);
protected static final StringManager localStrings = StringManager.getManager(DataSourceObjectBuilder.class);
protected DataSourceSpec spec = new DataSourceSpec();
protected transient DataSourceObjectBuilder dataSourceObjectBuilder;
protected PrintWriter logWriter;
protected transient ResourceAdapter resourceAdapter;
protected boolean statementWrapping;
protected SQLTraceDelegator sqlTraceDelegator;
protected LazyEnlistableConnectionManager connectionManager;
protected boolean isLazyConnectionManager;
private JdbcObjectsFactory jdbcObjectsFactory = JdbcObjectsFactory.getInstance();
private int statementCacheSize;
private String statementCacheType;
private long statementLeakTimeout;
private boolean statementLeakReclaim;
// Jdbc Stats provider that is created
private JdbcStatsProvider jdbcStatsProvider;
/**
* Creates a Connection Factory instance. The <code>ConnectionManager</code>
* implementation of the resource adapter is used here.
*
* @return Generic JDBC Connector implementation of
* <code>javax.sql.DataSource</code>
*/
@Override
public Object createConnectionFactory() {
logFine("In createConnectionFactory()");
return jdbcObjectsFactory.getDataSourceInstance(this, null);
}
/**
* Creates a Connection Factory instance. The <code>ConnectionManager</code>
* implementation of the application server is used here.
*
* @param connectionManager <code>ConnectionManager</code> passed by the application
* server
* @return Generic JDBC Connector implementation of
* <code>javax.sql.DataSource</code>
*/
@Override
public Object createConnectionFactory(ConnectionManager connectionManager) {
logFine("In createConnectionFactory(jakarta.resource.spi.ConnectionManager cxManager)");
DataSource connectionFactory = jdbcObjectsFactory.getDataSourceInstance(this, connectionManager);
if (connectionManager instanceof LazyEnlistableConnectionManager) {
this.connectionManager = (LazyEnlistableConnectionManager) connectionManager;
isLazyConnectionManager = true;
}
return connectionFactory;
}
/**
* Creates a new physical connection to the underlying EIS resource manager.
*
* @param subject <code>Subject</code> instance passed by the application server
* @param cxRequestInfo <code>ConnectionRequestInfo</code> which may be created
* as a result of the invocation <code>getConnection(user, password)</code> on
* the <code>DataSource</code> object
*
* @return <code>ManagedConnection</code> object created
* @throws ResourceException if there is an error in instantiating the
* <code>DataSource</code> object used for the creation of the
* <code>ManagedConnection</code> object
* @throws SecurityException if there ino <code>PasswordCredential</code> object
* satisfying this request
* @throws ResourceException if there is an error in allocating the physical
* connection
*/
@Override
public abstract ManagedConnection createManagedConnection(Subject subject, ConnectionRequestInfo cxRequestInfo) throws ResourceException;
/**
* Check if this <code>ManagedConnectionFactoryImpl</code> is equal to another
* <code>ManagedConnectionFactoryImpl</code>.
*
* @param other <code>ManagedConnectionFactoryImpl</code> object for checking
* equality with
* @return true if the property sets of both the
* <code>ManagedConnectionFactoryImpl</code> objects are the same false
* otherwise
*/
@Override
public abstract boolean equals(Object other);
/**
* Get the log writer for this <code>ManagedConnectionFactoryImpl</code>
* instance.
*
* @return <code>PrintWriter</code> associated with this
* <code>ManagedConnectionFactoryImpl</code> instance
* @see <code>setLogWriter</code>
*/
@Override
public java.io.PrintWriter getLogWriter() {
return logWriter;
}
/**
* Get the <code>ResourceAdapterImpl</code> for this
* <code>ManagedConnectionFactoryImpl</code> instance.
*
* @return <code>ResourceAdapterImpl</code> associated with this
* <code>ManagedConnectionFactoryImpl</code> instance
* @see <code>setResourceAdapter</code>
*/
@Override
public jakarta.resource.spi.ResourceAdapter getResourceAdapter() {
logFine("In getResourceAdapter");
return resourceAdapter;
}
/**
* Returns the hash code for this <code>ManagedConnectionFactoryImpl</code>.
*
* @return hash code for this <code>ManagedConnectionFactoryImpl</code>
*/
@Override
public int hashCode() {
logFine("In hashCode");
return spec.hashCode();
}
/**
* Returns a matched <code>ManagedConnection</code> from the candidate set of
* <code>ManagedConnection</code> objects.
*
* @param connectionSet <code>Set</code> of <code>ManagedConnection</code>
* objects passed by the application server
* @param subject passed by the application server for retrieving information
* required for matching
* @param cxRequestInfo <code>ConnectionRequestInfo</code> passed by the
* application server for retrieving information required for matching
* @return <code>ManagedConnection</code> that is the best match satisfying this
* request
* @throws ResourceException if there is an error accessing the
* <code>Subject</code> parameter or the <code>Set</code> of
* <code>ManagedConnection</code> objects passed by the application server
*/
@Override
public ManagedConnection matchManagedConnections(Set connectionSet, Subject subject, ConnectionRequestInfo cxRequestInfo) throws ResourceException {
logFine("In matchManagedConnections");
if (connectionSet == null) {
return null;
}
PasswordCredential passwordCredential = getPasswordCredential(this, subject, cxRequestInfo);
Iterator<ManagedConnectionImpl> iter = connectionSet.iterator();
ManagedConnectionImpl managedConnectionImpl = null;
while (iter.hasNext()) {
try {
managedConnectionImpl = iter.next();
} catch (NoSuchElementException nsee) {
_logger.log(SEVERE, "jdbc.exc_iter");
throw new ResourceException(nsee.getMessage());
}
if (passwordCredential == null && this.equals(managedConnectionImpl.getManagedConnectionFactory())) {
return managedConnectionImpl;
}
if (isPasswordCredentialEqual(passwordCredential, managedConnectionImpl.getPasswordCredential())) {
return managedConnectionImpl;
}
}
return null;
}
/**
* This method returns a set of invalid <code>ManagedConnection</code> objects
* chosen from a specified set of <code>ManagedConnection</code> objects.
*
* @param connectionSet a set of <code>ManagedConnection</code> objects that
* need to be validated.
* @return a set of invalid <code>ManagedConnection</code> objects.
* @throws ResourceException generic exception.
*/
@Override
public Set getInvalidConnections(Set connectionSet) throws ResourceException {
Iterator iter = connectionSet.iterator();
Set<ManagedConnectionImpl> invalidConnections = new HashSet<ManagedConnectionImpl>();
while (iter.hasNext()) {
ManagedConnectionImpl managedConnectionImpl = (ManagedConnectionImpl) iter.next();
try {
isValid(managedConnectionImpl);
} catch (ResourceException re) {
invalidConnections.add(managedConnectionImpl);
managedConnectionImpl.connectionErrorOccurred(re, null);
_logger.log(FINE, "jdbc.invalid_connection", re);
}
}
return invalidConnections;
}
/**
* Checks if a <code>ManagedConnection</code> is to be validated or not and
* validates it or returns.
*
* @param managedConnectionImpl <code>ManagedConnection</code> to be validated
* @throws ResourceException if the connection is not valid or if validation
* method is not proper
*/
void isValid(ManagedConnectionImpl managedConnectionImpl) throws ResourceException {
if (managedConnectionImpl == null || managedConnectionImpl.isTransactionInProgress()) {
return;
}
String conVal = spec.getDetail(CONNECTIONVALIDATIONREQUIRED);
boolean connectionValidationRequired = (conVal == null) ?
false :
Boolean.valueOf(conVal.toLowerCase(Locale.getDefault()));
if (!connectionValidationRequired) {
return;
}
String validationMethod = spec.getDetail(VALIDATIONMETHOD).toLowerCase(Locale.getDefault());
managedConnectionImpl.checkIfValid();
/**
* The above call checks if the actual physical connection is usable or not.
*/
Connection connection = managedConnectionImpl.getActualConnection();
if (validationMethod.equals("custom-validation")) {
isValidByCustomValidation(connection, spec.getDetail(VALIDATIONCLASSNAME));
} else if (validationMethod.equals("auto-commit")) {
isValidByAutoCommit(connection);
} else if (validationMethod.equals("meta-data")) {
isValidByMetaData(connection);
} else if (validationMethod.equals("table")) {
isValidByTableQuery(connection, spec.getDetail(DataSourceSpec.VALIDATIONTABLENAME));
} else {
throw new ResourceException("The validation method is not proper");
}
}
/**
* Checks if a <code>java.sql.Connection</code> is valid or not by doing a
* custom validation using the validation class name specified.
*
* @param connection <code>java.sql.Connection</code> to be validated
* @throws ResourceException if the connection is not valid
*/
protected void isValidByCustomValidation(Connection connection, String validationClassName) throws ResourceException {
boolean isValid = false;
if (connection == null) {
throw new ResourceException("The connection is not valid as " + "the connection is null");
}
try {
ConnectionValidation connectionValidation = (ConnectionValidation)
Thread.currentThread()
.getContextClassLoader()
.loadClass(validationClassName)
.getDeclaredConstructor()
.newInstance();
isValid = connectionValidation.isConnectionValid(connection);
} catch (Exception e) {
_logger.log(INFO, "jdbc.exc_custom_validation", validationClassName);
throw new ResourceException(e);
}
if (!isValid) {
_logger.log(INFO, "jdbc.exc_custom_validation", validationClassName);
throw new ResourceException("Custom validation detected invalid connection");
}
}
/**
* Checks if a <code>java.sql.Connection</code> is valid or not by checking its
* auto commit property.
*
* @param connection <code>java.sql.Connection</code> to be validated
* @throws ResourceException if the connection is not valid
*/
protected void isValidByAutoCommit(Connection connection) throws ResourceException {
if (connection == null) {
throw new ResourceException("The connection is not valid as " + "the connection is null");
}
try {
// Notice that using something like
// dbCon.setAutoCommit(dbCon.getAutoCommit()) will cause problems with
// some drivers like sybase
// We do not validate connections that are already enlisted
// in a transaction
// We cycle autocommit to true and false to by-pass drivers that
// might cache the call to set autocomitt
// Also notice that some XA data sources will throw and exception if
// you try to call setAutoCommit, for them this method is not recommended
boolean autoCommit = connection.getAutoCommit();
if (autoCommit) {
connection.setAutoCommit(false);
} else {
connection.rollback(); // prevents uncompleted transaction exceptions
connection.setAutoCommit(true);
}
connection.setAutoCommit(autoCommit);
} catch (Exception sqle) {
_logger.log(INFO, "jdbc.exc_autocommit_validation");
throw new ResourceException(sqle);
}
}
/**
* Checks if a <code>java.sql.Connection</code> is valid or not by checking its
* meta data.
*
* @param connection <code>java.sql.Connection</code> to be validated
* @throws ResourceException if the connection is not valid
*/
protected void isValidByMetaData(Connection connection) throws ResourceException {
if (connection == null) {
throw new ResourceException("The connection is not valid as " + "the connection is null");
}
try {
connection.getMetaData();
} catch (Exception sqle) {
_logger.log(INFO, "jdbc.exc_metadata_validation");
throw new ResourceException(sqle);
}
}
/**
* Checks if a <code>java.sql.Connection</code> is valid or not by querying a
* table.
*
* @param connection <code>java.sql.Connection</code> to be validated
* @param tableName table which should be queried
* @throws ResourceException if the connection is not valid
*/
protected void isValidByTableQuery(Connection connection, String tableName) throws ResourceException {
if (connection == null) {
throw new ResourceException("The connection is not valid as " + "the connection is null");
}
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
preparedStatement = connection.prepareStatement("SELECT COUNT(*) FROM " + tableName);
resultSet = preparedStatement.executeQuery();
} catch (Exception sqle) {
_logger.log(INFO, "jdbc.exc_table_validation", tableName);
throw new ResourceException(sqle);
} finally {
try {
if (resultSet != null) {
resultSet.close();
}
} catch (Exception e1) {
}
try {
if (preparedStatement != null) {
preparedStatement.close();
}
} catch (Exception e2) {
}
}
}
/**
* Sets the isolation level specified in the <code>ConnectionRequestInfo</code>
* for the <code>ManagedConnection</code> passed.
*
* @param managedConnectionImpl <code>ManagedConnection</code>
* @throws ResourceException if the isolation property is invalid or if the
* isolation cannot be set over the connection
*/
protected void setIsolation(ManagedConnectionImpl managedConnectionImpl) throws ResourceException {
Connection connection = managedConnectionImpl.getActualConnection();
if (connection == null) {
return;
}
String tranIsolation = spec.getDetail(TRANSACTIONISOLATION);
if (tranIsolation != null && !tranIsolation.equals("")) {
int tranIsolationInt = getTransactionIsolationInt(tranIsolation);
try {
connection.setTransactionIsolation(tranIsolationInt);
managedConnectionImpl.setLastTransactionIsolationLevel(tranIsolationInt);
} catch (SQLException sqle) {
_logger.log(SEVERE, "jdbc.exc_tx_iso", sqle);
throw new ResourceException("The transaction isolation could " + "not be set: " + sqle.getMessage());
}
}
}
/**
* Resets the isolation level for the <code>ManagedConnection</code> passed. If
* the transaction level is to be guaranteed to be the same as the one present
* when this <code>ManagedConnection</code> was created, as specified by the
* <code>ConnectionRequestInfo</code> passed, it sets the transaction isolation
* level from the <code>ConnectionRequestInfo</code> passed. Else, it sets it to
* the transaction isolation passed.
*
* @param managedConnectionImpl <code>ManagedConnection</code>
* @param tranIsol int
* @throws ResourceException if the isolation property is invalid or if the
* isolation cannot be set over the connection
*/
void resetIsolation(ManagedConnectionImpl managedConnectionImpl, int tranIsol) throws ResourceException {
Connection connection = managedConnectionImpl.getActualConnection();
if (connection == null) {
return;
}
String transactionIsolation = spec.getDetail(TRANSACTIONISOLATION);
if (transactionIsolation != null && !transactionIsolation.equals("")) {
String guaranteeIsolationLevel = spec.getDetail(GUARANTEEISOLATIONLEVEL);
if (guaranteeIsolationLevel != null && !guaranteeIsolationLevel.equals("")) {
boolean guarantee = Boolean.valueOf(guaranteeIsolationLevel.toLowerCase(Locale.getDefault()));
if (guarantee) {
int tranIsolationInt = getTransactionIsolationInt(transactionIsolation);
try {
if (tranIsolationInt != connection.getTransactionIsolation()) {
connection.setTransactionIsolation(tranIsolationInt);
}
} catch (SQLException sqle) {
_logger.log(SEVERE, "jdbc.exc_tx_iso", sqle);
throw new ResourceException("The isolation level could not be set: " + sqle.getMessage());
}
} else {
try {
if (tranIsol != connection.getTransactionIsolation()) {
connection.setTransactionIsolation(tranIsol);
}
} catch (SQLException sqle) {
_logger.log(SEVERE, "jdbc.exc_tx_iso", sqle);
throw new ResourceException("The isolation level could not be set: " + sqle.getMessage());
}
}
}
}
}
private void detectSqlTraceListeners() {
// Check for sql-trace-listeners attribute.
String sqlTraceListeners = getSqlTraceListeners();
String delimiter = ",";
if (sqlTraceListeners != null && !sqlTraceListeners.equals("null")) {
sqlTraceDelegator = new SQLTraceDelegator(getPoolName(), getApplicationName(), getModuleName());
StringTokenizer st = new StringTokenizer(sqlTraceListeners, delimiter);
while (st.hasMoreTokens()) {
String sqlTraceListener = st.nextToken().trim();
if (!sqlTraceListener.equals("")) {
Class listenerClass = null;
SQLTraceListener listener = null;
Constructor[] constructors = null;
Class[] parameterTypes = null;
Object[] initargs = null;
// Load the listener class
try {
listenerClass = Thread.currentThread().getContextClassLoader().loadClass(sqlTraceListener);
} catch (ClassNotFoundException ex) {
_logger.log(SEVERE, "jdbc.sql_trace_listener_cnfe", sqlTraceListener);
}
Class intf[] = listenerClass.getInterfaces();
for (int i = 0; i < intf.length; i++) {
Class interfaceName = intf[i];
if (interfaceName.getName().equals("org.glassfish.api.jdbc.SQLTraceListener")) {
try {
constructors = listenerClass.getConstructors();
for (Constructor constructor : constructors) {
parameterTypes = constructor.getParameterTypes();
// For now only the no argument constructors are allowed.
// TODO should this be documented?
if (parameterTypes.length == 0) {
listener = (SQLTraceListener) constructor.newInstance(initargs);
}
}
} catch (InstantiationException ex) {
_logger.log(SEVERE, "jdbc.sql_trace_listener_exception", ex.getMessage());
} catch (IllegalAccessException ex) {
_logger.log(SEVERE, "jdbc.sql_trace_listener_exception", ex.getMessage());
} catch (IllegalArgumentException ex) {
_logger.log(SEVERE, "jdbc.sql_trace_listener_exception", ex.getMessage());
} catch (InvocationTargetException ex) {
_logger.log(SEVERE, "jdbc.sql_trace_listener_exception", ex.getMessage());
} catch (SecurityException ex) {
_logger.log(SEVERE, "jdbc.sql_trace_listener_exception", ex.getMessage());
}
sqlTraceDelegator.registerSQLTraceListener(listener);
}
}
}
}
}
}
/**
* Gets the integer equivalent of the string specifying the transaction
* isolation.
*
* @param tranIsolation string specifying the isolation level
* @return tranIsolationInt the <code>java.sql.Connection</code> constant for
* the string specifying the isolation.
*/
private int getTransactionIsolationInt(String tranIsolation) throws ResourceException {
if (tranIsolation.equalsIgnoreCase("read-uncommitted")) {
return Connection.TRANSACTION_READ_UNCOMMITTED;
}
if (tranIsolation.equalsIgnoreCase("read-committed")) {
return Connection.TRANSACTION_READ_COMMITTED;
}
if (tranIsolation.equalsIgnoreCase("repeatable-read")) {
return Connection.TRANSACTION_REPEATABLE_READ;
}
if (tranIsolation.equalsIgnoreCase("serializable")) {
return Connection.TRANSACTION_SERIALIZABLE;
}
throw new ResourceException(
"Invalid transaction isolation; the transaction " +
"isolation level can be empty or any of the following: " +
"read-uncommitted, read-committed, repeatable-read, serializable");
}
/**
* Common operation performed by all the child MCFs before returning a created
* mc
*/
protected void validateAndSetIsolation(ManagedConnectionImpl managedConnectionImpl) throws ResourceException {
try {
isValid(managedConnectionImpl);
setIsolation(managedConnectionImpl);
} catch (ResourceException e) {
if (managedConnectionImpl != null) {
try {
managedConnectionImpl.destroy();
} catch (ResourceException e1) {
_logger.log(Level.WARNING, "jdbc.exc_destroy", e1);
}
}
throw new ResourceAllocationException(localStrings.getString("jdbc.exc_destroy", e.getMessage()), e);
}
}
private void detectStatementCachingSupport() {
String cacheSize = getStatementCacheSize();
if (cacheSize != null) {
try {
statementCacheSize = Integer.parseInt(cacheSize);
// TODO-SC FINE log-level with Pool Name (if possible)
if (_logger.isLoggable(FINE)) {
_logger.log(FINE, "StatementCaching Size : " + statementCacheSize);
}
} catch (NumberFormatException nfe) {
if (_logger.isLoggable(FINE)) {
_logger.fine("Exception while setting StatementCacheSize : " + nfe.getMessage());
}
// ignore
}
}
}
/**
* Set the log writer for this <code>ManagedConnectionFactoryImpl</code>
* instance.
*
* @param out <code>PrintWriter</code> passed by the application server
* @see <code>getLogWriter</code>
*/
@Override
public void setLogWriter(java.io.PrintWriter out) {
logWriter = out;
}
/**
* Set the associated <code>ResourceAdapterImpl</code> JavaBean.
*
* @param ra <code>ResourceAdapterImpl</code> associated with this
* <code>ManagedConnectionFactoryImpl</code> instance
* @see <code>getResourceAdapter</code>
*/
@Override
public void setResourceAdapter(jakarta.resource.spi.ResourceAdapter ra) {
this.resourceAdapter = ra;
}
/**
* Sets the user name
*
* @param user <code>String</code>
*/
@ConfigProperty(type = String.class, defaultValue = "APP")
public void setUser(String user) {
spec.setDetail(DataSourceSpec.USERNAME, user);
}
/**
* Gets the user name
*
* @return user
*/
public String getUser() {
return spec.getDetail(DataSourceSpec.USERNAME);
}
/**
* Sets the password
*
* @param passwd <code>String</code>
*/
@ConfigProperty(type = String.class, defaultValue = "APP")
public void setPassword(String passwd) {
spec.setDetail(DataSourceSpec.PASSWORD, passwd);
}
/**
* Gets the password
*
* @return passwd
*/
public String getPassword() {
return spec.getDetail(DataSourceSpec.PASSWORD);
}
/**
* Sets the class name of the data source
*
* @param className <code>String</code>
*/
@ConfigProperty(type = String.class, defaultValue = "org.apache.derby.jdbc.ClientConnectionPoolDataSource")
public void setClassName(String className) {
spec.setDetail(DataSourceSpec.CLASSNAME, className);
}
/**
* Gets the class name of the data source
*
* @return className
*/
public String getClassName() {
return spec.getDetail(DataSourceSpec.CLASSNAME);
}
/**
* Sets if connection validation is required or not
*
* @param conVldReq <code>String</code>
*/
@ConfigProperty(type = String.class, defaultValue = "false")
public void setConnectionValidationRequired(String conVldReq) {
spec.setDetail(DataSourceSpec.CONNECTIONVALIDATIONREQUIRED, conVldReq);
}
/**
* Returns if connection validation is required or not
*
* @return connection validation requirement
*/
public String getConnectionValidationRequired() {
return spec.getDetail(DataSourceSpec.CONNECTIONVALIDATIONREQUIRED);
}
/**
* Sets the validation method required
*
* @param validationMethod <code>String</code>
*/
@ConfigProperty(type = String.class, defaultValue = "")
public void setValidationMethod(String validationMethod) {
spec.setDetail(VALIDATIONMETHOD, validationMethod);
}
/**
* Returns the connection validation method type
*
* @return validation method
*/
public String getValidationMethod() {
return spec.getDetail(VALIDATIONMETHOD);
}
/**
* Sets the table checked for during validation
*
* @param table <code>String</code>
*/
@ConfigProperty(type = String.class, defaultValue = "")
public void setValidationTableName(String table) {
spec.setDetail(DataSourceSpec.VALIDATIONTABLENAME, table);
}
/**
* Returns the table checked for during validation
*
* @return table
*/
public String getValidationTableName() {
return spec.getDetail(DataSourceSpec.VALIDATIONTABLENAME);
}
/**
* Sets the validation class name checked for during validation
*
* @param className <code>String</code>
*/
public void setValidationClassName(String className) {
try {
Class validationClass = Thread.currentThread().getContextClassLoader().loadClass(className);
boolean isAssignable = ConnectionValidation.class.isAssignableFrom(validationClass);
if (isAssignable) {
spec.setDetail(VALIDATIONCLASSNAME, className);
} else {
// Validation Failed
_logger.log(SEVERE, "jdbc.set_custom_validation_class_name_failure", className);
throw new ResourceException("The Custom validation class name is "
+ "not valid as it does not implement " + ConnectionValidation.class.getName());
}
} catch (ResourceException ex) {
_logger.log(SEVERE, "jdbc.set_custom_validation_class_name_failure", ex.getMessage());
} catch (ClassNotFoundException ex) {
_logger.log(SEVERE, "jdbc.set_custom_validation_class_name_failure", ex.getMessage());
}
}
/**
* Returns the validation class name checked for during validation
*
* @return table
*/
public String getValidationClassName() {
return spec.getDetail(VALIDATIONCLASSNAME);
}
/**
* Sets the transaction isolation level
*
* @param trnIsolation <code>String</code>
*/
@ConfigProperty(type = String.class, defaultValue = "")
public void setTransactionIsolation(String trnIsolation) {
spec.setDetail(TRANSACTIONISOLATION, trnIsolation);
}
/**
* Returns the transaction isolation level
*
* @return transaction isolation level
*/
public String getTransactionIsolation() {
return spec.getDetail(TRANSACTIONISOLATION);
}
/**
* Sets if the transaction isolation level is to be guaranteed
*
* @param guaranteeIsolation <code>String</code>
*/
@ConfigProperty(type = String.class, defaultValue = "")
public void setGuaranteeIsolationLevel(String guaranteeIsolation) {
spec.setDetail(GUARANTEEISOLATIONLEVEL, guaranteeIsolation);
}
/**
* Returns the transaction isolation level
*
* @return isolation level guarantee
*/
public String getGuaranteeIsolationLevel() {
return spec.getDetail(GUARANTEEISOLATIONLEVEL);
}
protected boolean isEqual(PasswordCredential pc, String user, String password) {
// if equal get direct connection else
// get connection with user and password.
String thisUser = (pc == null) ? null : pc.getUserName();
char[] passwordArray = (pc == null) ? null : pc.getPassword();
char[] tmpPasswordArray = (password == null) ? null : password.toCharArray();
return (isStringEqual(thisUser, user) && Arrays.equals(passwordArray, tmpPasswordArray));
}
private boolean isStringEqual(String str1, String str2) {
return str1 == null ? str2 == null : str1.equals(str2);
}
/**
* Sets the server name.
*
* @param serverName <code>String</code>
* @see <code>getServerName</code>
*/
@ConfigProperty(type = String.class, defaultValue = "localhost")
public void setServerName(String serverName) {
spec.setDetail(DataSourceSpec.SERVERNAME, serverName);
}
/**
* Gets the server name.
*
* @return serverName
* @see <code>setServerName</code>
*/
public String getServerName() {
return spec.getDetail(DataSourceSpec.SERVERNAME);
}
/**
* Sets the port number.
*
* @param portNumber <code>String</code>
* @see <code>getPortNumber</code>
*/
@ConfigProperty(type = String.class, defaultValue = "1527")
public void setPortNumber(String portNumber) {
spec.setDetail(DataSourceSpec.PORTNUMBER, portNumber);
}
/**
* Gets the port number.
*
* @return portNumber
* @see <code>setPortNumber</code>
*/
public String getPortNumber() {
return spec.getDetail(DataSourceSpec.PORTNUMBER);
}
public void setJdbc30DataSource(String booleanValue) {
spec.setDetail(DataSourceSpec.JDBC30DATASOURCE, booleanValue);
}
public String getJdbc30DataSource() {
return spec.getDetail(DataSourceSpec.JDBC30DATASOURCE);
}
/**
* Sets the database name.
*
* @param databaseName <code>String</code>
* @see <code>getDatabaseName</code>
*/
@ConfigProperty(type = String.class, defaultValue = "testdb")
public void setDatabaseName(String databaseName) {
spec.setDetail(DataSourceSpec.DATABASENAME, databaseName);
}
/**
* Gets the database name.
*
* @return databaseName
* @see <code>setDatabaseName</code>
*/
public String getDatabaseName() {
return spec.getDetail(DataSourceSpec.DATABASENAME);
}
/**
* Sets the data source name.
*
* @param dsn <code>String</code>
* @see <code>getDataSourceName</code>
*/
@ConfigProperty(type = String.class, defaultValue = "")
public void setDataSourceName(String dsn) {
spec.setDetail(DataSourceSpec.DATASOURCENAME, dsn);
}
/**
* Gets the data source name.
*
* @return dsn
* @see <code>setDataSourceName</code>
*/
public String getDataSourceName() {
return spec.getDetail(DataSourceSpec.DATASOURCENAME);
}
/**
* Set Statement Wrapping value
*
* @param wrapping <code>String</code>
* @see <code> getStatementWrapping </code>
*/
public void setStatementWrapping(String wrapping) {
spec.setDetail(DataSourceSpec.STATEMENTWRAPPING, wrapping);
computeStatementWrappingStatus();
}
/**
* Gets the statement wrapping value
*
* @return String representing "true" or "false"
* @see <code>setStatementWrapping</code>
*/
public String getStatementWrapping() {
return spec.getDetail(DataSourceSpec.STATEMENTWRAPPING);
}
public void setStatementCacheSize(String value) {
spec.setDetail(DataSourceSpec.STATEMENTCACHESIZE, value);
detectStatementCachingSupport();
}
public String getStatementCacheSize() {
return spec.getDetail(DataSourceSpec.STATEMENTCACHESIZE);
}
public void setStatementLeakTimeoutInSeconds(String value) {
spec.setDetail(DataSourceSpec.STATEMENTLEAKTIMEOUTINSECONDS, value);
detectStatementLeakSupport();
}
public String getStatementLeakTimeoutInSeconds() {
return spec.getDetail(DataSourceSpec.STATEMENTLEAKTIMEOUTINSECONDS);
}
public void setStatementLeakReclaim(String value) {
spec.setDetail(DataSourceSpec.STATEMENTLEAKRECLAIM, value);
}
public String getStatementLeakReclaim() {
return spec.getDetail(DataSourceSpec.STATEMENTLEAKRECLAIM);
}
public void setPoolMonitoringSubTreeRoot(String value) {
spec.setDetail(DataSourceSpec.POOLMONITORINGSUBTREEROOT, value);
}
public String getPoolMonitoringSubTreeRoot() {
return spec.getDetail(DataSourceSpec.POOLMONITORINGSUBTREEROOT);
}
public String getApplicationName() {
return spec.getDetail(DataSourceSpec.APPLICATIONNAME);
}
public void setApplicationName(String value) {
spec.setDetail(DataSourceSpec.APPLICATIONNAME, value);
}
public String getModuleName() {
return spec.getDetail(DataSourceSpec.MODULENAME);
}
public void setModuleName(String value) {
spec.setDetail(DataSourceSpec.MODULENAME, value);
}
public String getPoolName() {
return spec.getDetail(DataSourceSpec.POOLNAME);
}
public void setPoolName(String value) {
spec.setDetail(DataSourceSpec.POOLNAME, value);
}
public String getStatementCacheType() {
return spec.getDetail(DataSourceSpec.STATEMENTCACHETYPE);
}
public void setStatementCacheType(String statementCacheType) {
spec.setDetail(DataSourceSpec.STATEMENTCACHETYPE, statementCacheType);
this.statementCacheType = getStatementCacheType();
if (this.statementCacheType == null || this.statementCacheType.trim().equals("")) {
if (_logger.isLoggable(FINE)) {
_logger.fine(
" Default StatementCaching Type : " +
localStrings.getString("jdbc.statement-cache.default.datastructure"));
}
} else {
if (_logger.isLoggable(FINE)) {
_logger.fine("StatementCaching Type : " + this.statementCacheType);
}
}
}
public String getNumberOfTopQueriesToReport() {
return spec.getDetail(DataSourceSpec.NUMBEROFTOPQUERIESTOREPORT);
}
public void setNumberOfTopQueriesToReport(String numTopQueriesToReport) {
spec.setDetail(DataSourceSpec.NUMBEROFTOPQUERIESTOREPORT, numTopQueriesToReport);
}
public String getTimeToKeepQueriesInMinutes() {
return spec.getDetail(DataSourceSpec.TIMETOKEEPQUERIESINMINUTES);
}
public void setTimeToKeepQueriesInMinutes(String timeToKeepQueries) {
spec.setDetail(DataSourceSpec.TIMETOKEEPQUERIESINMINUTES, timeToKeepQueries);
}
public String getInitSql() {
return spec.getDetail(DataSourceSpec.INITSQL);
}
public void setInitSql(String initSql) {
// TODO remove case where "null" is checked. Might be a CLI/GUI bug.
if (initSql != null && !initSql.equalsIgnoreCase("null") && !initSql.equals("")) {
spec.setDetail(DataSourceSpec.INITSQL, initSql);
}
}
/**
* Set StatementTimeout value
*
* @param timeout <code>String</code>
* @see <code> getStatementTimeout </code>
*/
public void setStatementTimeout(String timeout) {
spec.setDetail(DataSourceSpec.STATEMENTTIMEOUT, timeout);
}
/**
* Gets the StatementTimeout value
*
* @return String representing "true" or "false"
* @see <code>setStatementTimeout</code>
*/
public String getStatementTimeout() {
return spec.getDetail(DataSourceSpec.STATEMENTTIMEOUT);
}
public String getSqlTraceListeners() {
return spec.getDetail(DataSourceSpec.SQLTRACELISTENERS);
}
public void setSqlTraceListeners(String sqlTraceListeners) {
if (sqlTraceListeners != null) {
spec.setDetail(DataSourceSpec.SQLTRACELISTENERS, sqlTraceListeners);
detectSqlTraceListeners();
}
}
/**
* Sets the description.
*
* @param desc <code>String</code>
* @see <code>getDescription</code>
*/
@ConfigProperty(type = String.class, defaultValue = "Derby driver for datasource")
public void setDescription(String desc) {
spec.setDetail(DataSourceSpec.DESCRIPTION, desc);
}
/**
* Gets the description.
*
* @return desc
* @see <code>setDescription</code>
*/
public String getDescription() {
return spec.getDetail(DataSourceSpec.DESCRIPTION);
}
/**
* Sets the network protocol.
*
* @param nwProtocol <code>String</code>
* @see <code>getNetworkProtocol</code>
*/
@ConfigProperty(type = String.class, defaultValue = "")
public void setNetworkProtocol(String nwProtocol) {
spec.setDetail(DataSourceSpec.NETWORKPROTOCOL, nwProtocol);
}
/**
* Gets the network protocol.
*
* @return nwProtocol
* @see <code>setNetworkProtocol</code>
*/
public String getNetworkProtocol() {
return spec.getDetail(DataSourceSpec.NETWORKPROTOCOL);
}
/**
* Sets the role name.
*
* @param roleName <code>String</code>
* @see <code>getRoleName</code>
*/
@ConfigProperty(type = String.class, defaultValue = "")
public void setRoleName(String roleName) {
spec.setDetail(DataSourceSpec.ROLENAME, roleName);
}
/**
* Gets the role name.
*
* @return roleName
* @see <code>setRoleName</code>
*/
public String getRoleName() {
return spec.getDetail(DataSourceSpec.ROLENAME);
}
/**
* Sets the login timeout.
*
* @param loginTimeOut <code>String</code>
* @see <code>getLoginTimeOut</code>
*/
@ConfigProperty(type = String.class, defaultValue = "0")
public void setLoginTimeOut(String loginTimeOut) {
spec.setDetail(DataSourceSpec.LOGINTIMEOUT, loginTimeOut);
}
/**
* Gets the login timeout.
*
* @return loginTimeout
* @see <code>setLoginTimeOut</code>
*/
public String getLoginTimeOut() {
return spec.getDetail(DataSourceSpec.LOGINTIMEOUT);
}
/**
* Sets the delimiter.
*
* @param delim <code>String</code>
* @see <code>getDelimiter</code>
*/
@ConfigProperty(type = String.class, defaultValue = "#")
public void setDelimiter(String delim) {
spec.setDetail(DataSourceSpec.DELIMITER, delim);
}
/**
* Gets the delimiter.
*
* @return delim
* @see <code>setDelimiter</code>
*/
public String getDelimiter() {
return spec.getDetail(DataSourceSpec.DELIMITER);
}
public void setEscapeCharacter(String escapeCharacter) {
spec.setDetail(DataSourceSpec.ESCAPECHARACTER, escapeCharacter);
}
public String getEscapeCharacter() {
return spec.getDetail(DataSourceSpec.ESCAPECHARACTER);
}
/**
* Sets the driver specific properties.
*
* @param driverProps <code>String</code>
* @see <code>getDriverProperties</code>
*/
@ConfigProperty(type = String.class, defaultValue = "")
public void setDriverProperties(String driverProps) {
spec.setDetail(DataSourceSpec.DRIVERPROPERTIES, driverProps);
}
/**
* Gets the driver specific properties.
*
* @return driverProps
* @see <code>setDriverProperties</code>
*/
public String getDriverProperties() {
return spec.getDetail(DataSourceSpec.DRIVERPROPERTIES);
}
protected PoolInfo getPoolInfo() {
return new PoolInfo(getPoolName(), getApplicationName(), getModuleName());
}
protected ManagedConnectionImpl constructManagedConnection(PooledConnection pc, Connection sqlCon,
PasswordCredential passCred, ManagedConnectionFactoryImpl mcf) throws ResourceException {
return new ManagedConnectionImpl(pc, sqlCon, passCred, mcf, getPoolInfo(), statementCacheSize,
statementCacheType, sqlTraceDelegator, statementLeakTimeout, statementLeakReclaim);
}
/**
* Returns the underlying datasource
*
* @return DataSource of jdbc vendor
* @throws ResourceException
*/
public Object getDataSource() throws ResourceException {
if (dataSourceObjectBuilder == null) {
dataSourceObjectBuilder = new DataSourceObjectBuilder(spec);
}
return dataSourceObjectBuilder.constructDataSourceObject();
}
protected static final int JVM_OPTION_STATEMENT_WRAPPING_ON = 1;
protected static final int JVM_OPTION_STATEMENT_WRAPPING_OFF = 0;
protected static final int JVM_OPTION_STATEMENT_WRAPPING_NOT_SET = -1;
/**
* This wrapStatement flag is used to enable disable wrapping the statement
* objects. This can be enabled by passing jvm option,
* com.sun.appserv.jdbc.wrapstatement = true. This can be disabled by removing
* this option or by passing non true value. By default this will be disabled
*/
private static int wrapStatement = JVM_OPTION_STATEMENT_WRAPPING_NOT_SET;
static {
wrapStatement = getStatementWrappingJVMOption();
}
/**
* Gets the Statement Wrapping JVM Option (available in 8.2)<br>
* Which will be deprecated in future versions.
*
* @return int representing the JVM Option value of statement wrapping.
*/
private static int getStatementWrappingJVMOption() {
int result = JVM_OPTION_STATEMENT_WRAPPING_NOT_SET;
String str = System.getProperty("com.sun.appserv.jdbc.wrapJdbcObjects");
if ("true".equalsIgnoreCase(str)) {
result = JVM_OPTION_STATEMENT_WRAPPING_ON;
} else if ("false".equalsIgnoreCase(str)) {
result = JVM_OPTION_STATEMENT_WRAPPING_OFF;
}
return result;
}
/**
* 9.1 has attribute "wrap-statements" which will be overridden when JVM option
* is specified as "true" or "false" JVM Option will be deprecated in future
* versions.
*/
protected void computeStatementWrappingStatus() {
boolean poolProperty = false;
String statementWrappingString = getStatementWrapping();
if (statementWrappingString != null)
poolProperty = Boolean.valueOf(statementWrappingString);
if (wrapStatement == JVM_OPTION_STATEMENT_WRAPPING_ON
|| (wrapStatement == JVM_OPTION_STATEMENT_WRAPPING_NOT_SET && poolProperty)) {
statementWrapping = true;
} else {
statementWrapping = false;
}
}
/**
* Returns whether statement wrapping is enabled or not.<br>
*
* @return boolean representing statementwrapping status
*/
public boolean isStatementWrappingEnabled() {
return statementWrapping;
}
public JdbcObjectsFactory getJdbcObjectsFactory() {
return jdbcObjectsFactory;
}
protected void logFine(String logMessage) {
_logger.log(FINE, logMessage);
}
@Override
public void mcfCreated() {
String poolMonitoringSubTreeRoot = getPoolMonitoringSubTreeRoot();
String sqlTraceListeners = getSqlTraceListeners();
// Default values used in case sql tracing is OFF
int sqlTraceCacheSize = 0;
long timeToKeepQueries = 0;
if (sqlTraceListeners != null && !sqlTraceListeners.equals("null")) {
if (getNumberOfTopQueriesToReport() != null && !getNumberOfTopQueriesToReport().equals("null")) {
// Some value is set for this property
sqlTraceCacheSize = Integer.parseInt(getNumberOfTopQueriesToReport());
} else {
// No property by this name. default to 10 queries
sqlTraceCacheSize = 10;
}
if (getTimeToKeepQueriesInMinutes() != null && !getTimeToKeepQueriesInMinutes().equals("null")) {
// Time-To-Keep-Queries property has been set
timeToKeepQueries = Integer.parseInt(getTimeToKeepQueriesInMinutes());
} else {
// Default to 5 minutes after which cache is pruned.
timeToKeepQueries = 5;
// Time to keep queries is set to 5 minutes because the timer
// task will keep running till mcf is destroyed even if
// monitoring is turned OFF.
}
}
_logger.finest("MCF Created");
if (statementCacheSize > 0 || (sqlTraceListeners != null && !sqlTraceListeners.equals("null"))
|| statementLeakTimeout > 0) {
jdbcStatsProvider = new JdbcStatsProvider(getPoolName(), getApplicationName(), getModuleName(),
sqlTraceCacheSize, timeToKeepQueries);
// Get the poolname and use it to initialize the stats provider n register
StatsProviderManager.register("jdbc-connection-pool", PluginPoint.SERVER, poolMonitoringSubTreeRoot,
jdbcStatsProvider);
if (jdbcStatsProvider.getSqlTraceCache() != null) {
_logger.finest("Scheduling timer task for sql trace caching");
Timer timer = ((ResourceAdapterImpl) resourceAdapter).getTimer();
jdbcStatsProvider.getSqlTraceCache().scheduleTimerTask(timer);
}
_logger.finest("Registered JDBCRA Stats Provider");
}
}
@Override
public void mcfDestroyed() {
_logger.finest("MCF Destroyed");
if (jdbcStatsProvider != null) {
if (jdbcStatsProvider.getSqlTraceCache() != null) {
_logger.finest("Canceling timer task for sql trace caching");
jdbcStatsProvider.getSqlTraceCache().cancelTimerTask();
}
StatsProviderManager.unregister(jdbcStatsProvider);
jdbcStatsProvider = null;
_logger.finest("Unregistered JDBCRA Stats Provider");
}
}
private void detectStatementLeakSupport() {
String stmtLeakTimeout = getStatementLeakTimeoutInSeconds();
String stmtLeakReclaim = getStatementLeakReclaim();
if (stmtLeakTimeout != null) {
statementLeakTimeout = Integer.parseInt(stmtLeakTimeout) * 1000L;
statementLeakReclaim = Boolean.valueOf(stmtLeakReclaim);
if (_logger.isLoggable(FINE)) {
_logger.log(FINE, "StatementLeakTimeout in seconds: " + statementLeakTimeout
+ " & StatementLeakReclaim: " + statementLeakReclaim + " for pool : " + getPoolInfo());
}
}
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
resourceAdapter = ResourceAdapterImpl.getInstance();
}
}