package org.ovirt.engine.core.utils.db;
import java.io.PrintWriter;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.Statement;
import javax.sql.DataSource;
import org.ovirt.engine.core.utils.EngineLocalConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class StandaloneDataSource implements DataSource {
// The log:
private static final Logger log = LoggerFactory.getLogger(StandaloneDataSource.class);
// Names of the properties expected in the engine configuration file:
private static final String ENGINE_DB_DRIVER = "ENGINE_DB_DRIVER";
private static final String ENGINE_DB_URL = "ENGINE_DB_URL";
private static final String ENGINE_DB_USER = "ENGINE_DB_USER";
private static final String ENGINE_DB_PASSWORD = "ENGINE_DB_PASSWORD";
// The database connection details loaded from the configuration file:
private String driver;
private String url;
private String user;
private String password;
// We open and reuse a single connection and we wrap it so that is not
// closed when the client calls the close method:
private Connection connection;
private Connection wrapper;
public StandaloneDataSource() throws SQLException {
// Load the service configuration file:
EngineLocalConfig config = EngineLocalConfig.getInstance();
// Get the database connection details:
driver = config.getProperty(ENGINE_DB_DRIVER);
url = config.getProperty(ENGINE_DB_URL);
user = config.getProperty(ENGINE_DB_USER);
password = config.getProperty(ENGINE_DB_PASSWORD);
// Load the driver:
try {
Class.forName(driver);
}
catch (ClassNotFoundException exception) {
throw new SQLException("Failed to load database driver \"" + driver + "\".", exception);
}
// Open the connection:
openConnection();
// Register a shutdown hook to close the connection when finishing:
Runtime.getRuntime().addShutdownHook(
new Thread() {
@Override
public void run() {
closeConnection();
}
}
);
}
private void openConnection () throws SQLException {
// Open the connection:
connection = DriverManager.getConnection(url, user, password);
// Wrap the connection so that it is not closed when the client
// calls the close method:
wrapper = (Connection) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class<?>[] { Connection.class },
(proxy, method, args) -> {
if (method.getName().equals("close")) {
return null;
}
return method.invoke(connection, args);
}
);
}
private void closeConnection () {
try {
if (connection != null) {
connection.close();
}
}
catch (SQLException exception) {
log.error("Can't close connection.", exception);
}
connection = null;
wrapper = null;
}
/**
* test the connection by executing a simple query..
*
* Note: assuming connection is not null.
*
* @throws SQLException if a database access error occurs
* or this method is called on a closed connection.
*/
private void checkConnection() throws SQLException {
try (Statement statement = connection.createStatement();
ResultSet rs = statement.executeQuery("select null")) {
rs.next();
}
}
@Override
public Connection getConnection () throws SQLException {
try {
if (connection == null) {
openConnection();
}
checkConnection();
}
catch (SQLException exception) {
log.warn("Can't check the connection, will close it and open a new one.", exception);
closeConnection();
openConnection();
}
return wrapper;
}
@Override
public Connection getConnection (String user, String password) throws SQLException {
throw new SQLFeatureNotSupportedException();
}
@Override
public PrintWriter getLogWriter () throws SQLException {
throw new SQLFeatureNotSupportedException();
}
@Override
public void setLogWriter (PrintWriter out) throws SQLException {
throw new SQLFeatureNotSupportedException();
}
@Override
public void setLoginTimeout (int seconds) throws SQLException {
throw new SQLFeatureNotSupportedException();
}
@Override
public int getLoginTimeout () throws SQLException {
throw new SQLFeatureNotSupportedException();
}
@Override
public <T> T unwrap (Class<T> iface) throws SQLException {
throw new SQLFeatureNotSupportedException();
}
@Override
public boolean isWrapperFor (Class<?> iface) throws SQLException {
throw new SQLFeatureNotSupportedException();
}
public java.util.logging.Logger getParentLogger () throws SQLFeatureNotSupportedException {
throw new SQLFeatureNotSupportedException();
}
}