/*****************************************************************************
* Copyright (C) 2008 EnterpriseDB Corporation.
* Copyright (C) 2011 Stado Global Development Group.
*
* This file is part of Stado.
*
* Stado is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Stado is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Stado. If not, see <http://www.gnu.org/licenses/>.
*
* You can find Stado at http://www.stado.us
*
****************************************************************************/
package org.postgresql.driver.ds.common;
import javax.naming.*;
import java.sql.*;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
/**
* Base class for data sources and related classes.
*
* @author Aaron Mulder (ammulder@chariotsolutions.com)
*/
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.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;
// 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 = 0;
private int prepareThreshold = 5;
private int unknownLength = Integer.MAX_VALUE;
private int loginTimeout = 0; // in seconds
private int socketTimeout = 0; // in seconds
private boolean ssl = false;
private String sslfactory;
private boolean tcpKeepAlive = false;
private String compatible;
private int logLevel = 0;
private int protocolVersion = 0;
/**
* 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
* DataSource 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;
}
}
/**
* @return the login timeout, in seconds.
*/
public int getLoginTimeout() throws SQLException
{
return loginTimeout;
}
/**
* Set the login timeout, in seconds.
*/
public void setLoginTimeout(int i) throws SQLException
{
this.loginTimeout = i;
}
/**
* 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;
}
}
public String getCompatible()
{
return compatible;
}
public void setCompatible(String compatible)
{
this.compatible = compatible;
}
public int getLogLevel()
{
return logLevel;
}
public void setLogLevel(int logLevel)
{
this.logLevel = logLevel;
}
public int getProtocolVersion()
{
return protocolVersion;
}
public void setProtocolVersion(int protocolversion)
{
this.protocolVersion = protocolVersion;
}
/**
* 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;
}
/**
* Sets the default threshold for enabling server-side prepare.
* See {@link org.postgresql.driver.PGConnection#setPrepareThreshold(int)} for details.
*
* @param count the number of times a statement object must be reused before server-side
* prepare is enabled.
*/
public void setPrepareThreshold(int count)
{
this.prepareThreshold = count;
}
/**
* Gets the default threshold for enabling server-side prepare.
*
* @see #setPrepareThreshold(int)
*/
public int getPrepareThreshold()
{
return prepareThreshold;
}
public void setUnknownLength(int unknownLength)
{
this.unknownLength = unknownLength;
}
public int getUnknownLength()
{
return unknownLength;
}
/**
* Sets the socket timeout (SOTimeout), in seconds
*/
public void setSocketTimeout(int seconds)
{
this.socketTimeout = seconds;
}
/**
* @return the socket timeout (SOTimeout), in seconds
*/
public int getSocketTimeout()
{
return this.socketTimeout;
}
/**
* Set whether the connection will be SSL encrypted or not.
*
* @param enabled if <CODE>true</CODE>, connect with SSL.
*/
public void setSsl(boolean enabled)
{
this.ssl = enabled;
}
/**
* Gets SSL encryption setting.
*
* @return <CODE>true</CODE> if connections will be encrypted with SSL.
*/
public boolean getSsl()
{
return this.ssl;
}
/**
* Set the name of the {@link javax.net.ssl.SSLSocketFactory} to use for connections.
* Use <CODE>org.postgresql.driver.ssl.NonValidatingFactory</CODE> if you don't want certificate validation.
*
* @param classname name of a subclass of <CODE>javax.net.ssl.SSLSocketFactory</CODE> or <CODE>null</CODE> for the default implementation.
*/
public void setSslfactory(String classname)
{
this.sslfactory = classname;
}
/**
* Gets the name of the {@link javax.net.ssl.SSLSocketFactory} used for connections.
*
* @return name of the class or <CODE>null</CODE> if the default implementation is used.
*/
public String getSslfactory()
{
return this.sslfactory;
}
public void setTcpKeepAlive(boolean enabled)
{
tcpKeepAlive = enabled;
}
public boolean getTcpKeepAlive()
{
return tcpKeepAlive;
}
/**
* Generates a DriverManager URL from the other properties supplied.
*/
private String getUrl()
{
StringBuffer sb = new StringBuffer(100);
sb.append("jdbc:postgresql://");
sb.append(serverName);
if (portNumber != 0) {
sb.append(":").append(portNumber);
}
sb.append("/").append(databaseName);
sb.append("?loginTimeout=").append(loginTimeout);
sb.append("&socketTimeout=").append(socketTimeout);
sb.append("&prepareThreshold=").append(prepareThreshold);
sb.append("&unknownLength=").append(unknownLength);
sb.append("&loglevel=").append(logLevel);
if (protocolVersion != 0) {
sb.append("&protocolVersion=").append(protocolVersion);
}
if (ssl) {
sb.append("&ssl=true");
if (sslfactory != null) {
sb.append("&sslfactory=").append(sslfactory);
}
}
sb.append("&tcpkeepalive=").append(tcpKeepAlive);
if (compatible != null) {
sb.append("&compatible="+compatible);
}
return sb.toString();
}
/**
* Generates a reference using the appropriate 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));
}
ref.add(new StringRefAddr("prepareThreshold", Integer.toString(prepareThreshold)));
ref.add(new StringRefAddr("unknownLength", Integer.toString(unknownLength)));
ref.add(new StringRefAddr("loginTimeout", Integer.toString(loginTimeout)));
ref.add(new StringRefAddr("socketTimeout", Integer.toString(socketTimeout)));
ref.add(new StringRefAddr("ssl", Boolean.toString(ssl)));
ref.add(new StringRefAddr("sslfactory", sslfactory));
ref.add(new StringRefAddr("tcpKeepAlive", Boolean.toString(tcpKeepAlive)));
if (compatible != null)
{
ref.add(new StringRefAddr("compatible", compatible));
}
ref.add(new StringRefAddr("logLevel", Integer.toString(logLevel)));
ref.add(new StringRefAddr("protocolVersion", Integer.toString(protocolVersion)));
return ref;
}
protected void writeBaseObject(ObjectOutputStream out) throws IOException
{
out.writeObject(serverName);
out.writeObject(databaseName);
out.writeObject(user);
out.writeObject(password);
out.writeInt(portNumber);
out.writeInt(prepareThreshold);
out.writeInt(unknownLength);
out.writeInt(loginTimeout);
out.writeInt(socketTimeout);
out.writeBoolean(ssl);
out.writeObject(sslfactory);
out.writeBoolean(tcpKeepAlive);
out.writeObject(compatible);
out.writeInt(logLevel);
out.writeInt(protocolVersion);
}
protected void readBaseObject(ObjectInputStream in) throws IOException, ClassNotFoundException
{
serverName = (String)in.readObject();
databaseName = (String)in.readObject();
user = (String)in.readObject();
password = (String)in.readObject();
portNumber = in.readInt();
prepareThreshold = in.readInt();
unknownLength = in.readInt();
loginTimeout = in.readInt();
socketTimeout = in.readInt();
ssl = in.readBoolean();
sslfactory = (String)in.readObject();
tcpKeepAlive = in.readBoolean();
compatible = (String)in.readObject();
logLevel = in.readInt();
protocolVersion = in.readInt();
}
public void initializeFrom(BaseDataSource source) throws IOException, ClassNotFoundException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
source.writeBaseObject(oos);
oos.close();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
readBaseObject(ois);
}
}