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 Show documentation
Show all versions of jaybird Show documentation
JDBC Driver for the Firebird RDBMS
/*
* 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.sql.SQLException;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import javax.naming.BinaryRefAddr;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.resource.ResourceException;
import javax.sql.ConnectionEvent;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.PooledConnection;
import javax.sql.XAConnection;
import javax.sql.XADataSource;
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.FBConnectionRequestInfo;
import org.firebirdsql.jca.FBManagedConnection;
import org.firebirdsql.jca.FBManagedConnectionFactory;
import org.firebirdsql.jdbc.FBConnectionProperties;
import org.firebirdsql.jdbc.FBSQLException;
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 PooledConnectionManager, 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 FBManagedConnectionFactory mcf;
private FBConnectionProperties connectionProperties = new FBConnectionProperties();
/**
* Create instance of this class.
*/
public AbstractFBConnectionPoolDataSource() {
super();
}
/**
* 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 IllegalPoolStateException 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 synchronized FBManagedConnectionFactory getManagedConnectionFactory() {
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 this;
}
/**
* Allocate new physical connection for the specified user name and
* password.
*
* @param key key identifying pooled object.
*
* @return instance of {@link PooledObject}.
*
* @throws SQLException if connection cannot be allocated.
*/
public PooledObject allocateConnection(Object key)
throws SQLException
{
if (!(key instanceof AbstractConnectionPool.UserPasswordPair))
throw new SQLException("Incorrect key.");
AbstractConnectionPool.UserPasswordPair pair =
(AbstractConnectionPool.UserPasswordPair)key;
String userName = pair.getUserName();
String password = pair.getPassword();
try {
FBConnectionRequestInfo defaultCri =
getManagedConnectionFactory().getDefaultConnectionRequestInfo();
if (userName != null)
defaultCri.setUserName(userName);
if (password != null)
defaultCri.setPassword(password);
FBManagedConnection managedConnection = (FBManagedConnection)
getManagedConnectionFactory().createManagedConnection(null, defaultCri);
managedConnection.setConnectionSharing(false);
managedConnection.setManagedEnvironment(false);
PingablePooledConnection pooledConnection = null;
if (isPingable())
pooledConnection =
new FBPooledConnection(
managedConnection,
defaultCri,
getPingStatement(),
getPingInterval(),
isStatementPooling(),
/*getTransactionIsolationLevel(),*/
getMaxStatements(),
isKeepStatements());
else
pooledConnection =
new FBPooledConnection(
managedConnection,
defaultCri,
isStatementPooling(),
/*getTransactionIsolationLevel(),*/
getMaxStatements(),
isKeepStatements());
return pooledConnection;
} catch(ResourceException ex) {
throw new FBSQLException(ex);
}
}
/**
* 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 synchronized 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 synchronized 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 synchronized 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.
*
*
* @throws SQLException if specified type is not known.
*/
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);
}
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();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy