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.1 $
 */
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;
    }

    public Reference getReference() throws NamingException {
        Reference ref = new Reference(getClass().getName(), PGObjectFactory.class.getName(), null);
        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;
    }

}
