org.firebirdsql.pool.AbstractFBConnectionPoolDataSource Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jaybird-jdk15 Show documentation
Show all versions of jaybird-jdk15 Show documentation
JDBC Driver for the Firebird RDBMS
The newest version!
/*
* Firebird Open Source J2ee connector - jdbc driver
*
* Distributable under LGPL license.
* You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* LGPL License for more details.
*
* This file was created by members of the firebird development team.
* All individual contributions remain the Copyright (C) of those
* individuals. Contributors to this file are either listed here or
* can be obtained from a CVS history command.
*
* All rights reserved.
*/
package org.firebirdsql.pool;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.sql.SQLException;
import java.util.*;
import javax.naming.*;
import javax.resource.ResourceException;
import javax.sql.*;
import org.firebirdsql.gds.DatabaseParameterBuffer;
import org.firebirdsql.gds.TransactionParameterBuffer;
import org.firebirdsql.gds.impl.GDSFactory;
import org.firebirdsql.gds.impl.GDSType;
import org.firebirdsql.jca.*;
import org.firebirdsql.jdbc.*;
import org.firebirdsql.logging.Logger;
import org.firebirdsql.logging.LoggerFactory;
/**
* Connection pool for Firebird JDBC driver.
*
* Following properties are supported:
*
* blobBufferSize
* size of the buffer used to transfer BLOB data.
*
* blockingTimeout
* time in milliseconds during which {@link #getPooledConnection()} method will
* block if no free connection is in pool.
*
* charSet
* similar to encoding
, but takes Java character set name
* instead of Firebird's encoding.
*
* database
* path to a database including the server name; for example
* localhost/3050:c:/path/to/database.gdb
.
*
* encoding
* character encoding for the JDBC connection.
*
* freeSize
* read-only: gives amount of free connections in the pool, when 0, blocking
* will occur if workingSize
is equal to maxPoolSize
.
*
* isolation
* default transaction isolation level for connections as string; possible
* values are:
*
* - TRANSACTION_READ_COMMITTED
*
- TRANSACTION_REPEATABLE_READ
*
- TRANSACTION_SERIALIZABLE
*
*
* loginTimeout
* property from {@link javax.sql.DataSource}, in this context is a synonym
* for blockingTimeout
(however value is specified in seconds).
*
* maxIdleTime
* time in milliseconds after which idle physical connection in the
* pool is closed.
*
* maxStatements
* maximum number of pooled prepared statements, if 0, pooling is switched
* off.
*
* maxPoolSize
* maximum number of physical connections that can be opened by this data
* source.
*
* minPoolSize
* minimum number of connections that will remain open by this data source.
*
* nonStandardProperty
* a non-standard connection parameter in form name[=value]
.
*
* password
* password that is used to connect to database.
*
* pingInterval
* time interval during which connection will be proved for aliveness.
*
* pooling
* allows switching pooling off.
*
* statementPooling
* alternative way to switch statement pooling off.
*
* socketBufferSize
* size of the socket buffer in bytes. In some cases values used by JVM by
* default are not optimal. This results in performance degradation
* (especially when you transfer big BLOBs). Usually 8192 bytes provides
* good results.
*
* sqlRole
* SQL role name.
*
* tpbMapping
* mapping of the TPB parameters to JDBC transaction isolation levels.
*
* transactionIsolationLevel
* default transaction isolation level, number from {@link java.sql.Connection}
* interface.
*
* totalSize
* total number of allocated connections.
*
* type
* type of connection that will be created. There are four possible types:
* pure Java (or type 4), type 2 that will use Firebird client library to
* connect to the database, local-mode type 2 driver, and embedded that
* will use embedded engine (access to local databases). Possible values
* are (case insensitive):
*
* "PURE_JAVA"
or "TYPE4"
* for pure Java (type 4) JDBC connections;
*
* "NATIVE"
or "TYPE2"
* to use Firebird client library;
*
* "LOCAL"
* to use Firebird client library in local-mode (IPC link to server);
*
* "EMBEDDED"
* to use embedded engine.
*
*
* userName
* name of the user that will be used to access the database.
*
* workingSize
* number of connections that are in use (e.g. were obtained using
* {@link #getPooledConnection()} method, but not yet closed).
*
*
* @author Roman Rokytskyy
*/
abstract public class AbstractFBConnectionPoolDataSource extends BasicAbstractConnectionPool
implements ConnectionPoolDataSource, XADataSource, PooledConnectionEventListener, FirebirdPool {
public static final AbstractConnectionPool.UserPasswordPair
EMPTY_USER_PASSWORD = new AbstractConnectionPool.UserPasswordPair();
private static final String PING_STATEMENT = "SELECT cast(1 AS INTEGER) FROM rdb$database";
private static final Logger LOG = LoggerFactory.getLogger(AbstractFBConnectionPoolDataSource.class, false);
private transient PrintWriter logWriter;
private transient volatile FBManagedConnectionFactory mcf;
private FBConnectionProperties connectionProperties = new FBConnectionProperties();
/**
* Get connection properties. This method returns a live object where
* connection properties can be set. Usually application does not need to
* call this method, since this class implements appropriate interface and
* all properties can set directly. However, this is needed for custom
* serialization.
*
* @return "live" instance of {@link FBConnectionProperties}.
*/
public FBConnectionProperties getConnectionProperties() {
return connectionProperties;
}
/**
* Set the connection properties in bulk. This method replaces the instance
* created in constructor, therefore, if the managed connection factory was
* already initialized with that instance, change will not be visible to it.
* In this case appropriate exception is thrown.
*
* @param props instance of {@link FBConnectionProperties}.
*
* @throws IllegalStateException if managed connection factory is already
* initialized.
*/
public void setConnectionProperties(FBConnectionProperties props) {
if (props == null)
throw new NullPointerException();
checkNotStarted();
this.connectionProperties = props;
}
private void checkNotStarted() throws IllegalStateException {
if (mcf != null)
throw new IllegalStateException(
"ManagedConnectionFactory is already instantiated, " +
"changing connection properties in bulk is not allowed.");
}
private FBManagedConnectionFactory getManagedConnectionFactory() {
if (mcf != null) {
return mcf;
}
synchronized (this) {
if (mcf != null) {
return mcf;
}
GDSType gdsType = GDSType.getType(getType());
if (gdsType == null)
gdsType = GDSFactory.getDefaultGDSType();
mcf = new FBManagedConnectionFactory(gdsType, connectionProperties);
}
return mcf;
}
protected Logger getLogger() {
return LOG;
}
protected PooledConnectionManager getConnectionManager() {
return new PooledConnectionManagerImpl(getManagedConnectionFactory());
}
/**
* Get name of the connection queue.
*
* @see AbstractConnectionPool#getPoolName()
*/
protected String getPoolName() {
return getDatabase();
}
public PrintWriter getLogWriter() {
return logWriter;
}
public void setLogWriter(PrintWriter out) {
logWriter = out;
}
/**
* Get login timeout.
*
* @return value set in {@link #setLoginTimeout(int)} method or 0.
*/
public int getLoginTimeout() {
return getBlockingTimeout() / 1000;
}
/**
* Set login timeout for new connection. Currently ignored.
*
* @param seconds how long pool should wait until new connection is
* granted.
*/
public void setLoginTimeout(int seconds) {
setBlockingTimeout(seconds * 1000);
}
/**
* Get pooled connection from the pooled queue.
*/
protected PooledObject getPooledConnection(PooledConnectionQueue queue) throws SQLException {
FBPooledConnection connection = (FBPooledConnection) super.getPooledConnection(queue);
connection.addConnectionEventListener(this);
connection.setManagedEnvironment(false);
return connection;
}
/**
* Get pooled connection. This method will block until there will be
* free connection to return.
*
* @return instance of {@link PooledConnection}.
*
* @throws SQLException if pooled connection cannot be obtained.
*/
public PooledConnection getPooledConnection() throws SQLException {
return (PooledConnection)getPooledConnection(getQueue(EMPTY_USER_PASSWORD));
}
/**
* Get pooled connection for the specified user name and password.
*
* @param user user name.
* @param password password corresponding to specified user name.
*
* @return instance of {@link PooledConnection} for the specified
* credentials.
*
* @throws SQLException always, this method is not yet implemented.
*/
public PooledConnection getPooledConnection(String user, String password) throws SQLException {
return (PooledConnection)getPooledConnection(
getQueue(new AbstractConnectionPool.UserPasswordPair(user, password)));
}
/**
* Get XA connection. This method will block until there will be
* free connection to return.
*
* @return instance of {@link XAConnection}.
*
* @throws SQLException if pooled connection cannot be obtained.
*/
public XAConnection getXAConnection() throws SQLException {
FBPooledConnection result = (FBPooledConnection)getPooledConnection();
result.setManagedEnvironment(true);
return result;
}
/**
* Get XA connection for the specified user name and password.
*
* @param user user name.
* @param password password corresponding to specified user name.
*
* @return instance of {@link XAConnection} for the specified
* credentials.
*
* @throws SQLException always, this method is not yet implemented.
*/
public XAConnection getXAConnection(String user, String password)
throws SQLException
{
FBPooledConnection result = (FBPooledConnection)getPooledConnection(user, password);
result.setManagedEnvironment(true);
return result;
}
/**
* Notify about connection being closed.
*
* @param connectionEvent instance of {@link ConnectionEvent}.
*/
public void connectionClosed(ConnectionEvent connectionEvent) {
PooledObjectEvent event =
new PooledObjectEvent(connectionEvent.getSource());
pooledObjectReleased(event);
}
/**
* Notify about physical connection being closed.
*
* @param connectionEvent instance of {@link ConnectionEvent}.
*/
public void physicalConnectionClosed(ConnectionEvent connectionEvent) {
PooledObjectEvent event =
new PooledObjectEvent(connectionEvent.getSource(), true);
pooledObjectReleased(event);
}
/**
* Notify about the deallocation of the physical connection.
*
* @param connectionEvent instance of {@link ConnectionEvent}.
*/
public void physicalConnectionDeallocated(ConnectionEvent connectionEvent) {
PooledObjectEvent event =
new PooledObjectEvent(connectionEvent.getSource(), true);
physicalConnectionDeallocated(event);
}
/**
* Notify about serious error when using the connection. Currently
* these events are ignored.
*
* @param event instance of {@link ConnectionEvent} containing
* information about an error.
*/
public void connectionErrorOccurred(ConnectionEvent event) {
if (getLogger() != null)
getLogger().error("Error occured in connection.",
event.getSQLException());
}
public int getFreeSize() throws SQLException {
return getQueue(EMPTY_USER_PASSWORD).size();
}
public int getTotalSize() throws SQLException {
return getQueue(EMPTY_USER_PASSWORD).totalSize();
}
public int getWorkingSize() throws SQLException {
return getQueue(EMPTY_USER_PASSWORD).workingSize();
}
/**
* Check if this configuation defines a pingable connection JDBC pool.
*
* @see org.firebirdsql.pool.ConnectionPoolConfiguration#isPingable()
*/
public boolean isPingable() {
return true;
}
/**
* Get SQL statement that will be used to "ping" the connection.
*
* @see org.firebirdsql.pool.ConnectionPoolConfiguration#getPingStatement()
*/
public String getPingStatement() {
String pingStatement = super.getPingStatement();
if (pingStatement != null)
return pingStatement;
else
return PING_STATEMENT;
}
/**
* Set JDBC properties that will be passed when opening a connection.
*
* @param properties instance of {@link Properties} containing properties
* of a connection to open.
*/
public void setProperties(Properties properties) {
for (Iterator iter = properties.entrySet().iterator(); iter.hasNext();) {
Map.Entry entry = (Map.Entry) iter.next();
String key = (String)entry.getKey();
String value = (String)entry.getValue();
setNonStandardProperty(key, value);
}
}
/**
* Get type of JDBC driver that will be used. Note, value returned by this
* method might be different from that used in {@link #setType(String)} if
* you used synonym (either "TYPE4"
or "TYPE2"
).
*
* @return one of the following values:
*
* "PURE_JAVA"
for pure Java type 4 JDBC driver.
* "NATIVE"
for type 2 JDBC driver that will use Firebird
* client library.
* "EMBEDDED"
for type 2 JDBC driver that will use
* embedded engine.
*
*/
public String getType() {
return connectionProperties.getType();
}
/**
* Set type of JDBC driver to use.
*
* @param type type of driver to use. Possible values are (case insensitive):
*
* "PURE_JAVA"
or "TYPE4"
for pure Java type 4
* JDBC driver;
* "NATIVE"
or "TYPE2"
for type 2 JDBC driver
* that will use Firebird client library.
* "EMBEDDED"
for type 2 JDBC driver that will use embedded
* version of the server.
*
*/
public void setType(String type) {
checkNotStarted();
connectionProperties.setType(type);
}
/**
* Get type of JDBC driver that is used.
*
* @return type of JDBC driver that is used.
*/
public GDSType getGDSType() {
return GDSType.getType(getType());
}
/**
* Set type of the JDBC driver to use.
*
* @param gdsType type of the JDBC driver.
*/
public void setGDSType(GDSType gdsType) {
checkNotStarted();
setType(gdsType.toString());
}
/*
* Properties of this data source. These methods are created only
* for user convenience and are shortcuts to setProperty(String, String)
* method call with respective keys.
*/
public String getNonStandardProperty(String key) {
return connectionProperties.getNonStandardProperty(key);
}
public void setNonStandardProperty(String key, String value) {
checkNotStarted();
connectionProperties.setNonStandardProperty(key, value);
}
/**
* Method that allows setting non-standard property in the form "key=value"
* form. This method is needed by some containers to specify properties
* in the configuration.
*
* @param propertyMapping mapping between property name (key) and its value.
* Name and value are separated with "=", ":" or whitespace character.
* Whitespace characters on the beginning of the string and between key and
* value are ignored. No escaping is possible: "\n" is backslash-en, not
* a new line mark.
*/
public void setNonStandardProperty(String propertyMapping) {
checkNotStarted();
connectionProperties.setNonStandardProperty(propertyMapping);
}
public int getBlobBufferSize() {
return connectionProperties.getBlobBufferSize();
}
public void setBlobBufferSize(int blobBufferSize) {
checkNotStarted();
connectionProperties.setBlobBufferSize(blobBufferSize);
}
public String getEncoding() {
return connectionProperties.getEncoding();
}
public void setEncoding(String encoding) {
checkNotStarted();
connectionProperties.setEncoding(encoding);
}
public String getCharSet() {
return connectionProperties.getCharSet();
}
public void setCharSet(String charSet) {
checkNotStarted();
connectionProperties.setCharSet(charSet);
}
public String getPassword() {
return connectionProperties.getPassword();
}
public void setPassword(String password) {
checkNotStarted();
connectionProperties.setPassword(password);
}
public int getSocketBufferSize() {
checkNotStarted();
return connectionProperties.getSocketBufferSize();
}
public void setSocketBufferSize(int socketBufferSize) {
checkNotStarted();
connectionProperties.setSocketBufferSize(socketBufferSize);
}
public String getRoleName() {
return connectionProperties.getRoleName();
}
public void setRoleName(String roleName) {
checkNotStarted();
connectionProperties.setRoleName(roleName);
}
/**
* @deprecated please use {@link #getRoleName()} instead.
*/
public String getSqlRole() {
return getRoleName();
}
/**
* @deprecated please use {@link #setRoleName(String)} instead.
*/
public void setSqlRole(String sqlRole) {
setRoleName(sqlRole);
}
public String getTpbMapping() {
return connectionProperties.getTpbMapping();
}
public void setTpbMapping(String tpbMapping) {
checkNotStarted();
connectionProperties.setTpbMapping(tpbMapping);
}
public String getUserName() {
return connectionProperties.getUserName();
}
public void setUserName(String userName) {
checkNotStarted();
connectionProperties.setUserName(userName);
}
public int getBuffersNumber() {
return connectionProperties.getBlobBufferSize();
}
public String getDatabase() {
return connectionProperties.getDatabase();
}
public DatabaseParameterBuffer getDatabaseParameterBuffer() throws SQLException {
return connectionProperties.getDatabaseParameterBuffer();
}
public String getDefaultIsolation() {
return connectionProperties.getDefaultIsolation();
}
public int getDefaultTransactionIsolation() {
return connectionProperties.getDefaultTransactionIsolation();
}
public String getSqlDialect() {
return connectionProperties.getSqlDialect();
}
public TransactionParameterBuffer getTransactionParameters(int isolation) {
return connectionProperties.getTransactionParameters(isolation);
}
public String getUseTranslation() {
return connectionProperties.getUseTranslation();
}
public boolean isTimestampUsesLocalTimezone() {
return connectionProperties.isTimestampUsesLocalTimezone();
}
public boolean isUseStandardUdf() {
return connectionProperties.isUseStandardUdf();
}
public boolean isUseStreamBlobs() {
return connectionProperties.isUseStreamBlobs();
}
public void setBuffersNumber(int buffersNumber) {
checkNotStarted();
connectionProperties.setBuffersNumber(buffersNumber);
}
public void setDatabase(String database) {
checkNotStarted();
connectionProperties.setDatabase(database);
}
public void setDefaultIsolation(String isolation) {
checkNotStarted();
connectionProperties.setDefaultIsolation(isolation);
}
public void setDefaultTransactionIsolation(int defaultIsolationLevel) {
checkNotStarted();
connectionProperties.setDefaultTransactionIsolation(defaultIsolationLevel);
}
public void setSqlDialect(String sqlDialect) {
checkNotStarted();
connectionProperties.setSqlDialect(sqlDialect);
}
public void setTimestampUsesLocalTimezone(boolean timestampUsesLocalTimezone) {
checkNotStarted();
connectionProperties.setTimestampUsesLocalTimezone(timestampUsesLocalTimezone);
}
public void setTransactionParameters(int isolation, TransactionParameterBuffer tpb) {
checkNotStarted();
connectionProperties.setTransactionParameters(isolation, tpb);
}
public void setUseStandardUdf(boolean useStandardUdf) {
checkNotStarted();
connectionProperties.setUseStandardUdf(useStandardUdf);
}
public void setUseStreamBlobs(boolean useStreamBlobs) {
checkNotStarted();
connectionProperties.setUseStreamBlobs(useStreamBlobs);
}
public void setUseTranslation(String translationPath) {
checkNotStarted();
connectionProperties.setUseTranslation(translationPath);
}
public boolean isDefaultResultSetHoldable() {
return connectionProperties.isDefaultResultSetHoldable();
}
public void setDefaultResultSetHoldable(boolean isHoldable) {
checkNotStarted();
connectionProperties.setDefaultResultSetHoldable(isHoldable);
}
public int getSoTimeout() {
checkNotStarted();
return connectionProperties.getSoTimeout();
}
public void setSoTimeout(int soTimeout) {
checkNotStarted();
connectionProperties.setSoTimeout(soTimeout);
}
public int getConnectTimeout() {
return connectionProperties.getConnectTimeout();
}
public void setConnectTimeout(int connectTimeout) {
checkNotStarted();
connectionProperties.setConnectTimeout(connectTimeout);
}
protected static final String REF_PROPERTIES = "properties";
protected static final String REF_NON_STANDARD_PROPERTY = "nonStandard";
public Reference getDefaultReference() {
Reference ref = super.getDefaultReference();
byte[] data = serialize(connectionProperties);
ref.add(new BinaryRefAddr(REF_PROPERTIES, data));
return ref;
}
protected Object processObjectInstance(AbstractFBConnectionPoolDataSource ds, Reference ref) throws Exception
{
if (ds == null)
return null;
for (int i = 0; i < ref.size(); i++) {
RefAddr element = ref.get(i);
String type = element.getType();
if (REF_NON_STANDARD_PROPERTY.equals(type))
ds.setNonStandardProperty(element.getContent().toString());
else
if (REF_PROPERTIES.equals(type)) {
byte[] data = (byte[]) element.getContent();
FBConnectionProperties props = (FBConnectionProperties) deserialize(data);
ds.setConnectionProperties(props);
} else
if (element.getContent() instanceof String)
ds.setNonStandardProperty(type, element.getContent().toString());
}
return ds;
}
protected BasicAbstractConnectionPool createObjectInstance() {
return FBPooledDataSourceFactory.createFBConnectionPoolDataSource();
}
/**
* Implementation of {@link org.firebirdsql.pool.AbstractFBConnectionPoolDataSource.PooledConnectionManagerImpl}
*/
private class PooledConnectionManagerImpl implements PooledConnectionManager {
private final WeakReference mcf;
private PooledConnectionManagerImpl(FBManagedConnectionFactory mcf) {
this.mcf = new WeakReference(mcf);
}
public PooledObject allocateConnection(Object key, PooledConnectionQueue queue)throws SQLException {
if (!(key instanceof AbstractConnectionPool.UserPasswordPair))
throw new SQLException("Incorrect key.");
final AbstractConnectionPool.UserPasswordPair pair = (AbstractConnectionPool.UserPasswordPair)key;
final FBManagedConnectionFactory connectionFactory = mcf.get();
if (connectionFactory == null) {
throw new SQLException("Weak reference to connection factory is null. Underlying connection pool has not been initialized or has been closed.");
}
final String userName = pair.getUserName();
final String password = pair.getPassword();
try {
final FBConnectionRequestInfo defaultCri = connectionFactory.getDefaultConnectionRequestInfo();
if (userName != null)
defaultCri.setUserName(userName);
if (password != null)
defaultCri.setPassword(password);
final FBManagedConnection managedConnection = (FBManagedConnection) connectionFactory.createManagedConnection(null, defaultCri);
managedConnection.setConnectionSharing(false);
managedConnection.setManagedEnvironment(false);
final PingablePooledConnection pooledConnection;
if (isPingable())
pooledConnection =
new FBPooledConnection(
managedConnection,
defaultCri,
getPingStatement(),
getPingInterval(),
isStatementPooling(),
getMaxStatements(),
isKeepStatements(),
queue);
else
pooledConnection =
new FBPooledConnection(
managedConnection,
defaultCri,
isStatementPooling(),
getMaxStatements(),
isKeepStatements(),
queue);
return pooledConnection;
} catch(ResourceException ex) {
throw new FBSQLException(ex);
}
}
}
}