mirror of
https://github.com/postgres/postgres.git
synced 2025-05-06 19:59:18 +03:00
missed a few files
This commit is contained in:
parent
2a9bf5b33d
commit
b195c10df7
@ -1,6 +0,0 @@
|
|||||||
.classpath
|
|
||||||
.project
|
|
||||||
.externalToolBuilders
|
|
||||||
build
|
|
||||||
build.properties
|
|
||||||
jars
|
|
@ -1 +0,0 @@
|
|||||||
Driver.java
|
|
@ -1,265 +0,0 @@
|
|||||||
package org.postgresql.jdbc2.optional;
|
|
||||||
|
|
||||||
import javax.naming.*;
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
import java.sql.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for data sources and related classes.
|
|
||||||
*
|
|
||||||
* @author Aaron Mulder (ammulder@chariotsolutions.com)
|
|
||||||
* @version $Revision: 1.3 $
|
|
||||||
*/
|
|
||||||
public abstract class BaseDataSource implements Referenceable
|
|
||||||
{
|
|
||||||
// Load the normal driver, since we'll use it to actually connect to the
|
|
||||||
// database. That way we don't have to maintain the connecting code in
|
|
||||||
// multiple places.
|
|
||||||
static {
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Class.forName("org.postgresql.Driver");
|
|
||||||
}
|
|
||||||
catch (ClassNotFoundException e)
|
|
||||||
{
|
|
||||||
System.err.println("PostgreSQL DataSource unable to load PostgreSQL JDBC Driver");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Needed to implement the DataSource/ConnectionPoolDataSource interfaces
|
|
||||||
private transient PrintWriter logger;
|
|
||||||
// Don't track loginTimeout, since we'd just ignore it anyway
|
|
||||||
|
|
||||||
// Standard properties, defined in the JDBC 2.0 Optional Package spec
|
|
||||||
private String serverName = "localhost";
|
|
||||||
private String databaseName;
|
|
||||||
private String user;
|
|
||||||
private String password;
|
|
||||||
private int portNumber;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a connection to the PostgreSQL database. The database is identified by the
|
|
||||||
* DataSource properties serverName, databaseName, and portNumber. The user to
|
|
||||||
* connect as is identified by the DataSource properties user and password.
|
|
||||||
*
|
|
||||||
* @return A valid database connection.
|
|
||||||
* @throws SQLException
|
|
||||||
* Occurs when the database connection cannot be established.
|
|
||||||
*/
|
|
||||||
public Connection getConnection() throws SQLException
|
|
||||||
{
|
|
||||||
return getConnection(user, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a connection to the PostgreSQL database. The database is identified by the
|
|
||||||
* DataAource properties serverName, databaseName, and portNumber. The user to
|
|
||||||
* connect as is identified by the arguments user and password, which override
|
|
||||||
* the DataSource properties by the same name.
|
|
||||||
*
|
|
||||||
* @return A valid database connection.
|
|
||||||
* @throws SQLException
|
|
||||||
* Occurs when the database connection cannot be established.
|
|
||||||
*/
|
|
||||||
public Connection getConnection(String user, String password) throws SQLException
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Connection con = DriverManager.getConnection(getUrl(), user, password);
|
|
||||||
if (logger != null)
|
|
||||||
{
|
|
||||||
logger.println("Created a non-pooled connection for " + user + " at " + getUrl());
|
|
||||||
}
|
|
||||||
return con;
|
|
||||||
}
|
|
||||||
catch (SQLException e)
|
|
||||||
{
|
|
||||||
if (logger != null)
|
|
||||||
{
|
|
||||||
logger.println("Failed to create a non-pooled connection for " + user + " at " + getUrl() + ": " + e);
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This DataSource does not support a configurable login timeout.
|
|
||||||
* @return 0
|
|
||||||
*/
|
|
||||||
public int getLoginTimeout() throws SQLException
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This DataSource does not support a configurable login timeout. Any value
|
|
||||||
* provided here will be ignored.
|
|
||||||
*/
|
|
||||||
public void setLoginTimeout(int i) throws SQLException
|
|
||||||
{}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the log writer used to log connections opened.
|
|
||||||
*/
|
|
||||||
public PrintWriter getLogWriter() throws SQLException
|
|
||||||
{
|
|
||||||
return logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The DataSource will note every connection opened to the provided log writer.
|
|
||||||
*/
|
|
||||||
public void setLogWriter(PrintWriter printWriter) throws SQLException
|
|
||||||
{
|
|
||||||
logger = printWriter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the name of the host the PostgreSQL database is running on.
|
|
||||||
*/
|
|
||||||
public String getServerName()
|
|
||||||
{
|
|
||||||
return serverName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the name of the host the PostgreSQL database is running on. If this
|
|
||||||
* is changed, it will only affect future calls to getConnection. The default
|
|
||||||
* value is <tt>localhost</tt>.
|
|
||||||
*/
|
|
||||||
public void setServerName(String serverName)
|
|
||||||
{
|
|
||||||
if (serverName == null || serverName.equals(""))
|
|
||||||
{
|
|
||||||
this.serverName = "localhost";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.serverName = serverName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the name of the PostgreSQL database, running on the server identified
|
|
||||||
* by the serverName property.
|
|
||||||
*/
|
|
||||||
public String getDatabaseName()
|
|
||||||
{
|
|
||||||
return databaseName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the name of the PostgreSQL database, running on the server identified
|
|
||||||
* by the serverName property. If this is changed, it will only affect
|
|
||||||
* future calls to getConnection.
|
|
||||||
*/
|
|
||||||
public void setDatabaseName(String databaseName)
|
|
||||||
{
|
|
||||||
this.databaseName = databaseName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a description of this DataSource-ish thing. Must be customized by
|
|
||||||
* subclasses.
|
|
||||||
*/
|
|
||||||
public abstract String getDescription();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the user to connect as by default. If this is not specified, you must
|
|
||||||
* use the getConnection method which takes a user and password as parameters.
|
|
||||||
*/
|
|
||||||
public String getUser()
|
|
||||||
{
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the user to connect as by default. If this is not specified, you must
|
|
||||||
* use the getConnection method which takes a user and password as parameters.
|
|
||||||
* If this is changed, it will only affect future calls to getConnection.
|
|
||||||
*/
|
|
||||||
public void setUser(String user)
|
|
||||||
{
|
|
||||||
this.user = user;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the password to connect with by default. If this is not specified but a
|
|
||||||
* password is needed to log in, you must use the getConnection method which takes
|
|
||||||
* a user and password as parameters.
|
|
||||||
*/
|
|
||||||
public String getPassword()
|
|
||||||
{
|
|
||||||
return password;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the password to connect with by default. If this is not specified but a
|
|
||||||
* password is needed to log in, you must use the getConnection method which takes
|
|
||||||
* a user and password as parameters. If this is changed, it will only affect
|
|
||||||
* future calls to getConnection.
|
|
||||||
*/
|
|
||||||
public void setPassword(String password)
|
|
||||||
{
|
|
||||||
this.password = password;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the port which the PostgreSQL server is listening on for TCP/IP
|
|
||||||
* connections.
|
|
||||||
*
|
|
||||||
* @return The port, or 0 if the default port will be used.
|
|
||||||
*/
|
|
||||||
public int getPortNumber()
|
|
||||||
{
|
|
||||||
return portNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the port which the PostgreSQL server is listening on for TCP/IP
|
|
||||||
* connections. Be sure the -i flag is passed to postmaster when PostgreSQL
|
|
||||||
* is started. If this is not set, or set to 0, the default port will be used.
|
|
||||||
*/
|
|
||||||
public void setPortNumber(int portNumber)
|
|
||||||
{
|
|
||||||
this.portNumber = portNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a DriverManager URL from the other properties supplied.
|
|
||||||
*/
|
|
||||||
private String getUrl()
|
|
||||||
{
|
|
||||||
return "jdbc:postgresql://" + serverName + (portNumber == 0 ? "" : ":" + portNumber) + "/" + databaseName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a reference using the appropriate object factory. This
|
|
||||||
* implementation uses the JDBC 2 optional package object factory.
|
|
||||||
*/
|
|
||||||
protected Reference createReference()
|
|
||||||
{
|
|
||||||
return new Reference(getClass().getName(), PGObjectFactory.class.getName(), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Reference getReference() throws NamingException
|
|
||||||
{
|
|
||||||
Reference ref = createReference();
|
|
||||||
ref.add(new StringRefAddr("serverName", serverName));
|
|
||||||
if (portNumber != 0)
|
|
||||||
{
|
|
||||||
ref.add(new StringRefAddr("portNumber", Integer.toString(portNumber)));
|
|
||||||
}
|
|
||||||
ref.add(new StringRefAddr("databaseName", databaseName));
|
|
||||||
if (user != null)
|
|
||||||
{
|
|
||||||
ref.add(new StringRefAddr("user", user));
|
|
||||||
}
|
|
||||||
if (password != null)
|
|
||||||
{
|
|
||||||
ref.add(new StringRefAddr("password", password));
|
|
||||||
}
|
|
||||||
return ref;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,82 +0,0 @@
|
|||||||
package org.postgresql.jdbc2.optional;
|
|
||||||
|
|
||||||
import javax.sql.ConnectionPoolDataSource;
|
|
||||||
import javax.sql.PooledConnection;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PostgreSQL implementation of ConnectionPoolDataSource. The app server or
|
|
||||||
* middleware vendor should provide a DataSource implementation that takes advantage
|
|
||||||
* of this ConnectionPoolDataSource. If not, you can use the PostgreSQL implementation
|
|
||||||
* known as PoolingDataSource, but that should only be used if your server or middleware
|
|
||||||
* vendor does not provide their own. Why? The server may want to reuse the same
|
|
||||||
* Connection across all EJBs requesting a Connection within the same Transaction, or
|
|
||||||
* provide other similar advanced features.
|
|
||||||
*
|
|
||||||
* <p>In any case, in order to use this ConnectionPoolDataSource, you must set the property
|
|
||||||
* databaseName. The settings for serverName, portNumber, user, and password are
|
|
||||||
* optional. Note: these properties are declared in the superclass.</p>
|
|
||||||
*
|
|
||||||
* <p>This implementation supports JDK 1.3 and higher.</p>
|
|
||||||
*
|
|
||||||
* @author Aaron Mulder (ammulder@chariotsolutions.com)
|
|
||||||
* @version $Revision: 1.2 $
|
|
||||||
*/
|
|
||||||
public class ConnectionPool extends BaseDataSource implements Serializable, ConnectionPoolDataSource
|
|
||||||
{
|
|
||||||
private boolean defaultAutoCommit = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a description of this DataSource.
|
|
||||||
*/
|
|
||||||
public String getDescription()
|
|
||||||
{
|
|
||||||
return "ConnectionPoolDataSource from " + org.postgresql.Driver.getVersion();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a connection which may be pooled by the app server or middleware
|
|
||||||
* implementation of DataSource.
|
|
||||||
*
|
|
||||||
* @throws java.sql.SQLException
|
|
||||||
* Occurs when the physical database connection cannot be established.
|
|
||||||
*/
|
|
||||||
public PooledConnection getPooledConnection() throws SQLException
|
|
||||||
{
|
|
||||||
return new PooledConnectionImpl(getConnection(), defaultAutoCommit);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a connection which may be pooled by the app server or middleware
|
|
||||||
* implementation of DataSource.
|
|
||||||
*
|
|
||||||
* @throws java.sql.SQLException
|
|
||||||
* Occurs when the physical database connection cannot be established.
|
|
||||||
*/
|
|
||||||
public PooledConnection getPooledConnection(String user, String password) throws SQLException
|
|
||||||
{
|
|
||||||
return new PooledConnectionImpl(getConnection(user, password), defaultAutoCommit);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets whether connections supplied by this pool will have autoCommit
|
|
||||||
* turned on by default. The default value is <tt>false</tt>, so that
|
|
||||||
* autoCommit will be turned off by default.
|
|
||||||
*/
|
|
||||||
public boolean isDefaultAutoCommit()
|
|
||||||
{
|
|
||||||
return defaultAutoCommit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets whether connections supplied by this pool will have autoCommit
|
|
||||||
* turned on by default. The default value is <tt>false</tt>, so that
|
|
||||||
* autoCommit will be turned off by default.
|
|
||||||
*/
|
|
||||||
public void setDefaultAutoCommit(boolean defaultAutoCommit)
|
|
||||||
{
|
|
||||||
this.defaultAutoCommit = defaultAutoCommit;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,108 +0,0 @@
|
|||||||
package org.postgresql.jdbc2.optional;
|
|
||||||
|
|
||||||
import javax.naming.spi.ObjectFactory;
|
|
||||||
import javax.naming.*;
|
|
||||||
import java.util.Hashtable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a DataSource-ish thing based on a JNDI reference. In the case of a
|
|
||||||
* SimpleDataSource or ConnectionPool, a new instance is created each time, as
|
|
||||||
* there is no connection state to maintain. In the case of a PoolingDataSource,
|
|
||||||
* the same DataSource will be returned for every invocation within the same
|
|
||||||
* VM/ClassLoader, so that the state of the connections in the pool will be
|
|
||||||
* consistent.
|
|
||||||
*
|
|
||||||
* @author Aaron Mulder (ammulder@chariotsolutions.com)
|
|
||||||
* @version $Revision: 1.3 $
|
|
||||||
*/
|
|
||||||
public class PGObjectFactory implements ObjectFactory
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Dereferences a PostgreSQL DataSource. Other types of references are
|
|
||||||
* ignored.
|
|
||||||
*/
|
|
||||||
public Object getObjectInstance(Object obj, Name name, Context nameCtx,
|
|
||||||
Hashtable environment) throws Exception
|
|
||||||
{
|
|
||||||
Reference ref = (Reference)obj;
|
|
||||||
if (ref.getClassName().equals(SimpleDataSource.class.getName()))
|
|
||||||
{
|
|
||||||
return loadSimpleDataSource(ref);
|
|
||||||
}
|
|
||||||
else if (ref.getClassName().equals(ConnectionPool.class.getName()))
|
|
||||||
{
|
|
||||||
return loadConnectionPool(ref);
|
|
||||||
}
|
|
||||||
else if (ref.getClassName().equals(PoolingDataSource.class.getName()))
|
|
||||||
{
|
|
||||||
return loadPoolingDataSource(ref);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Object loadPoolingDataSource(Reference ref)
|
|
||||||
{
|
|
||||||
// If DataSource exists, return it
|
|
||||||
String name = getProperty(ref, "dataSourceName");
|
|
||||||
PoolingDataSource pds = PoolingDataSource.getDataSource(name);
|
|
||||||
if (pds != null)
|
|
||||||
{
|
|
||||||
return pds;
|
|
||||||
}
|
|
||||||
// Otherwise, create a new one
|
|
||||||
pds = new PoolingDataSource();
|
|
||||||
pds.setDataSourceName(name);
|
|
||||||
loadBaseDataSource(pds, ref);
|
|
||||||
String min = getProperty(ref, "initialConnections");
|
|
||||||
if (min != null)
|
|
||||||
{
|
|
||||||
pds.setInitialConnections(Integer.parseInt(min));
|
|
||||||
}
|
|
||||||
String max = getProperty(ref, "maxConnections");
|
|
||||||
if (max != null)
|
|
||||||
{
|
|
||||||
pds.setMaxConnections(Integer.parseInt(max));
|
|
||||||
}
|
|
||||||
return pds;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Object loadSimpleDataSource(Reference ref)
|
|
||||||
{
|
|
||||||
SimpleDataSource ds = new SimpleDataSource();
|
|
||||||
return loadBaseDataSource(ds, ref);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Object loadConnectionPool(Reference ref)
|
|
||||||
{
|
|
||||||
ConnectionPool cp = new ConnectionPool();
|
|
||||||
return loadBaseDataSource(cp, ref);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Object loadBaseDataSource(BaseDataSource ds, Reference ref)
|
|
||||||
{
|
|
||||||
ds.setDatabaseName(getProperty(ref, "databaseName"));
|
|
||||||
ds.setPassword(getProperty(ref, "password"));
|
|
||||||
String port = getProperty(ref, "portNumber");
|
|
||||||
if (port != null)
|
|
||||||
{
|
|
||||||
ds.setPortNumber(Integer.parseInt(port));
|
|
||||||
}
|
|
||||||
ds.setServerName(getProperty(ref, "serverName"));
|
|
||||||
ds.setUser(getProperty(ref, "user"));
|
|
||||||
return ds;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getProperty(Reference ref, String s)
|
|
||||||
{
|
|
||||||
RefAddr addr = ref.get(s);
|
|
||||||
if (addr == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return (String)addr.getContent();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,392 +0,0 @@
|
|||||||
package org.postgresql.jdbc2.optional;
|
|
||||||
|
|
||||||
import javax.sql.*;
|
|
||||||
import java.sql.*;
|
|
||||||
import java.util.*;
|
|
||||||
import java.lang.reflect.*;
|
|
||||||
import org.postgresql.PGConnection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PostgreSQL implementation of the PooledConnection interface. This shouldn't
|
|
||||||
* be used directly, as the pooling client should just interact with the
|
|
||||||
* ConnectionPool instead.
|
|
||||||
* @see ConnectionPool
|
|
||||||
*
|
|
||||||
* @author Aaron Mulder (ammulder@chariotsolutions.com)
|
|
||||||
* @author Csaba Nagy (ncsaba@yahoo.com)
|
|
||||||
* @version $Revision: 1.8 $
|
|
||||||
*/
|
|
||||||
public class PooledConnectionImpl implements PooledConnection
|
|
||||||
{
|
|
||||||
private List listeners = new LinkedList();
|
|
||||||
private Connection con;
|
|
||||||
private ConnectionHandler last;
|
|
||||||
private boolean autoCommit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new PooledConnection representing the specified physical
|
|
||||||
* connection.
|
|
||||||
*/
|
|
||||||
protected PooledConnectionImpl(Connection con, boolean autoCommit)
|
|
||||||
{
|
|
||||||
this.con = con;
|
|
||||||
this.autoCommit = autoCommit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a listener for close or fatal error events on the connection
|
|
||||||
* handed out to a client.
|
|
||||||
*/
|
|
||||||
public void addConnectionEventListener(ConnectionEventListener connectionEventListener)
|
|
||||||
{
|
|
||||||
listeners.add(connectionEventListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes a listener for close or fatal error events on the connection
|
|
||||||
* handed out to a client.
|
|
||||||
*/
|
|
||||||
public void removeConnectionEventListener(ConnectionEventListener connectionEventListener)
|
|
||||||
{
|
|
||||||
listeners.remove(connectionEventListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the physical database connection represented by this
|
|
||||||
* PooledConnection. If any client has a connection based on
|
|
||||||
* this PooledConnection, it is forcibly closed as well.
|
|
||||||
*/
|
|
||||||
public void close() throws SQLException
|
|
||||||
{
|
|
||||||
if (last != null)
|
|
||||||
{
|
|
||||||
last.close();
|
|
||||||
if (!con.getAutoCommit())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
con.rollback();
|
|
||||||
}
|
|
||||||
catch (SQLException e)
|
|
||||||
{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
con.close();
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
con = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a handle for a client to use. This is a wrapper around the
|
|
||||||
* physical connection, so the client can call close and it will just
|
|
||||||
* return the connection to the pool without really closing the
|
|
||||||
* pgysical connection.
|
|
||||||
*
|
|
||||||
* <p>According to the JDBC 2.0 Optional Package spec (6.2.3), only one
|
|
||||||
* client may have an active handle to the connection at a time, so if
|
|
||||||
* there is a previous handle active when this is called, the previous
|
|
||||||
* one is forcibly closed and its work rolled back.</p>
|
|
||||||
*/
|
|
||||||
public Connection getConnection() throws SQLException
|
|
||||||
{
|
|
||||||
if (con == null)
|
|
||||||
{
|
|
||||||
// Before throwing the exception, let's notify the registered listeners about the error
|
|
||||||
final SQLException sqlException = new SQLException("This PooledConnection has already been closed!");
|
|
||||||
fireConnectionFatalError(sqlException);
|
|
||||||
throw sqlException;
|
|
||||||
}
|
|
||||||
// If any error occures while opening a new connection, the listeners
|
|
||||||
// have to be notified. This gives a chance to connection pools to
|
|
||||||
// elliminate bad pooled connections.
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Only one connection can be open at a time from this PooledConnection. See JDBC 2.0 Optional Package spec section 6.2.3
|
|
||||||
if (last != null)
|
|
||||||
{
|
|
||||||
last.close();
|
|
||||||
if (!con.getAutoCommit())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
con.rollback();
|
|
||||||
}
|
|
||||||
catch (SQLException e)
|
|
||||||
{}
|
|
||||||
}
|
|
||||||
con.clearWarnings();
|
|
||||||
}
|
|
||||||
con.setAutoCommit(autoCommit);
|
|
||||||
}
|
|
||||||
catch (SQLException sqlException)
|
|
||||||
{
|
|
||||||
fireConnectionFatalError(sqlException);
|
|
||||||
throw (SQLException)sqlException.fillInStackTrace();
|
|
||||||
}
|
|
||||||
ConnectionHandler handler = new ConnectionHandler(con);
|
|
||||||
last = handler;
|
|
||||||
Connection con = (Connection)Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Connection.class, PGConnection.class}, handler);
|
|
||||||
last.setProxy(con);
|
|
||||||
return con;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to fire a connection closed event to all listeners.
|
|
||||||
*/
|
|
||||||
void fireConnectionClosed()
|
|
||||||
{
|
|
||||||
ConnectionEvent evt = null;
|
|
||||||
// Copy the listener list so the listener can remove itself during this method call
|
|
||||||
ConnectionEventListener[] local = (ConnectionEventListener[]) listeners.toArray(new ConnectionEventListener[listeners.size()]);
|
|
||||||
for (int i = 0; i < local.length; i++)
|
|
||||||
{
|
|
||||||
ConnectionEventListener listener = local[i];
|
|
||||||
if (evt == null)
|
|
||||||
{
|
|
||||||
evt = new ConnectionEvent(this);
|
|
||||||
}
|
|
||||||
listener.connectionClosed(evt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to fire a connection error event to all listeners.
|
|
||||||
*/
|
|
||||||
void fireConnectionFatalError(SQLException e)
|
|
||||||
{
|
|
||||||
ConnectionEvent evt = null;
|
|
||||||
// Copy the listener list so the listener can remove itself during this method call
|
|
||||||
ConnectionEventListener[] local = (ConnectionEventListener[])listeners.toArray(new ConnectionEventListener[listeners.size()]);
|
|
||||||
for (int i = 0; i < local.length; i++)
|
|
||||||
{
|
|
||||||
ConnectionEventListener listener = local[i];
|
|
||||||
if (evt == null)
|
|
||||||
{
|
|
||||||
evt = new ConnectionEvent(this, e);
|
|
||||||
}
|
|
||||||
listener.connectionErrorOccurred(evt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instead of declaring a class implementing Connection, which would have
|
|
||||||
* to be updated for every JDK rev, use a dynamic proxy to handle all
|
|
||||||
* calls through the Connection interface. This is the part that
|
|
||||||
* requires JDK 1.3 or higher, though JDK 1.2 could be supported with a
|
|
||||||
* 3rd-party proxy package.
|
|
||||||
*/
|
|
||||||
private class ConnectionHandler implements InvocationHandler
|
|
||||||
{
|
|
||||||
private Connection con;
|
|
||||||
private Connection proxy; // the Connection the client is currently using, which is a proxy
|
|
||||||
private boolean automatic = false;
|
|
||||||
|
|
||||||
public ConnectionHandler(Connection con)
|
|
||||||
{
|
|
||||||
this.con = con;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object invoke(Object proxy, Method method, Object[] args)
|
|
||||||
throws Throwable
|
|
||||||
{
|
|
||||||
// From Object
|
|
||||||
if (method.getDeclaringClass().getName().equals("java.lang.Object"))
|
|
||||||
{
|
|
||||||
if (method.getName().equals("toString"))
|
|
||||||
{
|
|
||||||
return "Pooled connection wrapping physical connection " + con;
|
|
||||||
}
|
|
||||||
if (method.getName().equals("hashCode"))
|
|
||||||
{
|
|
||||||
return new Integer(con.hashCode());
|
|
||||||
}
|
|
||||||
if (method.getName().equals("equals"))
|
|
||||||
{
|
|
||||||
if (args[0] == null)
|
|
||||||
{
|
|
||||||
return Boolean.FALSE;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return Proxy.isProxyClass(args[0].getClass()) && ((ConnectionHandler) Proxy.getInvocationHandler(args[0])).con == con ? Boolean.TRUE : Boolean.FALSE;
|
|
||||||
}
|
|
||||||
catch (ClassCastException e)
|
|
||||||
{
|
|
||||||
return Boolean.FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return method.invoke(con, args);
|
|
||||||
}
|
|
||||||
catch (InvocationTargetException e)
|
|
||||||
{
|
|
||||||
throw e.getTargetException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// All the rest is from the Connection or PGConnection interface
|
|
||||||
if (method.getName().equals("isClosed"))
|
|
||||||
{
|
|
||||||
return con == null ? Boolean.TRUE : Boolean.FALSE;
|
|
||||||
}
|
|
||||||
if (con == null)
|
|
||||||
{
|
|
||||||
throw new SQLException(automatic ? "Connection has been closed automatically because a new connection was opened for the same PooledConnection or the PooledConnection has been closed" : "Connection has been closed");
|
|
||||||
}
|
|
||||||
if (method.getName().equals("close"))
|
|
||||||
{
|
|
||||||
SQLException ex = null;
|
|
||||||
if (!con.getAutoCommit())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
con.rollback();
|
|
||||||
}
|
|
||||||
catch (SQLException e)
|
|
||||||
{
|
|
||||||
ex = e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
con.clearWarnings();
|
|
||||||
con = null;
|
|
||||||
proxy = null;
|
|
||||||
last = null;
|
|
||||||
fireConnectionClosed();
|
|
||||||
if (ex != null)
|
|
||||||
{
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
else if(method.getName().equals("createStatement"))
|
|
||||||
{
|
|
||||||
Statement st = (Statement)method.invoke(con, args);
|
|
||||||
return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Statement.class, org.postgresql.PGStatement.class}, new StatementHandler(this, st));
|
|
||||||
}
|
|
||||||
else if(method.getName().equals("prepareCall"))
|
|
||||||
{
|
|
||||||
Statement st = (Statement)method.invoke(con, args);
|
|
||||||
return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{CallableStatement.class, org.postgresql.PGStatement.class}, new StatementHandler(this, st));
|
|
||||||
}
|
|
||||||
else if(method.getName().equals("prepareStatement"))
|
|
||||||
{
|
|
||||||
Statement st = (Statement)method.invoke(con, args);
|
|
||||||
return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{PreparedStatement.class, org.postgresql.PGStatement.class}, new StatementHandler(this, st));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return method.invoke(con, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connection getProxy() {
|
|
||||||
return proxy;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setProxy(Connection proxy) {
|
|
||||||
this.proxy = proxy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close()
|
|
||||||
{
|
|
||||||
if (con != null)
|
|
||||||
{
|
|
||||||
automatic = true;
|
|
||||||
}
|
|
||||||
con = null;
|
|
||||||
proxy = null;
|
|
||||||
// No close event fired here: see JDBC 2.0 Optional Package spec section 6.3
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isClosed() {
|
|
||||||
return con == null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instead of declaring classes implementing Statement, PreparedStatement,
|
|
||||||
* and CallableStatement, which would have to be updated for every JDK rev,
|
|
||||||
* use a dynamic proxy to handle all calls through the Statement
|
|
||||||
* interfaces. This is the part that requires JDK 1.3 or higher, though
|
|
||||||
* JDK 1.2 could be supported with a 3rd-party proxy package.
|
|
||||||
*
|
|
||||||
* The StatementHandler is required in order to return the proper
|
|
||||||
* Connection proxy for the getConnection method.
|
|
||||||
*/
|
|
||||||
private static class StatementHandler implements InvocationHandler {
|
|
||||||
private ConnectionHandler con;
|
|
||||||
private Statement st;
|
|
||||||
|
|
||||||
public StatementHandler(ConnectionHandler con, Statement st) {
|
|
||||||
this.con = con;
|
|
||||||
this.st = st;
|
|
||||||
}
|
|
||||||
public Object invoke(Object proxy, Method method, Object[] args)
|
|
||||||
throws Throwable
|
|
||||||
{
|
|
||||||
// From Object
|
|
||||||
if (method.getDeclaringClass().getName().equals("java.lang.Object"))
|
|
||||||
{
|
|
||||||
if (method.getName().equals("toString"))
|
|
||||||
{
|
|
||||||
return "Pooled statement wrapping physical statement " + st;
|
|
||||||
}
|
|
||||||
if (method.getName().equals("hashCode"))
|
|
||||||
{
|
|
||||||
return new Integer(st.hashCode());
|
|
||||||
}
|
|
||||||
if (method.getName().equals("equals"))
|
|
||||||
{
|
|
||||||
if (args[0] == null)
|
|
||||||
{
|
|
||||||
return Boolean.FALSE;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return Proxy.isProxyClass(args[0].getClass()) && ((StatementHandler) Proxy.getInvocationHandler(args[0])).st == st ? Boolean.TRUE : Boolean.FALSE;
|
|
||||||
}
|
|
||||||
catch (ClassCastException e)
|
|
||||||
{
|
|
||||||
return Boolean.FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return method.invoke(st, args);
|
|
||||||
}
|
|
||||||
// All the rest is from the Statement interface
|
|
||||||
if (st == null || con.isClosed())
|
|
||||||
{
|
|
||||||
throw new SQLException("Statement has been closed");
|
|
||||||
}
|
|
||||||
if (method.getName().equals("close"))
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
st.close();
|
|
||||||
} finally {
|
|
||||||
con = null;
|
|
||||||
st = null;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (method.getName().equals("getConnection"))
|
|
||||||
{
|
|
||||||
return con.getProxy(); // the proxied connection, not a physical connection
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return method.invoke(st, args);
|
|
||||||
}
|
|
||||||
catch (InvocationTargetException e)
|
|
||||||
{
|
|
||||||
throw e.getTargetException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,494 +0,0 @@
|
|||||||
package org.postgresql.jdbc2.optional;
|
|
||||||
|
|
||||||
import javax.sql.*;
|
|
||||||
import javax.naming.*;
|
|
||||||
import java.util.*;
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DataSource which uses connection pooling. <font color="red">Don't use this if
|
|
||||||
* your server/middleware vendor provides a connection pooling implementation
|
|
||||||
* which interfaces with the PostgreSQL ConnectionPoolDataSource implementation!</font>
|
|
||||||
* This class is provided as a convenience, but the JDBC Driver is really not
|
|
||||||
* supposed to handle the connection pooling algorithm. Instead, the server or
|
|
||||||
* middleware product is supposed to handle the mechanics of connection pooling,
|
|
||||||
* and use the PostgreSQL implementation of ConnectionPoolDataSource to provide
|
|
||||||
* the connections to pool.
|
|
||||||
*
|
|
||||||
* <p>If you're sure you want to use this, then you must set the properties
|
|
||||||
* dataSourceName, databaseName, user, and password (if required for the user).
|
|
||||||
* The settings for serverName, portNumber, initialConnections, and
|
|
||||||
* maxConnections are optional. Note that <i>only connections
|
|
||||||
* for the default user will be pooled!</i> Connections for other users will
|
|
||||||
* be normal non-pooled connections, and will not count against the maximum pool
|
|
||||||
* size limit.</p>
|
|
||||||
*
|
|
||||||
* <p>If you put this DataSource in JNDI, and access it from different JVMs (or
|
|
||||||
* otherwise load this class from different ClassLoaders), you'll end up with one
|
|
||||||
* pool per ClassLoader or VM. This is another area where a server-specific
|
|
||||||
* implementation may provide advanced features, such as using a single pool
|
|
||||||
* across all VMs in a cluster.</p>
|
|
||||||
*
|
|
||||||
* <p>This implementation supports JDK 1.3 and higher.</p>
|
|
||||||
*
|
|
||||||
* @author Aaron Mulder (ammulder@chariotsolutions.com)
|
|
||||||
* @version $Revision: 1.3 $
|
|
||||||
*/
|
|
||||||
public class PoolingDataSource extends BaseDataSource implements DataSource
|
|
||||||
{
|
|
||||||
private static Map dataSources = new HashMap();
|
|
||||||
|
|
||||||
static PoolingDataSource getDataSource(String name)
|
|
||||||
{
|
|
||||||
return (PoolingDataSource)dataSources.get(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Additional Data Source properties
|
|
||||||
protected String dataSourceName; // Must be protected for subclasses to sync updates to it
|
|
||||||
private int initialConnections = 0;
|
|
||||||
private int maxConnections = 0;
|
|
||||||
// State variables
|
|
||||||
private boolean initialized = false;
|
|
||||||
private Stack available = new Stack();
|
|
||||||
private Stack used = new Stack();
|
|
||||||
private Object lock = new Object();
|
|
||||||
private ConnectionPool source;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a description of this DataSource.
|
|
||||||
*/
|
|
||||||
public String getDescription()
|
|
||||||
{
|
|
||||||
return "Pooling DataSource '" + dataSourceName + " from " + org.postgresql.Driver.getVersion();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensures the DataSource properties are not changed after the DataSource has
|
|
||||||
* been used.
|
|
||||||
*
|
|
||||||
* @throws java.lang.IllegalStateException
|
|
||||||
* The Server Name cannot be changed after the DataSource has been
|
|
||||||
* used.
|
|
||||||
*/
|
|
||||||
public void setServerName(String serverName)
|
|
||||||
{
|
|
||||||
if (initialized)
|
|
||||||
{
|
|
||||||
throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
|
|
||||||
}
|
|
||||||
super.setServerName(serverName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensures the DataSource properties are not changed after the DataSource has
|
|
||||||
* been used.
|
|
||||||
*
|
|
||||||
* @throws java.lang.IllegalStateException
|
|
||||||
* The Database Name cannot be changed after the DataSource has been
|
|
||||||
* used.
|
|
||||||
*/
|
|
||||||
public void setDatabaseName(String databaseName)
|
|
||||||
{
|
|
||||||
if (initialized)
|
|
||||||
{
|
|
||||||
throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
|
|
||||||
}
|
|
||||||
super.setDatabaseName(databaseName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensures the DataSource properties are not changed after the DataSource has
|
|
||||||
* been used.
|
|
||||||
*
|
|
||||||
* @throws java.lang.IllegalStateException
|
|
||||||
* The User cannot be changed after the DataSource has been
|
|
||||||
* used.
|
|
||||||
*/
|
|
||||||
public void setUser(String user)
|
|
||||||
{
|
|
||||||
if (initialized)
|
|
||||||
{
|
|
||||||
throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
|
|
||||||
}
|
|
||||||
super.setUser(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensures the DataSource properties are not changed after the DataSource has
|
|
||||||
* been used.
|
|
||||||
*
|
|
||||||
* @throws java.lang.IllegalStateException
|
|
||||||
* The Password cannot be changed after the DataSource has been
|
|
||||||
* used.
|
|
||||||
*/
|
|
||||||
public void setPassword(String password)
|
|
||||||
{
|
|
||||||
if (initialized)
|
|
||||||
{
|
|
||||||
throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
|
|
||||||
}
|
|
||||||
super.setPassword(password);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensures the DataSource properties are not changed after the DataSource has
|
|
||||||
* been used.
|
|
||||||
*
|
|
||||||
* @throws java.lang.IllegalStateException
|
|
||||||
* The Port Number cannot be changed after the DataSource has been
|
|
||||||
* used.
|
|
||||||
*/
|
|
||||||
public void setPortNumber(int portNumber)
|
|
||||||
{
|
|
||||||
if (initialized)
|
|
||||||
{
|
|
||||||
throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
|
|
||||||
}
|
|
||||||
super.setPortNumber(portNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the number of connections that will be created when this DataSource
|
|
||||||
* is initialized. If you do not call initialize explicitly, it will be
|
|
||||||
* initialized the first time a connection is drawn from it.
|
|
||||||
*/
|
|
||||||
public int getInitialConnections()
|
|
||||||
{
|
|
||||||
return initialConnections;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the number of connections that will be created when this DataSource
|
|
||||||
* is initialized. If you do not call initialize explicitly, it will be
|
|
||||||
* initialized the first time a connection is drawn from it.
|
|
||||||
*
|
|
||||||
* @throws java.lang.IllegalStateException
|
|
||||||
* The Initial Connections cannot be changed after the DataSource has been
|
|
||||||
* used.
|
|
||||||
*/
|
|
||||||
public void setInitialConnections(int initialConnections)
|
|
||||||
{
|
|
||||||
if (initialized)
|
|
||||||
{
|
|
||||||
throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
|
|
||||||
}
|
|
||||||
this.initialConnections = initialConnections;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the maximum number of connections that the pool will allow. If a request
|
|
||||||
* comes in and this many connections are in use, the request will block until a
|
|
||||||
* connection is available. Note that connections for a user other than the
|
|
||||||
* default user will not be pooled and don't count against this limit.
|
|
||||||
*
|
|
||||||
* @return The maximum number of pooled connection allowed, or 0 for no maximum.
|
|
||||||
*/
|
|
||||||
public int getMaxConnections()
|
|
||||||
{
|
|
||||||
return maxConnections;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the maximum number of connections that the pool will allow. If a request
|
|
||||||
* comes in and this many connections are in use, the request will block until a
|
|
||||||
* connection is available. Note that connections for a user other than the
|
|
||||||
* default user will not be pooled and don't count against this limit.
|
|
||||||
*
|
|
||||||
* @param maxConnections The maximum number of pooled connection to allow, or
|
|
||||||
* 0 for no maximum.
|
|
||||||
*
|
|
||||||
* @throws java.lang.IllegalStateException
|
|
||||||
* The Maximum Connections cannot be changed after the DataSource has been
|
|
||||||
* used.
|
|
||||||
*/
|
|
||||||
public void setMaxConnections(int maxConnections)
|
|
||||||
{
|
|
||||||
if (initialized)
|
|
||||||
{
|
|
||||||
throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
|
|
||||||
}
|
|
||||||
this.maxConnections = maxConnections;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the name of this DataSource. This uniquely identifies the DataSource.
|
|
||||||
* You cannot use more than one DataSource in the same VM with the same name.
|
|
||||||
*/
|
|
||||||
public String getDataSourceName()
|
|
||||||
{
|
|
||||||
return dataSourceName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the name of this DataSource. This is required, and uniquely identifies
|
|
||||||
* the DataSource. You cannot create or use more than one DataSource in the
|
|
||||||
* same VM with the same name.
|
|
||||||
*
|
|
||||||
* @throws java.lang.IllegalStateException
|
|
||||||
* The Data Source Name cannot be changed after the DataSource has been
|
|
||||||
* used.
|
|
||||||
* @throws java.lang.IllegalArgumentException
|
|
||||||
* Another PoolingDataSource with the same dataSourceName already
|
|
||||||
* exists.
|
|
||||||
*/
|
|
||||||
public void setDataSourceName(String dataSourceName)
|
|
||||||
{
|
|
||||||
if (initialized)
|
|
||||||
{
|
|
||||||
throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
|
|
||||||
}
|
|
||||||
if (this.dataSourceName != null && dataSourceName != null && dataSourceName.equals(this.dataSourceName))
|
|
||||||
{
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
synchronized (dataSources)
|
|
||||||
{
|
|
||||||
if (getDataSource(dataSourceName) != null)
|
|
||||||
{
|
|
||||||
throw new IllegalArgumentException("DataSource with name '" + dataSourceName + "' already exists!");
|
|
||||||
}
|
|
||||||
if (this.dataSourceName != null)
|
|
||||||
{
|
|
||||||
dataSources.remove(this.dataSourceName);
|
|
||||||
}
|
|
||||||
this.dataSourceName = dataSourceName;
|
|
||||||
dataSources.put(dataSourceName, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes this DataSource. If the initialConnections is greater than zero,
|
|
||||||
* that number of connections will be created. After this method is called,
|
|
||||||
* the DataSource properties cannot be changed. If you do not call this
|
|
||||||
* explicitly, it will be called the first time you get a connection from the
|
|
||||||
* DataSource.
|
|
||||||
* @throws java.sql.SQLException
|
|
||||||
* Occurs when the initialConnections is greater than zero, but the
|
|
||||||
* DataSource is not able to create enough physical connections.
|
|
||||||
*/
|
|
||||||
public void initialize() throws SQLException
|
|
||||||
{
|
|
||||||
synchronized (lock)
|
|
||||||
{
|
|
||||||
source = createConnectionPool();
|
|
||||||
source.setDatabaseName(getDatabaseName());
|
|
||||||
source.setPassword(getPassword());
|
|
||||||
source.setPortNumber(getPortNumber());
|
|
||||||
source.setServerName(getServerName());
|
|
||||||
source.setUser(getUser());
|
|
||||||
while (available.size() < initialConnections)
|
|
||||||
{
|
|
||||||
available.push(source.getPooledConnection());
|
|
||||||
}
|
|
||||||
initialized = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean isInitialized() {
|
|
||||||
return initialized;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the appropriate ConnectionPool to use for this DataSource.
|
|
||||||
*/
|
|
||||||
protected ConnectionPool createConnectionPool() {
|
|
||||||
return new ConnectionPool();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a <b>non-pooled</b> connection, unless the user and password are the
|
|
||||||
* same as the default values for this connection pool.
|
|
||||||
*
|
|
||||||
* @return A pooled connection.
|
|
||||||
* @throws SQLException
|
|
||||||
* Occurs when no pooled connection is available, and a new physical
|
|
||||||
* connection cannot be created.
|
|
||||||
*/
|
|
||||||
public Connection getConnection(String user, String password) throws SQLException
|
|
||||||
{
|
|
||||||
// If this is for the default user/password, use a pooled connection
|
|
||||||
if (user == null ||
|
|
||||||
(user.equals(getUser()) && ((password == null && getPassword() == null) || (password != null && password.equals(getPassword())))))
|
|
||||||
{
|
|
||||||
return getConnection();
|
|
||||||
}
|
|
||||||
// Otherwise, use a non-pooled connection
|
|
||||||
if (!initialized)
|
|
||||||
{
|
|
||||||
initialize();
|
|
||||||
}
|
|
||||||
return super.getConnection(user, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a connection from the connection pool.
|
|
||||||
*
|
|
||||||
* @return A pooled connection.
|
|
||||||
* @throws SQLException
|
|
||||||
* Occurs when no pooled connection is available, and a new physical
|
|
||||||
* connection cannot be created.
|
|
||||||
*/
|
|
||||||
public Connection getConnection() throws SQLException
|
|
||||||
{
|
|
||||||
if (!initialized)
|
|
||||||
{
|
|
||||||
initialize();
|
|
||||||
}
|
|
||||||
return getPooledConnection();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes this DataSource, and all the pooled connections, whether in use or not.
|
|
||||||
*/
|
|
||||||
public void close()
|
|
||||||
{
|
|
||||||
synchronized (lock)
|
|
||||||
{
|
|
||||||
while (available.size() > 0)
|
|
||||||
{
|
|
||||||
PooledConnectionImpl pci = (PooledConnectionImpl)available.pop();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
pci.close();
|
|
||||||
}
|
|
||||||
catch (SQLException e)
|
|
||||||
{}
|
|
||||||
}
|
|
||||||
available = null;
|
|
||||||
while (used.size() > 0)
|
|
||||||
{
|
|
||||||
PooledConnectionImpl pci = (PooledConnectionImpl)used.pop();
|
|
||||||
pci.removeConnectionEventListener(connectionEventListener);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
pci.close();
|
|
||||||
}
|
|
||||||
catch (SQLException e)
|
|
||||||
{}
|
|
||||||
}
|
|
||||||
used = null;
|
|
||||||
}
|
|
||||||
removeStoredDataSource();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void removeStoredDataSource() {
|
|
||||||
synchronized (dataSources)
|
|
||||||
{
|
|
||||||
dataSources.remove(dataSourceName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a connection from the pool. Will get an available one if
|
|
||||||
* present, or create a new one if under the max limit. Will
|
|
||||||
* block if all used and a new one would exceed the max.
|
|
||||||
*/
|
|
||||||
private Connection getPooledConnection() throws SQLException
|
|
||||||
{
|
|
||||||
PooledConnection pc = null;
|
|
||||||
synchronized (lock)
|
|
||||||
{
|
|
||||||
if (available == null)
|
|
||||||
{
|
|
||||||
throw new SQLException("DataSource has been closed.");
|
|
||||||
}
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
if (available.size() > 0)
|
|
||||||
{
|
|
||||||
pc = (PooledConnection)available.pop();
|
|
||||||
used.push(pc);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (maxConnections == 0 || used.size() < maxConnections)
|
|
||||||
{
|
|
||||||
pc = source.getPooledConnection();
|
|
||||||
used.push(pc);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Wake up every second at a minimum
|
|
||||||
lock.wait(1000L);
|
|
||||||
}
|
|
||||||
catch (InterruptedException e)
|
|
||||||
{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pc.addConnectionEventListener(connectionEventListener);
|
|
||||||
return pc.getConnection();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notified when a pooled connection is closed, or a fatal error occurs
|
|
||||||
* on a pooled connection. This is the only way connections are marked
|
|
||||||
* as unused.
|
|
||||||
*/
|
|
||||||
private ConnectionEventListener connectionEventListener = new ConnectionEventListener()
|
|
||||||
{
|
|
||||||
public void connectionClosed(ConnectionEvent event)
|
|
||||||
{
|
|
||||||
((PooledConnection)event.getSource()).removeConnectionEventListener(this);
|
|
||||||
synchronized (lock)
|
|
||||||
{
|
|
||||||
if (available == null)
|
|
||||||
{
|
|
||||||
return ; // DataSource has been closed
|
|
||||||
}
|
|
||||||
boolean removed = used.remove(event.getSource());
|
|
||||||
if (removed)
|
|
||||||
{
|
|
||||||
available.push(event.getSource());
|
|
||||||
// There's now a new connection available
|
|
||||||
lock.notify();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// a connection error occured
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is only called for fatal errors, where the physical connection is
|
|
||||||
* useless afterward and should be removed from the pool.
|
|
||||||
*/
|
|
||||||
public void connectionErrorOccurred(ConnectionEvent event)
|
|
||||||
{
|
|
||||||
((PooledConnection) event.getSource()).removeConnectionEventListener(this);
|
|
||||||
synchronized (lock)
|
|
||||||
{
|
|
||||||
if (available == null)
|
|
||||||
{
|
|
||||||
return ; // DataSource has been closed
|
|
||||||
}
|
|
||||||
used.remove(event.getSource());
|
|
||||||
// We're now at least 1 connection under the max
|
|
||||||
lock.notify();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds custom properties for this DataSource to the properties defined in
|
|
||||||
* the superclass.
|
|
||||||
*/
|
|
||||||
public Reference getReference() throws NamingException
|
|
||||||
{
|
|
||||||
Reference ref = super.getReference();
|
|
||||||
ref.add(new StringRefAddr("dataSourceName", dataSourceName));
|
|
||||||
if (initialConnections > 0)
|
|
||||||
{
|
|
||||||
ref.add(new StringRefAddr("initialConnections", Integer.toString(initialConnections)));
|
|
||||||
}
|
|
||||||
if (maxConnections > 0)
|
|
||||||
{
|
|
||||||
ref.add(new StringRefAddr("maxConnections", Integer.toString(maxConnections)));
|
|
||||||
}
|
|
||||||
return ref;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
package org.postgresql.jdbc2.optional;
|
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple DataSource which does not perform connection pooling. In order to use
|
|
||||||
* the DataSource, you must set the property databaseName. The settings for
|
|
||||||
* serverName, portNumber, user, and password are optional. Note: these properties
|
|
||||||
* are declared in the superclass.
|
|
||||||
*
|
|
||||||
* @author Aaron Mulder (ammulder@chariotsolutions.com)
|
|
||||||
* @version $Revision: 1.2 $
|
|
||||||
*/
|
|
||||||
public class SimpleDataSource extends BaseDataSource implements Serializable, DataSource
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Gets a description of this DataSource.
|
|
||||||
*/
|
|
||||||
public String getDescription()
|
|
||||||
{
|
|
||||||
return "Non-Pooling DataSource from " + org.postgresql.Driver.getVersion();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,194 +0,0 @@
|
|||||||
/*-------------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* BlobInputStream.java
|
|
||||||
* This is an implementation of an InputStream from a large object.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2003, PostgreSQL Global Development Group
|
|
||||||
*
|
|
||||||
* IDENTIFICATION
|
|
||||||
* $PostgreSQL: pgsql/src/interfaces/jdbc/org/postgresql/largeobject/BlobInputStream.java,v 1.6 2003/11/29 19:52:11 pgsql Exp $
|
|
||||||
*
|
|
||||||
*-------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
package org.postgresql.largeobject;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
public class BlobInputStream extends InputStream
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* The parent LargeObject
|
|
||||||
*/
|
|
||||||
private LargeObject lo;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Buffer used to improve performance
|
|
||||||
*/
|
|
||||||
private byte[] buffer;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Position within buffer
|
|
||||||
*/
|
|
||||||
private int bpos;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The buffer size
|
|
||||||
*/
|
|
||||||
private int bsize;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The mark position
|
|
||||||
*/
|
|
||||||
private int mpos = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @param lo LargeObject to read from
|
|
||||||
*/
|
|
||||||
public BlobInputStream(LargeObject lo)
|
|
||||||
{
|
|
||||||
this(lo, 1024);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @param lo LargeObject to read from
|
|
||||||
* @param bsize buffer size
|
|
||||||
*/
|
|
||||||
public BlobInputStream(LargeObject lo, int bsize)
|
|
||||||
{
|
|
||||||
this.lo = lo;
|
|
||||||
buffer = null;
|
|
||||||
bpos = 0;
|
|
||||||
this.bsize = bsize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The minimum required to implement input stream
|
|
||||||
*/
|
|
||||||
public int read() throws java.io.IOException
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (buffer == null || bpos >= buffer.length)
|
|
||||||
{
|
|
||||||
buffer = lo.read(bsize);
|
|
||||||
bpos = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle EOF
|
|
||||||
if (bpos >= buffer.length)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ret = (buffer[bpos] & 0x7F);
|
|
||||||
if ((buffer[bpos] &0x80) == 0x80)
|
|
||||||
{
|
|
||||||
ret |= 0x80;
|
|
||||||
}
|
|
||||||
|
|
||||||
bpos++;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
catch (SQLException se)
|
|
||||||
{
|
|
||||||
throw new IOException(se.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Closes this input stream and releases any system resources associated
|
|
||||||
* with the stream.
|
|
||||||
*
|
|
||||||
* <p> The <code>close</code> method of <code>InputStream</code> does
|
|
||||||
* nothing.
|
|
||||||
*
|
|
||||||
* @exception IOException if an I/O error occurs.
|
|
||||||
*/
|
|
||||||
public void close() throws IOException
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
lo.close();
|
|
||||||
lo = null;
|
|
||||||
}
|
|
||||||
catch (SQLException se)
|
|
||||||
{
|
|
||||||
throw new IOException(se.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Marks the current position in this input stream. A subsequent call to
|
|
||||||
* the <code>reset</code> method repositions this stream at the last marked
|
|
||||||
* position so that subsequent reads re-read the same bytes.
|
|
||||||
*
|
|
||||||
* <p> The <code>readlimit</code> arguments tells this input stream to
|
|
||||||
* allow that many bytes to be read before the mark position gets
|
|
||||||
* invalidated.
|
|
||||||
*
|
|
||||||
* <p> The general contract of <code>mark</code> is that, if the method
|
|
||||||
* <code>markSupported</code> returns <code>true</code>, the stream somehow
|
|
||||||
* remembers all the bytes read after the call to <code>mark</code> and
|
|
||||||
* stands ready to supply those same bytes again if and whenever the method
|
|
||||||
* <code>reset</code> is called. However, the stream is not required to
|
|
||||||
* remember any data at all if more than <code>readlimit</code> bytes are
|
|
||||||
* read from the stream before <code>reset</code> is called.
|
|
||||||
*
|
|
||||||
* <p> The <code>mark</code> method of <code>InputStream</code> does
|
|
||||||
* nothing.
|
|
||||||
*
|
|
||||||
* @param readlimit the maximum limit of bytes that can be read before
|
|
||||||
* the mark position becomes invalid.
|
|
||||||
* @see java.io.InputStream#reset()
|
|
||||||
*/
|
|
||||||
public synchronized void mark(int readlimit)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
mpos = lo.tell();
|
|
||||||
}
|
|
||||||
catch (SQLException se)
|
|
||||||
{
|
|
||||||
//throw new IOException(se.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Repositions this stream to the position at the time the
|
|
||||||
* <code>mark</code> method was last called on this input stream.
|
|
||||||
* NB: If mark is not called we move to the begining.
|
|
||||||
* @see java.io.InputStream#mark(int)
|
|
||||||
* @see java.io.IOException
|
|
||||||
*/
|
|
||||||
public synchronized void reset()
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
lo.seek(mpos);
|
|
||||||
}
|
|
||||||
catch (SQLException se)
|
|
||||||
{
|
|
||||||
throw new IOException(se.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Tests if this input stream supports the <code>mark</code> and
|
|
||||||
* <code>reset</code> methods. The <code>markSupported</code> method of
|
|
||||||
* <code>InputStream</code> returns <code>false</code>.
|
|
||||||
*
|
|
||||||
* @return <code>true</code> if this true type supports the mark and reset
|
|
||||||
* method; <code>false</code> otherwise.
|
|
||||||
* @see java.io.InputStream#mark(int)
|
|
||||||
* @see java.io.InputStream#reset()
|
|
||||||
*/
|
|
||||||
public boolean markSupported()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,148 +0,0 @@
|
|||||||
/*-------------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* BlobOutputStream.java
|
|
||||||
* This implements a basic output stream that writes to a LargeObject
|
|
||||||
*
|
|
||||||
* Copyright (c) 2003, PostgreSQL Global Development Group
|
|
||||||
*
|
|
||||||
* IDENTIFICATION
|
|
||||||
* $PostgreSQL: pgsql/src/interfaces/jdbc/org/postgresql/largeobject/BlobOutputStream.java,v 1.7 2003/11/29 19:52:11 pgsql Exp $
|
|
||||||
*
|
|
||||||
*-------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
package org.postgresql.largeobject;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
public class BlobOutputStream extends OutputStream
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* The parent LargeObject
|
|
||||||
*/
|
|
||||||
private LargeObject lo;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Buffer
|
|
||||||
*/
|
|
||||||
private byte buf[];
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Size of the buffer (default 1K)
|
|
||||||
*/
|
|
||||||
private int bsize;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Position within the buffer
|
|
||||||
*/
|
|
||||||
private int bpos;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Create an OutputStream to a large object
|
|
||||||
* @param lo LargeObject
|
|
||||||
*/
|
|
||||||
public BlobOutputStream(LargeObject lo)
|
|
||||||
{
|
|
||||||
this(lo, 1024);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Create an OutputStream to a large object
|
|
||||||
* @param lo LargeObject
|
|
||||||
* @param bsize The size of the buffer used to improve performance
|
|
||||||
*/
|
|
||||||
public BlobOutputStream(LargeObject lo, int bsize)
|
|
||||||
{
|
|
||||||
this.lo = lo;
|
|
||||||
this.bsize = bsize;
|
|
||||||
buf = new byte[bsize];
|
|
||||||
bpos = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void write(int b) throws java.io.IOException
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (bpos >= bsize)
|
|
||||||
{
|
|
||||||
lo.write(buf);
|
|
||||||
bpos = 0;
|
|
||||||
}
|
|
||||||
buf[bpos++] = (byte)b;
|
|
||||||
}
|
|
||||||
catch (SQLException se)
|
|
||||||
{
|
|
||||||
throw new IOException(se.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void write(byte[] buf, int off, int len) throws java.io.IOException
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// If we have any internally buffered data, send it first
|
|
||||||
if ( bpos > 0 )
|
|
||||||
flush();
|
|
||||||
|
|
||||||
if ( off == 0 && len == buf.length )
|
|
||||||
lo.write(buf); // save a buffer creation and copy since full buffer written
|
|
||||||
else
|
|
||||||
lo.write(buf,off,len);
|
|
||||||
}
|
|
||||||
catch (SQLException se)
|
|
||||||
{
|
|
||||||
throw new IOException(se.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Flushes this output stream and forces any buffered output bytes
|
|
||||||
* to be written out. The general contract of <code>flush</code> is
|
|
||||||
* that calling it is an indication that, if any bytes previously
|
|
||||||
* written have been buffered by the implementation of the output
|
|
||||||
* stream, such bytes should immediately be written to their
|
|
||||||
* intended destination.
|
|
||||||
*
|
|
||||||
* @exception IOException if an I/O error occurs.
|
|
||||||
*/
|
|
||||||
public void flush() throws IOException
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (bpos > 0)
|
|
||||||
lo.write(buf, 0, bpos);
|
|
||||||
bpos = 0;
|
|
||||||
}
|
|
||||||
catch (SQLException se)
|
|
||||||
{
|
|
||||||
throw new IOException(se.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Closes this output stream and releases any system resources
|
|
||||||
* associated with this stream. The general contract of <code>close</code>
|
|
||||||
* is that it closes the output stream. A closed stream cannot perform
|
|
||||||
* output operations and cannot be reopened.
|
|
||||||
* <p>
|
|
||||||
* The <code>close</code> method of <code>OutputStream</code> does nothing.
|
|
||||||
*
|
|
||||||
* @exception IOException if an I/O error occurs.
|
|
||||||
*/
|
|
||||||
public void close() throws IOException
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
flush();
|
|
||||||
lo.close();
|
|
||||||
lo = null;
|
|
||||||
}
|
|
||||||
catch (SQLException se)
|
|
||||||
{
|
|
||||||
throw new IOException(se.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,328 +0,0 @@
|
|||||||
/*-------------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* LargeObject.java
|
|
||||||
* This class implements the large object interface to org.postgresql.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2003, PostgreSQL Global Development Group
|
|
||||||
*
|
|
||||||
* IDENTIFICATION
|
|
||||||
* $PostgreSQL: pgsql/src/interfaces/jdbc/org/postgresql/largeobject/LargeObject.java,v 1.11 2003/11/29 19:52:11 pgsql Exp $
|
|
||||||
*
|
|
||||||
*-------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
package org.postgresql.largeobject;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import org.postgresql.fastpath.Fastpath;
|
|
||||||
import org.postgresql.fastpath.FastpathArg;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This class provides the basic methods required to run the interface, plus
|
|
||||||
* a pair of methods that provide InputStream and OutputStream classes
|
|
||||||
* for this object.
|
|
||||||
*
|
|
||||||
* <p>Normally, client code would use the getAsciiStream, getBinaryStream,
|
|
||||||
* or getUnicodeStream methods in ResultSet, or setAsciiStream,
|
|
||||||
* setBinaryStream, or setUnicodeStream methods in PreparedStatement to
|
|
||||||
* access Large Objects.
|
|
||||||
*
|
|
||||||
* <p>However, sometimes lower level access to Large Objects are required,
|
|
||||||
* that are not supported by the JDBC specification.
|
|
||||||
*
|
|
||||||
* <p>Refer to org.postgresql.largeobject.LargeObjectManager on how to gain access
|
|
||||||
* to a Large Object, or how to create one.
|
|
||||||
*
|
|
||||||
* @see org.postgresql.largeobject.LargeObjectManager
|
|
||||||
* @see java.sql.ResultSet#getAsciiStream
|
|
||||||
* @see java.sql.ResultSet#getBinaryStream
|
|
||||||
* @see java.sql.ResultSet#getUnicodeStream
|
|
||||||
* @see java.sql.PreparedStatement#setAsciiStream
|
|
||||||
* @see java.sql.PreparedStatement#setBinaryStream
|
|
||||||
* @see java.sql.PreparedStatement#setUnicodeStream
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class LargeObject
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Indicates a seek from the begining of a file
|
|
||||||
*/
|
|
||||||
public static final int SEEK_SET = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Indicates a seek from the current position
|
|
||||||
*/
|
|
||||||
public static final int SEEK_CUR = 1;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Indicates a seek from the end of a file
|
|
||||||
*/
|
|
||||||
public static final int SEEK_END = 2;
|
|
||||||
|
|
||||||
private Fastpath fp; // Fastpath API to use
|
|
||||||
private int oid; // OID of this object
|
|
||||||
private int fd; // the descriptor of the open large object
|
|
||||||
|
|
||||||
private BlobOutputStream os; // The current output stream
|
|
||||||
|
|
||||||
private boolean closed = false; // true when we are closed
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This opens a large object.
|
|
||||||
*
|
|
||||||
* <p>If the object does not exist, then an SQLException is thrown.
|
|
||||||
*
|
|
||||||
* @param fp FastPath API for the connection to use
|
|
||||||
* @param oid of the Large Object to open
|
|
||||||
* @param mode Mode of opening the large object
|
|
||||||
* (defined in LargeObjectManager)
|
|
||||||
* @exception SQLException if a database-access error occurs.
|
|
||||||
* @see org.postgresql.largeobject.LargeObjectManager
|
|
||||||
*/
|
|
||||||
protected LargeObject(Fastpath fp, int oid, int mode) throws SQLException
|
|
||||||
{
|
|
||||||
this.fp = fp;
|
|
||||||
this.oid = oid;
|
|
||||||
|
|
||||||
FastpathArg args[] = new FastpathArg[2];
|
|
||||||
args[0] = new FastpathArg(oid);
|
|
||||||
args[1] = new FastpathArg(mode);
|
|
||||||
this.fd = fp.getInteger("lo_open", args);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Release large object resources during garbage cleanup */
|
|
||||||
protected void finalize() throws SQLException
|
|
||||||
{
|
|
||||||
//This code used to call close() however that was problematic
|
|
||||||
//because the scope of the fd is a transaction, thus if commit
|
|
||||||
//or rollback was called before garbage collection ran then
|
|
||||||
//the call to close would error out with an invalid large object
|
|
||||||
//handle. So this method now does nothing and lets the server
|
|
||||||
//handle cleanup when it ends the transaction.
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @return the OID of this LargeObject
|
|
||||||
*/
|
|
||||||
public int getOID()
|
|
||||||
{
|
|
||||||
return oid;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This method closes the object. You must not call methods in this
|
|
||||||
* object after this is called.
|
|
||||||
* @exception SQLException if a database-access error occurs.
|
|
||||||
*/
|
|
||||||
public void close() throws SQLException
|
|
||||||
{
|
|
||||||
if (!closed)
|
|
||||||
{
|
|
||||||
// flush any open output streams
|
|
||||||
if (os != null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// we can't call os.close() otherwise we go into an infinite loop!
|
|
||||||
os.flush();
|
|
||||||
}
|
|
||||||
catch (IOException ioe)
|
|
||||||
{
|
|
||||||
throw new SQLException(ioe.getMessage());
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
os = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// finally close
|
|
||||||
FastpathArg args[] = new FastpathArg[1];
|
|
||||||
args[0] = new FastpathArg(fd);
|
|
||||||
fp.fastpath("lo_close", false, args); // true here as we dont care!!
|
|
||||||
closed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Reads some data from the object, and return as a byte[] array
|
|
||||||
*
|
|
||||||
* @param len number of bytes to read
|
|
||||||
* @return byte[] array containing data read
|
|
||||||
* @exception SQLException if a database-access error occurs.
|
|
||||||
*/
|
|
||||||
public byte[] read(int len) throws SQLException
|
|
||||||
{
|
|
||||||
// This is the original method, where the entire block (len bytes)
|
|
||||||
// is retrieved in one go.
|
|
||||||
FastpathArg args[] = new FastpathArg[2];
|
|
||||||
args[0] = new FastpathArg(fd);
|
|
||||||
args[1] = new FastpathArg(len);
|
|
||||||
return fp.getData("loread", args);
|
|
||||||
|
|
||||||
// This version allows us to break this down into 4k blocks
|
|
||||||
//if (len<=4048) {
|
|
||||||
//// handle as before, return the whole block in one go
|
|
||||||
//FastpathArg args[] = new FastpathArg[2];
|
|
||||||
//args[0] = new FastpathArg(fd);
|
|
||||||
//args[1] = new FastpathArg(len);
|
|
||||||
//return fp.getData("loread",args);
|
|
||||||
//} else {
|
|
||||||
//// return in 4k blocks
|
|
||||||
//byte[] buf=new byte[len];
|
|
||||||
//int off=0;
|
|
||||||
//while (len>0) {
|
|
||||||
//int bs=4048;
|
|
||||||
//len-=bs;
|
|
||||||
//if (len<0) {
|
|
||||||
//bs+=len;
|
|
||||||
//len=0;
|
|
||||||
//}
|
|
||||||
//read(buf,off,bs);
|
|
||||||
//off+=bs;
|
|
||||||
//}
|
|
||||||
//return buf;
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Reads some data from the object into an existing array
|
|
||||||
*
|
|
||||||
* @param buf destination array
|
|
||||||
* @param off offset within array
|
|
||||||
* @param len number of bytes to read
|
|
||||||
* @return the number of bytes actually read
|
|
||||||
* @exception SQLException if a database-access error occurs.
|
|
||||||
*/
|
|
||||||
public int read(byte buf[], int off, int len) throws SQLException
|
|
||||||
{
|
|
||||||
byte b[] = read(len);
|
|
||||||
if (b.length < len)
|
|
||||||
len = b.length;
|
|
||||||
System.arraycopy(b, 0, buf, off, len);
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Writes an array to the object
|
|
||||||
*
|
|
||||||
* @param buf array to write
|
|
||||||
* @exception SQLException if a database-access error occurs.
|
|
||||||
*/
|
|
||||||
public void write(byte buf[]) throws SQLException
|
|
||||||
{
|
|
||||||
FastpathArg args[] = new FastpathArg[2];
|
|
||||||
args[0] = new FastpathArg(fd);
|
|
||||||
args[1] = new FastpathArg(buf);
|
|
||||||
fp.fastpath("lowrite", false, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Writes some data from an array to the object
|
|
||||||
*
|
|
||||||
* @param buf destination array
|
|
||||||
* @param off offset within array
|
|
||||||
* @param len number of bytes to write
|
|
||||||
* @exception SQLException if a database-access error occurs.
|
|
||||||
*/
|
|
||||||
public void write(byte buf[], int off, int len) throws SQLException
|
|
||||||
{
|
|
||||||
byte data[] = new byte[len];
|
|
||||||
System.arraycopy(buf, off, data, 0, len);
|
|
||||||
write(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Sets the current position within the object.
|
|
||||||
*
|
|
||||||
* <p>This is similar to the fseek() call in the standard C library. It
|
|
||||||
* allows you to have random access to the large object.
|
|
||||||
*
|
|
||||||
* @param pos position within object
|
|
||||||
* @param ref Either SEEK_SET, SEEK_CUR or SEEK_END
|
|
||||||
* @exception SQLException if a database-access error occurs.
|
|
||||||
*/
|
|
||||||
public void seek(int pos, int ref) throws SQLException
|
|
||||||
{
|
|
||||||
FastpathArg args[] = new FastpathArg[3];
|
|
||||||
args[0] = new FastpathArg(fd);
|
|
||||||
args[1] = new FastpathArg(pos);
|
|
||||||
args[2] = new FastpathArg(ref);
|
|
||||||
fp.fastpath("lo_lseek", false, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Sets the current position within the object.
|
|
||||||
*
|
|
||||||
* <p>This is similar to the fseek() call in the standard C library. It
|
|
||||||
* allows you to have random access to the large object.
|
|
||||||
*
|
|
||||||
* @param pos position within object from begining
|
|
||||||
* @exception SQLException if a database-access error occurs.
|
|
||||||
*/
|
|
||||||
public void seek(int pos) throws SQLException
|
|
||||||
{
|
|
||||||
seek(pos, SEEK_SET);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @return the current position within the object
|
|
||||||
* @exception SQLException if a database-access error occurs.
|
|
||||||
*/
|
|
||||||
public int tell() throws SQLException
|
|
||||||
{
|
|
||||||
FastpathArg args[] = new FastpathArg[1];
|
|
||||||
args[0] = new FastpathArg(fd);
|
|
||||||
return fp.getInteger("lo_tell", args);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This method is inefficient, as the only way to find out the size of
|
|
||||||
* the object is to seek to the end, record the current position, then
|
|
||||||
* return to the original position.
|
|
||||||
*
|
|
||||||
* <p>A better method will be found in the future.
|
|
||||||
*
|
|
||||||
* @return the size of the large object
|
|
||||||
* @exception SQLException if a database-access error occurs.
|
|
||||||
*/
|
|
||||||
public int size() throws SQLException
|
|
||||||
{
|
|
||||||
int cp = tell();
|
|
||||||
seek(0, SEEK_END);
|
|
||||||
int sz = tell();
|
|
||||||
seek(cp, SEEK_SET);
|
|
||||||
return sz;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns an InputStream from this object.
|
|
||||||
*
|
|
||||||
* <p>This InputStream can then be used in any method that requires an
|
|
||||||
* InputStream.
|
|
||||||
*
|
|
||||||
* @exception SQLException if a database-access error occurs.
|
|
||||||
*/
|
|
||||||
public InputStream getInputStream() throws SQLException
|
|
||||||
{
|
|
||||||
return new BlobInputStream(this, 4096);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns an OutputStream to this object
|
|
||||||
*
|
|
||||||
* <p>This OutputStream can then be used in any method that requires an
|
|
||||||
* OutputStream.
|
|
||||||
*
|
|
||||||
* @exception SQLException if a database-access error occurs.
|
|
||||||
*/
|
|
||||||
public OutputStream getOutputStream() throws SQLException
|
|
||||||
{
|
|
||||||
if (os == null)
|
|
||||||
os = new BlobOutputStream(this, 4096);
|
|
||||||
return os;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,229 +0,0 @@
|
|||||||
/*-------------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* LargeObjectManager.java
|
|
||||||
* This class implements the large object interface to org.postgresql.
|
|
||||||
*
|
|
||||||
* It provides methods that allow client code to create, open and delete
|
|
||||||
* large objects from the database. When opening an object, an instance of
|
|
||||||
* org.postgresql.largeobject.LargeObject is returned, and its methods
|
|
||||||
* then allow access to the object.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2003, PostgreSQL Global Development Group
|
|
||||||
*
|
|
||||||
* IDENTIFICATION
|
|
||||||
* $PostgreSQL: pgsql/src/interfaces/jdbc/org/postgresql/largeobject/LargeObjectManager.java,v 1.12 2003/12/17 15:38:42 davec Exp $
|
|
||||||
*
|
|
||||||
*-------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
package org.postgresql.largeobject;
|
|
||||||
|
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import org.postgresql.Driver;
|
|
||||||
import org.postgresql.core.BaseConnection;
|
|
||||||
import org.postgresql.fastpath.Fastpath;
|
|
||||||
import org.postgresql.fastpath.FastpathArg;
|
|
||||||
import org.postgresql.util.PSQLException;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This class implements the large object interface to org.postgresql.
|
|
||||||
*
|
|
||||||
* <p>It provides methods that allow client code to create, open and delete
|
|
||||||
* large objects from the database. When opening an object, an instance of
|
|
||||||
* org.postgresql.largeobject.LargeObject is returned, and its methods then allow
|
|
||||||
* access to the object.
|
|
||||||
*
|
|
||||||
* <p>This class can only be created by org.postgresql.Connection
|
|
||||||
*
|
|
||||||
* <p>To get access to this class, use the following segment of code:
|
|
||||||
* <br><pre>
|
|
||||||
* import org.postgresql.largeobject.*;
|
|
||||||
*
|
|
||||||
* Connection conn;
|
|
||||||
* LargeObjectManager lobj;
|
|
||||||
*
|
|
||||||
* ... code that opens a connection ...
|
|
||||||
*
|
|
||||||
* lobj = ((org.postgresql.PGConnection)myconn).getLargeObjectAPI();
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* <p>Normally, client code would use the getAsciiStream, getBinaryStream,
|
|
||||||
* or getUnicodeStream methods in ResultSet, or setAsciiStream,
|
|
||||||
* setBinaryStream, or setUnicodeStream methods in PreparedStatement to
|
|
||||||
* access Large Objects.
|
|
||||||
*
|
|
||||||
* <p>However, sometimes lower level access to Large Objects are required,
|
|
||||||
* that are not supported by the JDBC specification.
|
|
||||||
*
|
|
||||||
* <p>Refer to org.postgresql.largeobject.LargeObject on how to manipulate the
|
|
||||||
* contents of a Large Object.
|
|
||||||
*
|
|
||||||
* @see java.sql.ResultSet#getAsciiStream
|
|
||||||
* @see java.sql.ResultSet#getBinaryStream
|
|
||||||
* @see java.sql.ResultSet#getUnicodeStream
|
|
||||||
* @see java.sql.PreparedStatement#setAsciiStream
|
|
||||||
* @see java.sql.PreparedStatement#setBinaryStream
|
|
||||||
* @see java.sql.PreparedStatement#setUnicodeStream
|
|
||||||
*/
|
|
||||||
public class LargeObjectManager
|
|
||||||
{
|
|
||||||
// the fastpath api for this connection
|
|
||||||
private Fastpath fp;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This mode indicates we want to write to an object
|
|
||||||
*/
|
|
||||||
public static final int WRITE = 0x00020000;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This mode indicates we want to read an object
|
|
||||||
*/
|
|
||||||
public static final int READ = 0x00040000;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This mode is the default. It indicates we want read and write access to
|
|
||||||
* a large object
|
|
||||||
*/
|
|
||||||
public static final int READWRITE = READ | WRITE;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This prevents us being created by mere mortals
|
|
||||||
*/
|
|
||||||
private LargeObjectManager()
|
|
||||||
{}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Constructs the LargeObject API.
|
|
||||||
*
|
|
||||||
* <p><b>Important Notice</b>
|
|
||||||
* <br>This method should only be called by org.postgresql.Connection
|
|
||||||
*
|
|
||||||
* <p>There should only be one LargeObjectManager per Connection. The
|
|
||||||
* org.postgresql.Connection class keeps track of the various extension API's
|
|
||||||
* and it's advised you use those to gain access, and not going direct.
|
|
||||||
*/
|
|
||||||
public LargeObjectManager(BaseConnection conn) throws SQLException
|
|
||||||
{
|
|
||||||
// We need Fastpath to do anything
|
|
||||||
this.fp = conn.getFastpathAPI();
|
|
||||||
|
|
||||||
// Now get the function oid's for the api
|
|
||||||
//
|
|
||||||
// This is an example of Fastpath.addFunctions();
|
|
||||||
//
|
|
||||||
String sql;
|
|
||||||
if (conn.getMetaData().supportsSchemasInTableDefinitions()) {
|
|
||||||
sql = "SELECT p.proname,p.oid "+
|
|
||||||
" FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n "+
|
|
||||||
" WHERE p.pronamespace=n.oid AND n.nspname='pg_catalog' AND (";
|
|
||||||
} else {
|
|
||||||
sql = "SELECT proname,oid FROM pg_proc WHERE ";
|
|
||||||
}
|
|
||||||
sql += " proname = 'lo_open'" +
|
|
||||||
" or proname = 'lo_close'" +
|
|
||||||
" or proname = 'lo_creat'" +
|
|
||||||
" or proname = 'lo_unlink'" +
|
|
||||||
" or proname = 'lo_lseek'" +
|
|
||||||
" or proname = 'lo_tell'" +
|
|
||||||
" or proname = 'loread'" +
|
|
||||||
" or proname = 'lowrite'";
|
|
||||||
|
|
||||||
if (conn.getMetaData().supportsSchemasInTableDefinitions()) {
|
|
||||||
sql += ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
ResultSet res = conn.createStatement().executeQuery(sql);
|
|
||||||
|
|
||||||
if (res == null)
|
|
||||||
throw new PSQLException("postgresql.lo.init");
|
|
||||||
|
|
||||||
fp.addFunctions(res);
|
|
||||||
res.close();
|
|
||||||
if (Driver.logDebug)
|
|
||||||
Driver.debug("Large Object initialised");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This opens an existing large object, based on its OID. This method
|
|
||||||
* assumes that READ and WRITE access is required (the default).
|
|
||||||
*
|
|
||||||
* @param oid of large object
|
|
||||||
* @return LargeObject instance providing access to the object
|
|
||||||
* @exception SQLException on error
|
|
||||||
*/
|
|
||||||
public LargeObject open(int oid) throws SQLException
|
|
||||||
{
|
|
||||||
return new LargeObject(fp, oid, READWRITE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This opens an existing large object, based on its OID
|
|
||||||
*
|
|
||||||
* @param oid of large object
|
|
||||||
* @param mode mode of open
|
|
||||||
* @return LargeObject instance providing access to the object
|
|
||||||
* @exception SQLException on error
|
|
||||||
*/
|
|
||||||
public LargeObject open(int oid, int mode) throws SQLException
|
|
||||||
{
|
|
||||||
return new LargeObject(fp, oid, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This creates a large object, returning its OID.
|
|
||||||
*
|
|
||||||
* <p>It defaults to READWRITE for the new object's attributes.
|
|
||||||
*
|
|
||||||
* @return oid of new object
|
|
||||||
* @exception SQLException on error
|
|
||||||
*/
|
|
||||||
public int create() throws SQLException
|
|
||||||
{
|
|
||||||
FastpathArg args[] = new FastpathArg[1];
|
|
||||||
args[0] = new FastpathArg(READWRITE);
|
|
||||||
return fp.getInteger("lo_creat", args);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This creates a large object, returning its OID
|
|
||||||
*
|
|
||||||
* @param mode a bitmask describing different attributes of the new object
|
|
||||||
* @return oid of new object
|
|
||||||
* @exception SQLException on error
|
|
||||||
*/
|
|
||||||
public int create(int mode) throws SQLException
|
|
||||||
{
|
|
||||||
FastpathArg args[] = new FastpathArg[1];
|
|
||||||
args[0] = new FastpathArg(mode);
|
|
||||||
return fp.getInteger("lo_creat", args);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This deletes a large object.
|
|
||||||
*
|
|
||||||
* @param oid describing object to delete
|
|
||||||
* @exception SQLException on error
|
|
||||||
*/
|
|
||||||
public void delete(int oid) throws SQLException
|
|
||||||
{
|
|
||||||
FastpathArg args[] = new FastpathArg[1];
|
|
||||||
args[0] = new FastpathArg(oid);
|
|
||||||
fp.fastpath("lo_unlink", false, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This deletes a large object.
|
|
||||||
*
|
|
||||||
* <p>It is identical to the delete method, and is supplied as the C API uses
|
|
||||||
* unlink.
|
|
||||||
*
|
|
||||||
* @param oid describing object to delete
|
|
||||||
* @exception SQLException on error
|
|
||||||
*/
|
|
||||||
public void unlink(int oid) throws SQLException
|
|
||||||
{
|
|
||||||
delete(oid);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user