/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.components.database.common.jdbc.internal;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLNonTransientConnectionException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Logger;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.osgi.framework.BundleContext;
import de.rcenvironment.components.database.common.jdbc.JDBCDriverInformation;
import de.rcenvironment.components.database.common.jdbc.JDBCDriverService;
import de.rcenvironment.core.configuration.ConfigurationService;
import de.rcenvironment.core.configuration.ConfigurationService.ConfigurablePathListId;
import de.rcenvironment.core.utils.common.StringUtils;
/**
* Implementation of {@link JDBCDriverService}.
*
* @author Doreen Seider
* @author Oliver Seebach
*/
public class JDBCDriverServiceImpl implements JDBCDriverService {
private static final String ERROR_MESSAGE = "Failed to register JDBC driver (file: %s, driver class: %s): %s";
private static final Log LOGGER = LogFactory.getLog(JDBCDriverServiceImpl.class);
private static Map<String, JDBCDriverInformation> driverClassNamesWithInfo = new HashMap<>();
private final Map<String, URLClassLoader> classLoaders = new HashMap<>();
private ConfigurationService configurationService;
private Set<JDBCDriverInformation> driverInformations = new HashSet<>();
private List<String> registeredDrivers = new ArrayList<>();
// see http://www.devx.com/tips/Tip/28818 for list -- seeb_ol, November 2015
static {
driverClassNamesWithInfo.put("com.mysql.jdbc.Driver",
new JDBCDriverInformationImpl("mysql", "MySQL"));
driverClassNamesWithInfo.put("org.postgresql.Driver",
new JDBCDriverInformationImpl("postgresql", "PostgreSQL"));
driverClassNamesWithInfo.put("COM.ibm.db2.jdbc.app.DB2Driver",
new JDBCDriverInformationImpl("db2", "IBM DB2L"));
driverClassNamesWithInfo.put("sun.jdbc.odbc.JdbcOdbcDriver",
new JDBCDriverInformationImpl("odbc", "JDBC-ODBC Bridge"));
driverClassNamesWithInfo.put("weblogic.jdbc.mssqlserver4.Driver",
new JDBCDriverInformationImpl("weblogic:mssqlserver4", "Microsoft SQL Server"));
driverClassNamesWithInfo.put("oracle.jdbc.driver.OracleDriver",
new JDBCDriverInformationImpl("oracle:thin", "Oracle Thin"));
driverClassNamesWithInfo.put("com.pointbase.jdbc.jdbcUniversalDriver",
new JDBCDriverInformationImpl("pointbase://embedded[", "PointBase Embedded Server"));
driverClassNamesWithInfo.put("COM.cloudscape.core.JDBCDriver",
new JDBCDriverInformationImpl("cloudscape", "Cloudscape"));
driverClassNamesWithInfo.put("RmiJdbc.RJDriver",
new JDBCDriverInformationImpl("rmi", "Cloudscape RMI"));
driverClassNamesWithInfo.put("org.firebirdsql.jdbc.FBDriver",
new JDBCDriverInformationImpl("firebirdsql", "Firebird (JCA/JDBC Driver)"));
driverClassNamesWithInfo.put("ids.sql.IDSDriver",
new JDBCDriverInformationImpl("ids", "IDS Server"));
driverClassNamesWithInfo.put("com.informix.jdbc.IfxDriver",
new JDBCDriverInformationImpl("informix-sqli", "Informix Dynamic Server"));
driverClassNamesWithInfo.put("jdbc.idbDriver",
new JDBCDriverInformationImpl("idb", "InstantDB (v3.13 and earlier)"));
driverClassNamesWithInfo.put("org.enhydra.instantdb.jdbc.idbDriver",
new JDBCDriverInformationImpl("idb", "InstantDB (v3.14 and later)"));
driverClassNamesWithInfo.put("interbase.interclient.Driver",
new JDBCDriverInformationImpl("interbase", "Interbase (InterClient Driver)"));
driverClassNamesWithInfo.put("hSql.hDriver",
new JDBCDriverInformationImpl("HypersonicSQL", "Hypersonic SQL (v1.2 and earlier)"));
driverClassNamesWithInfo.put("org.hsql.jdbcDriver",
new JDBCDriverInformationImpl("HypersonicSQL", "Hypersonic SQL (v1.3 and later)"));
driverClassNamesWithInfo.put("com.ashna.jturbo.driver.Driver",
new JDBCDriverInformationImpl("JTurbo", "Microsoft SQL Server (JTurbo Driver)"));
driverClassNamesWithInfo.put("com.inet.tds.TdsDriver",
new JDBCDriverInformationImpl("inetdae", "Microsoft SQL Server (Sprinta Driver)"));
driverClassNamesWithInfo.put("com.microsoft.sqlserver.jdbc.SQLServerDriver",
new JDBCDriverInformationImpl("microsoft:sqlserver", "Microsoft SQL Server 2000 (Microsoft Driver)"));
driverClassNamesWithInfo.put("oracle.jdbc.driver.OracleDriver",
new JDBCDriverInformationImpl("oracle:oci", "Oracle OCI 9i"));
driverClassNamesWithInfo.put("com.sybase.jdbc.SybDriver",
new JDBCDriverInformationImpl("sybase:Tds", "Sybase (jConnect 4.2 and earlier)"));
driverClassNamesWithInfo.put("com.sybase.jdbc2.jdbc.SybDriver",
new JDBCDriverInformationImpl("sybase:Tds", "Sybase (jConnect 5.2)"));
}
/**
* Activate method.
*
* @param context The BundleContext
*/
public void activate(BundleContext context) {
registerJDBCDrivers();
}
/**
* OSGi-DS life cycle method.
*/
public void deactivate() {
for (String classloaderName : classLoaders.keySet()) {
URLClassLoader classLoader = classLoaders.get(classloaderName);
if (classLoader != null) {
try {
classLoader.close();
LOGGER.debug("Closed classloader for " + classloaderName);
} catch (IOException e) {
LOGGER.error("Failed to close classloader for " + classloaderName + ". Reason: " + e.getMessage());
}
}
}
}
@Override
public Set<JDBCDriverInformation> getRegisteredJDBCDrivers() {
return Collections.unmodifiableSet(driverInformations);
}
/**
* Bind configuration service method.
*
* @param service The service to bind.
*/
public void bindConfigurationService(ConfigurationService service) {
configurationService = service;
}
private void loadDriverIfNotLoadedAlready(File driverFile, String driverClassName) {
// TODO improve check if driver already loaded -- seeb_ol, November 2015
if (!registeredDrivers.contains(driverClassName)) {
loadDriver(driverFile, driverClassName);
}
}
private void registerJDBCDrivers() {
for (File dir : configurationService.getConfigurablePathList(ConfigurablePathListId.JDBC_DRIVER_DIRS)) {
for (File file : dir.listFiles()) {
// TODO improve filter and detection mechanism -- seeb_ol, November 2015
if (file.getName().contains("mysql")) {
String scheme = "com.mysql.jdbc.Driver";
loadDriverIfNotLoadedAlready(file, scheme);
} else if (file.getName().contains("postgresql")) {
String scheme = "org.postgresql.Driver";
loadDriverIfNotLoadedAlready(file, scheme);
}
}
}
}
private void loadDriver(File driverFile, String driverClassName) {
if (!driverFile.exists() || !driverFile.isFile() || !driverFile.canRead()) {
LOGGER
.error(StringUtils.format(ERROR_MESSAGE, driverClassName, driverFile.getAbsolutePath(), "file not found or not readable"));
return;
}
URL[] urls;
try {
urls = new URL[] { driverFile.toURI().toURL() };
} catch (MalformedURLException e) {
LOGGER.error(StringUtils.format(ERROR_MESSAGE, driverClassName, driverFile.getAbsolutePath(), e.getMessage()));
return;
}
try {
URLClassLoader classLoader = new URLClassLoader(urls);
final Class<?> driverClass = Class.forName(driverClassName, true, classLoader);
final Driver driver = (Driver) driverClass.newInstance();
DriverManager.registerDriver(new DriverAdapter(driver));
registeredDrivers.add(driverClassName);
classLoaders.put(driverClassName, classLoader);
LOGGER.info("Successfully registered JDBC driver from file: " + driverFile.getAbsolutePath());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | SQLException e) {
LOGGER.error(StringUtils.format(ERROR_MESSAGE, driverClassName, driverFile.getAbsolutePath(), e.getMessage()));
return;
}
driverInformations.add(driverClassNamesWithInfo.get(driverClassName));
}
@Override
public Connection getConnectionWithCredentials(String url, String databaseUser, String databasePassword)
throws SQLException {
try {
Connection connection = DriverManager.getConnection(url, databaseUser, databasePassword);
return connection;
} catch (SQLNonTransientConnectionException e) {
throw new SQLException("Failed to establish connection; " + e.getCause().getMessage());
}
}
@Override
public Driver getDriverForURL(String url) throws SQLException {
return DriverManager.getDriver(url);
}
/**
* A driver adapter to forward requests to another {@link Driver} implementation.
*
* @author Christian Weiss
*/
private class DriverAdapter implements Driver {
private final Driver driver;
DriverAdapter(final Driver driver) {
this.driver = driver;
}
@Override
public Connection connect(String url, Properties info) throws SQLException {
return driver.connect(url, info);
}
@Override
public boolean acceptsURL(String url) throws SQLException {
return driver.acceptsURL(url);
}
@Override
public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException {
return driver.getPropertyInfo(url, info);
}
@Override
public int getMajorVersion() {
return driver.getMajorVersion();
}
@Override
public int getMinorVersion() {
return driver.getMinorVersion();
}
@Override
public boolean jdbcCompliant() {
return driver.jdbcCompliant();
}
/**
* This method is necessary for Java 7 compatibility. It MUST NOT have an @Override annotation, otherwise it will break on Java 6.
* -- misc_ro
*
* @see java.sql.Driver#getParentLogger()
*
* @return nothing; this method will always throw an exception (see parent JavaDoc)
* @throws SQLFeatureNotSupportedException always thrown to signal that java.util.logging is not used
*/
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
// see java.sql.Driver#getParentLogger()
throw new SQLFeatureNotSupportedException("Not using java.util.logging");
}
}
}