/*
* Constellation - An open source and standard compliant SDI
* http://www.constellation-sdi.org
*
* Copyright 2014 Geomatys.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.constellation.generic.database;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.sis.util.logging.Logging;
import org.geotoolkit.internal.sql.DefaultDataSource;
import org.geotoolkit.jdbc.DBCPDataSource;
import org.geotoolkit.jdbc.WrappedDataSource;
import org.postgresql.ds.PGSimpleDataSource;
import org.postgresql.ds.common.BaseDataSource;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.DataSource;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import java.lang.reflect.UndeclaredThrowableException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author Guilhem Legal
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "BDD")
public class BDD {
private static final Logger LOGGER = Logging.getLogger("org.constellation.generic.database");
public static final String POSTGRES_DRIVER_CLASS = "org.postgresql.Driver";
public static final String ORACLE_DRIVER_CLASS = "oracle.jdbc.driver.OracleDriver";
/**
* The className of the driver
*/
private String className;
/**
* The url to connect the database
*/
private String connectURL;
/**
* The username connecting the database
*/
private String user;
/**
* The password of the user
*/
private String password;
/**
* The database schema.
*/
private String schema;
private boolean sharedConnection = false;
/**
* Constructor used by JAXB
*/
public BDD() {
}
public BDD(final BDD that) {
this.className = that.className;
this.connectURL = that.connectURL;
this.password = that.password;
this.schema = that.schema;
this.sharedConnection = that.sharedConnection;
this.user = that.user;
}
/**
* Build a new DataSource informations.
*
* @param className the type of the driver (such as "org.postgresql.Driver").
* @param connectURL the url of the database.
* @param user The user name.
* @param password The password.
*/
public BDD(String className, String connectURL, String user, String password) {
this.className = className;
this.connectURL = connectURL;
this.password = password;
this.user = user;
}
/**
* Return the type of the driver (such as "org.postgresql.Driver").
* @return
*/
public String getClassName() {
return className;
}
/**
* Return the url of the database
* @return
*/
public String getConnectURL() {
return connectURL;
}
/**
* Return The user name.
* @return
*/
public String getUser() {
return user;
}
/**
* return the passsword of the user.
* @return
*/
public String getPassword() {
return password;
}
/**
* extract the host name from the database url.
* @return
*/
public String getHostName() {
if (connectURL != null && connectURL.indexOf("://") != -1) {
String hostName = connectURL.substring(connectURL.indexOf("://") + 3);
if (hostName.indexOf(':') != -1) {
hostName = hostName.substring(0, hostName.indexOf(':'));
return hostName;
} else {
return null;
}
}
return null;
}
/**
* extract the database name from the database url.
* @return
*/
public String getDatabaseName() {
if (connectURL != null && connectURL.lastIndexOf('/') != -1) {
return connectURL.substring(connectURL.lastIndexOf('/') + 1);
}
return null;
}
/**
* extract the port number from the database url or 5432 if its not present.
* @return
*/
public int getPortNumber() {
if (connectURL != null && connectURL.lastIndexOf(':') != -1) {
String portName = connectURL.substring(connectURL.lastIndexOf(':') + 1);
if (portName.indexOf('/') != -1) {
portName = portName.substring(0, portName.indexOf('/'));
try {
return Integer.parseInt(portName);
} catch (NumberFormatException ex) {
LOGGER.log(Level.WARNING, "unable to parse the port number: {0} using default", portName);
return 5432;
}
} else {
return 5432;
}
}
return 5432;
}
/**
* @param className the className to set
*/
public void setClassName(final String className) {
this.className = className;
}
/**
* @param connectURL the connectURL to set
*/
public void setConnectURL(final String connectURL) {
this.connectURL = connectURL;
}
/**
* @param user the user to set
*/
public void setUser(final String user) {
this.user = user;
}
/**
* @param password the password to set
*/
public void setPassword(final String password) {
this.password = password;
}
/**
* @return the schema
*/
public String getSchema() {
return schema;
}
/**
* @param schema the schema to set
*/
public void setSchema(String schema) {
this.schema = schema;
}
/**
* @return the sharedConnection
*/
public boolean isSharedConnection() {
return sharedConnection;
}
/**
* @param sharedConnection the sharedConnection to set
*/
public void setSharedConnection(boolean sharedConnection) {
this.sharedConnection = sharedConnection;
}
/**
* Creates a data source for the given classname using the reflection API.
* This avoid direct dependency to a driver that we can not redistribute.
*/
private Object createDataSourceByReflection(final String classname) throws SQLException {
try {
final Class<?> c = Class.forName(classname);
final Object source = c.newInstance();
c.getMethod("setURL", String.class).invoke(source, connectURL);
c.getMethod("setUser", String.class).invoke(source, user);
c.getMethod("setPassword", String.class).invoke(source, password);
return source;
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
final Throwable cause = e.getCause();
if (cause instanceof SQLException) {
throw (SQLException) cause;
}
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
throw new UndeclaredThrowableException(e);
}
}
/**
* Return a new connection to the database.
*
* @return
* @throws java.sql.SQLException
*/
public DataSource getDataSource() throws SQLException {
final DataSource source;
// by Default we use the postgres driver.
if (className == null) {
className = POSTGRES_DRIVER_CLASS;
}
switch (className) {
case POSTGRES_DRIVER_CLASS:
if (connectURL != null && connectURL.startsWith("jdbc:postgresql://")) {
final PGSimpleDataSource pgSource = new PGSimpleDataSource();
fillSourceFromURL(pgSource);
source = pgSource;
} else {
return null;
}
break;
case ORACLE_DRIVER_CLASS:
source = (DataSource) createDataSourceByReflection("oracle.jdbc.pool.OracleDataSource");
break;
default:
source = new DefaultDataSource(connectURL);
break;
}
return source;
}
/**
* Return a new connection to the database.
*
* @return
* @throws java.sql.SQLException
*/
public DataSource getPooledDataSource() {
final DataSource source;
// by Default we use the postgres driver.
if (className == null) {
className = POSTGRES_DRIVER_CLASS;
}
switch (className) {
case POSTGRES_DRIVER_CLASS:
if (connectURL != null && connectURL.startsWith("jdbc:postgresql://")) {
//final PGConnectionPoolDataSource pgSource = new PGConnectionPoolDataSource();
final BasicDataSource dataSource = new BasicDataSource();
// some default data source behaviour
dataSource.setPoolPreparedStatements(true);
// driver
dataSource.setDriverClassName(POSTGRES_DRIVER_CLASS);
// url
dataSource.setUrl(connectURL);
// username
dataSource.setUsername(user);
// password
if (password != null) {
dataSource.setPassword(password);
}
/* max wait
final Integer maxWait = (Integer) params.parameter(MAXWAIT.getName().toString()).getValue();
if (maxWait != null && maxWait != -1) {
dataSource.setMaxWait(maxWait * 1000);
}
// connection pooling options
final Integer minConn = (Integer) params.parameter(MINCONN.getName().toString()).getValue();
if ( minConn != null ) {
dataSource.setMinIdle(minConn);
}
final Integer maxConn = (Integer) params.parameter(MAXCONN.getName().toString()).getValue();
if ( maxConn != null ) {
dataSource.setMaxActive(maxConn);
}
final Boolean validate = (Boolean) params.parameter(VALIDATECONN.getName().toString()).getValue();
if(validate != null && validate && getValidationQuery() != null) {
dataSource.setTestOnBorrow(true);
dataSource.setValidationQuery(getValidationQuery());
}*/
// some datastores might need this
dataSource.setAccessToUnderlyingConnectionAllowed(true);
return new DBCPDataSource(dataSource);
} else {
return null;
}
case ORACLE_DRIVER_CLASS:
try {
source = new WrappedDataSource((ConnectionPoolDataSource)
createDataSourceByReflection("oracle.jdbc.pool.OracleConnectionPoolDataSource"));
} catch (SQLException ex) {
LOGGER.log(Level.SEVERE, "SQLException while creating oracle datasource", ex);
return null;
}
break;
default:
source = new DefaultDataSource(connectURL);
break;
}
return source;
}
/**
* Fill the dataSource supplied with the informations extracted from the database URL.
* @param pgSource
*/
private void fillSourceFromURL(final BaseDataSource pgSource) {
// exemple : jdbc:postgresql://localhost:5432/mdweb-SML
String url = connectURL.substring(18);
final String host;
final int port;
if (url.indexOf(':') != -1) {
host = url.substring(0, url.indexOf(':'));
url = url.substring(url.indexOf(':') + 1);
final String sPort = url.substring(0, url.indexOf('/'));
port = Integer.parseInt(sPort);
} else {
host = url.substring(0, url.indexOf('/'));
port = 5432;
LOGGER.finer("Using default postgres post 5432");
}
final String dbName = url.substring(url.indexOf('/') + 1);
pgSource.setServerName(host);
pgSource.setPortNumber(port);
pgSource.setDatabaseName(dbName);
pgSource.setUser(user);
pgSource.setPassword(password);
}
/**
* Return a new connection to the database.
*
* @return
* @throws java.sql.SQLException
*
* @todo The call to Class.forName(...) is not needed anymore since Java 6 and should be removed.
*/
public Connection getFreshConnection() throws SQLException {
// by Default we use the postgres driver.
if (className == null) {
className = POSTGRES_DRIVER_CLASS;
}
try {
Class.forName(className);
} catch (ClassNotFoundException e) {
// Non-fatal exception, ignore. If there is really a problem, the
// following line is expected to throw the appropriate SQLException.
}
return DriverManager.getConnection(connectURL, user, password);
}
public boolean isPostgres() {
if (className == null) {
className = POSTGRES_DRIVER_CLASS;
}
return className.equals(POSTGRES_DRIVER_CLASS);
}
@Override
public String toString() {
final StringBuilder s = new StringBuilder("[BDD]");
s.append("className: ").append(className).append('\n');
s.append("connectURL: ").append(connectURL).append('\n');
s.append("user: ").append(user).append('\n');
s.append("password: ").append(password).append('\n');
return s.toString();
}
/**
* Verify if this entry is identical to the specified object.
*/
@Override
public boolean equals(final Object object) {
if (object == this) {
return true;
}
if (object instanceof BDD) {
final BDD that = (BDD) object;
return Objects.equals(this.className, that.className) &&
Objects.equals(this.connectURL, that.connectURL) &&
Objects.equals(this.user , that.user) &&
Objects.equals(this.schema , that.schema) &&
Objects.equals(this.password, that.password);
}
return false;
}
@Override
public int hashCode() {
int hash = 5;
hash = 59 * hash + (this.className != null ? this.className.hashCode() : 0);
hash = 59 * hash + (this.connectURL != null ? this.connectURL.hashCode() : 0);
hash = 59 * hash + (this.user != null ? this.user.hashCode() : 0);
hash = 59 * hash + (this.password != null ? this.password.hashCode() : 0);
hash = 59 * hash + (this.schema != null ? this.schema.hashCode() : 0);
return hash;
}
}