/** * Redistribution and use of this software and associated documentation * ("Software"), with or without modification, are permitted provided * that the following conditions are met: * * 1. Redistributions of source code must retain copyright * statements and notices. Redistributions must also contain a * copy of this document. * * 2. Redistributions in binary form must reproduce the * above copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other * materials provided with the distribution. * * 3. The name "Exolab" must not be used to endorse or promote * products derived from this Software without prior written * permission of Exoffice Technologies. For written permission, * please contact info@exolab.org. * * 4. Products derived from this Software may not be called "Exolab" * nor may "Exolab" appear in their names without prior written * permission of Exoffice Technologies. Exolab is a registered * trademark of Exoffice Technologies. * * 5. Due credit should be given to the Exolab Project * (http://www.exolab.org/). * * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * * Copyright 1999 (C) Exoffice Technologies Inc. All Rights Reserved. * * $Id: PostgresqlDataSource.java,v 1.2 2000/11/10 22:06:26 momjian Exp $ */ package org.postgresql; import java.io.PrintWriter; import java.io.Serializable; import java.util.Properties; import java.util.Hashtable; import java.sql.Connection; import java.sql.SQLException; import java.sql.DriverManager; import java.rmi.Remote; import javax.sql.DataSource; import javax.naming.Referenceable; import javax.naming.Reference; import javax.naming.StringRefAddr; import javax.naming.RefAddr; import javax.naming.Context; import javax.naming.Name; import javax.naming.NamingException; import javax.naming.spi.ObjectFactory; // FIXME //import postgresql.util.PSQLException; //import postgresql.xa.XADataSourceImpl; import org.postgresql.util.PSQLException; import org.postgresql.xa.XADataSourceImpl; //--------- /** * Implements a JDBC 2.0 {@link javax.sql.DataSource} for the * PostgreSQL driver with JNDI persistance support. XA and pooled * connection support is also available, but the application must * used the designated DataSource interface to obtain them. *

* The supported data source properties are: *

 * description         (optional)
 * databaseName        (required)
 * loginTimeout        (optional)
 * user                (optional)
 * password            (optional)
 * serverName          (optional)
 * portNumber          (optional)
 * transactionTimeout  (optional for XA connections)
 * 
* This data source may be serialized and stored in a JNDI * directory. Example of how to create a new data source and * register it with JNDI: *
 * PostgresqlDataSource ds;
 * InitialContext       ctx;
 *
 * ds = new PostgresqlDataSource();
 * ds.setDatabaseName( "test" );
 * ds.setUser( "me" );
 * ds.setPassword( "secret" );
 * ctx = new InitialContext();
 * ctx.rebind( "/comp/jdbc/test", ds );
 * 
* Example for obtaining the data source from JNDI and * opening a new connections: *
 * InitialContext       ctx;
 * DataSource           ds;
 * 
 * ctx = new InitialContext();
 * ds = (DataSource) ctx.lookup( "/comp/jdbc/test" );
 * ds.getConnection();
 * 
* * * @author Assaf Arkin * @version 1.0 * @see XADataSourceImpl * @see DataSource * @see Connection */ public class PostgresqlDataSource extends XADataSourceImpl implements DataSource, Referenceable, ObjectFactory, Serializable { /** * Holds the timeout for opening a new connection, specified * in seconds. The default is obtained from the JDBC driver. */ private int _loginTimeout; /** * Holds the user's account name. */ private String _user; /** * Holds the database password. */ private String _password; /** * Holds the name of the particular database on the server. */ private String _databaseName; /** * Description of this datasource. */ private String _description = "PostgreSQL DataSource"; /** * Holds the database server name. If null, this is * assumed to be the localhost. */ private String _serverName; /** * Holds the port number where a server is listening. * The default value will open a connection with an * unspecified port. */ private int _portNumber = DEFAULT_PORT; /** * The default port number. Since we open the connection * without specifying the port if it's the default one, * this value can be meaningless. */ private static final int DEFAULT_PORT = 0; /** * Holds the log writer to which all messages should be * printed. The default writer is obtained from the driver * manager, but it can be specified at the datasource level * and will be passed to the driver. May be null. */ private transient PrintWriter _logWriter; /** * Each datasource maintains it's own driver, in case of * driver-specific setup (e.g. pools, log writer). */ // FIXME // private transient postgresql.Driver _driver; private transient org.postgresql.Driver _driver; //--------- public PostgresqlDataSource() { _logWriter = DriverManager.getLogWriter(); _loginTimeout = DriverManager.getLoginTimeout(); } public Connection getConnection() throws SQLException { // Uses the username and password specified for the datasource. return getConnection( _user, _password ); } public synchronized Connection getConnection( String user, String password ) throws SQLException { Connection conn; Properties info; String url; if ( _driver == null ) { try { // Constructs a driver for use just by this data source // which will produce TwoPhaseConnection-s. This driver // is not registered with the driver manager. // FIXME // _driver = new postgresql.Driver(); _driver = new org.postgresql.Driver(); //----------- //FIXME // _driver.setLogWriter( _logWriter ); // Method seems to be unavailable. Just commented it out. //---------- } catch ( SQLException except ) { if ( _logWriter != null ) _logWriter.println( "DataSource: Failed to initialize JDBC driver: " + except ); throw except; } } // Use info to supply properties that are not in the URL. info = new Properties(); info.put( "loginTimeout", Integer.toString( _loginTimeout ) ); // DriverManager will do that and not rely on the URL alone. if ( user == null ) { user = _user; password = _password; } if ( user == null || password == null ) throw new PSQLException( "postgresql.ds.userpswd" ); info.put( "user", user ); info.put( "password", password ); if ( _serverName != null ) info.put( "PGHOST", _serverName ); if ( _portNumber != DEFAULT_PORT ) info.put( "PGPORT", Integer.toString( _portNumber ) ); if ( _databaseName != null ) info.put( "PGDBNAME", _databaseName ); // Construct the URL suitable for this driver. url = "jdbc:postgresql:"; // Attempt to establish a connection. Report a successful // attempt or a failure. try { conn = _driver.connect( url, info ); // FIXME // if ( ! ( conn instanceof postgresql.jdbc2.Connection ) ) { if ( ! ( conn instanceof org.postgresql.jdbc2.Connection ) ) { //-------- if ( _logWriter != null ) _logWriter.println( "DataSource: JDBC 1 connections not supported" ); throw new PSQLException( "postgresql.ds.onlyjdbc2" ); } } catch ( SQLException except ) { if ( _logWriter != null ) _logWriter.println( "DataSource: getConnection failed " + except ); throw except; } if ( conn != null && _logWriter != null ) _logWriter.println( "DataSource: getConnection returning " + conn ); return conn; } public PrintWriter getLogWriter() { return _logWriter; } public synchronized void setLogWriter( PrintWriter writer ) { // Once a log writer has been set, we cannot set it since some // thread might be conditionally accessing it right now without // synchronizing. if ( writer != null ) { if ( _driver != null ) // FIXME // _driver.setLogWriter( writer ); // Method seems to be unavailable. Commented it out. //---------- _logWriter = writer; } } public void setLoginTimeout( int seconds ) { _loginTimeout = seconds; } public synchronized int getLoginTimeout() { return _loginTimeout; } /** * Sets the name of the particular database on the server. * The standard name for this property is databaseName. * * @param databaseName The name of the particular database on the server */ public synchronized void setDatabaseName( String databaseName ) { if ( databaseName == null ) throw new NullPointerException( "DataSource: Argument 'databaseName' is null" ); _databaseName = databaseName; } /** * Returns the name of the particular database on the server. * The standard name for this property is databaseName. * * @return The name of the particular database on the server */ public String getDatabaseName() { return _databaseName; } /** * Sets the description of this datasource. * The standard name for this property is description. * * @param description The description of this datasource */ public synchronized void setDescription( String description ) { if ( description == null ) throw new NullPointerException( "DataSource: Argument 'description' is null" ); _description = description; } /** * Returns the description of this datasource. * The standard name for this property is description. * * @return The description of this datasource */ public String getDescription() { return _description; } /** * Sets the database password. * The standard name for this property is password. * * @param password The database password */ public synchronized void setPassword( String password ) { _password = password; } /** * Returns the database password. * The standard name for this property is password. * * @return The database password */ public String getPassword() { return _password; } /** * Sets the port number where a server is listening. * The standard name for this property is portNumber. * * @param portNumber The port number where a server is listening */ public synchronized void setPortNumber( int portNumber ) { _portNumber = portNumber; } /** * Returns the port number where a server is listening. * The standard name for this property is portNumber. * * @return The port number where a server is listening */ public int getPortNumber() { return _portNumber; } /** * Sets the database server name. * The standard name for this property is serverName. * * @param serverName The database server name */ public synchronized void setServerName( String serverName ) { _serverName = serverName; } /** * Returns the database server name. * The standard name for this property is serverName. * * @return The database server name */ public String getServerName() { return _serverName; } /** * Sets the user's account name. * The standard name for this property is user. * * @param user The user's account name */ public synchronized void setUser( String user ) { _user = user; } /** * Returns the user's account name. * The standard name for this property is user. * * @return The user's account name */ public String getUser() { return _user; } /** * Returns true if this datasource and the other are equal. * The two datasources are equal if and only if they will produce * the exact same connections. Connection properties like database * name, user name, etc are comapred. Setup properties like * description, log writer, etc are not compared. */ public synchronized boolean equals( Object other ) { if ( other == this ) return true; if ( other == null || ! ( other instanceof PostgresqlDataSource ) ) return false; PostgresqlDataSource with; with = (PostgresqlDataSource) other; if ( _databaseName != null && _databaseName.equals( with._databaseName ) ) if ( _portNumber == with._portNumber && ( ( _serverName == null && with._serverName == null ) || ( _serverName != null && _serverName.equals( with._serverName ) ) ) ) if ( ( _user == null && with._user == null ) || ( _user != null && _password != null && _user.equals( with._user ) && _password.equals( with._password ) ) ) return true; return false; } public String toString() { if ( _description != null ) return _description; else { String url; url = "jdbc:postgresql:"; if ( _serverName != null ) { if ( _portNumber == DEFAULT_PORT ) url = url + "//" + _serverName + "/"; else url = url + "//" + _serverName + ":" + _portNumber + "/"; } else if ( _portNumber != DEFAULT_PORT ) url = url + "//localhost:" + _portNumber + "/"; if ( _databaseName != null ) url = url + _databaseName; return "DataSource " + url; } } public synchronized Reference getReference() { Reference ref; // We use same object as factory. ref = new Reference( getClass().getName(), getClass().getName(), null ); // Mandatory properties ref.add( new StringRefAddr( "description", _description ) ); ref.add( new StringRefAddr( "databaseName", _databaseName ) ); ref.add( new StringRefAddr( "loginTimeout", Integer.toString( _loginTimeout ) ) ); // Optional properties if ( _user != null ) ref.add( new StringRefAddr( "user", _user ) ); if ( _password != null ) ref.add( new StringRefAddr( "password", _password ) ); if ( _serverName != null ) ref.add( new StringRefAddr( "serverName", _serverName ) ); if ( _portNumber != DEFAULT_PORT ) ref.add( new StringRefAddr( "portNumber", Integer.toString( _portNumber ) ) ); ref.add( new StringRefAddr( "transactionTimeout", Integer.toString( getTransactionTimeout() ) ) ); return ref; } public Object getObjectInstance( Object refObj, Name name, Context nameCtx, Hashtable env ) throws NamingException { Reference ref; // Can only reconstruct from a reference. if ( refObj instanceof Reference ) { ref = (Reference) refObj; // Make sure reference is of datasource class. if ( ref.getClassName().equals( getClass().getName() ) ) { PostgresqlDataSource ds; RefAddr addr; try { ds = (PostgresqlDataSource) Class.forName( ref.getClassName() ).newInstance(); } catch ( Exception except ) { throw new NamingException( except.toString() ); } // Mandatory properties ds._description = (String) ref.get( "description" ).getContent(); ds._databaseName = (String) ref.get( "databaseName" ).getContent(); ds._loginTimeout = Integer.parseInt( (String) ref.get( "loginTimeout" ).getContent() ); // Optional properties addr = ref.get( "user" ); if ( addr != null ) ds._user = (String) addr.getContent(); addr = ref.get( "password" ); if ( addr != null ) ds._password = (String) addr.getContent(); addr = ref.get( "serverName" ); if ( addr != null ) ds._serverName = (String) addr.getContent(); addr = ref.get( "portNumber" ); if ( addr != null ) ds._portNumber = Integer.parseInt( (String) addr.getContent() ); addr = ref.get( "transactionTimeout" ); if ( addr != null ) setTransactionTimeout( Integer.parseInt( (String) addr.getContent() ) ); return ds; } else throw new NamingException( "DataSource: Reference not constructed from class " + getClass().getName() ); } else if ( refObj instanceof Remote ) return refObj; else return null; } }