001 package org.sonar.jpa.session;
002
003 import org.apache.commons.configuration.Configuration;
004 import org.apache.commons.lang.StringUtils;
005 import org.sonar.api.database.DatabaseProperties;
006
007 import java.sql.Connection;
008 import java.sql.Driver;
009 import java.sql.DriverManager;
010 import java.sql.SQLException;
011 import java.util.Properties;
012
013 public class DriverDatabaseConnector extends AbstractDatabaseConnector {
014
015 private ClassLoader classloader;
016
017 public DriverDatabaseConnector(Configuration configuration) {
018 super(configuration, true);
019 this.classloader = getClass().getClassLoader();
020 }
021
022 public DriverDatabaseConnector(Configuration configuration, ClassLoader classloader) {
023 super(configuration, true);
024 this.classloader = classloader;
025 }
026
027 public String getDriver() {
028 String driver = getConfiguration().getString(DatabaseProperties.PROP_DRIVER);
029 if (driver == null) {
030 driver = getConfiguration().getString(DatabaseProperties.PROP_DRIVER_DEPRECATED);
031 }
032 if (driver == null) {
033 driver = DatabaseProperties.PROP_DRIVER_DEFAULT_VALUE;
034 }
035 return driver;
036 }
037
038 public String getUrl() {
039 return getConfiguration().getString(DatabaseProperties.PROP_URL, DatabaseProperties.PROP_URL_DEFAULT_VALUE);
040 }
041
042 public String getUsername() {
043 String username = getConfiguration().getString(DatabaseProperties.PROP_USER);
044 if (username == null) {
045 username = getConfiguration().getString(DatabaseProperties.PROP_USER_DEPRECATED);
046 }
047 if (username == null) {
048 username = DatabaseProperties.PROP_USER_DEFAULT_VALUE;
049 }
050 return username;
051 }
052
053 public String getPassword() {
054 return getConfiguration().getString(DatabaseProperties.PROP_PASSWORD, DatabaseProperties.PROP_PASSWORD_DEFAULT_VALUE);
055 }
056
057 public Connection getConnection() throws SQLException {
058 try {
059 /*
060 The sonar batch downloads the JDBC driver in a separated classloader.
061 This is a well-know problem of java.sql.DriverManager. The workaround
062 is to use a proxy.
063 See http://stackoverflow.com/questions/288828/how-to-use-a-jdbc-driver-from-an-arbitrary-location
064 */
065 Driver driver = (Driver)classloader.loadClass(getDriver()).newInstance();
066 DriverManager.registerDriver(new DriverProxy(driver));
067
068 } catch (Exception e) {
069 SQLException ex = new SQLException("SQL driver not found " + getDriver());
070 ex.initCause(e);
071 throw ex;
072 }
073 return DriverManager.getConnection(getUrl(), getUsername(), getPassword());
074 }
075
076 @Override
077 public void setupEntityManagerFactory(Properties factoryProps) {
078 factoryProps.put("hibernate.connection.url", getUrl());
079 factoryProps.put("hibernate.connection.driver_class", getDriver());
080 factoryProps.put("hibernate.connection.username", getUsername());
081 if (StringUtils.isNotEmpty(getPassword())) {
082 factoryProps.put("hibernate.connection.password", getPassword());
083 }
084 }
085 }
086
087 /**
088 * A Driver that stands in for another Driver.
089 * This is necessary because java.sql.DriverManager
090 * examines the Driver class class loader.
091 */
092 final class DriverProxy implements Driver {
093 private final Driver target;
094
095 DriverProxy(Driver target) {
096 if (target == null) {
097 throw new NullPointerException();
098 }
099 this.target = target;
100 }
101
102 public Driver getTarget() {
103 return target;
104 }
105
106 public boolean acceptsURL(String url) throws SQLException {
107 return target.acceptsURL(url);
108 }
109
110 public Connection connect(
111 String url, Properties info
112 ) throws SQLException {
113 return target.connect(url, info);
114 }
115
116 public int getMajorVersion() {
117 return target.getMajorVersion();
118 }
119
120 public int getMinorVersion() {
121 return target.getMinorVersion();
122 }
123
124 public java.sql.DriverPropertyInfo[] getPropertyInfo(
125 String url, Properties info
126 ) throws SQLException {
127 return target.getPropertyInfo(url, info);
128 }
129
130 public boolean jdbcCompliant() {
131 return target.jdbcCompliant();
132 }
133
134 @Override
135 public String toString() {
136 return "Proxy: " + target;
137 }
138
139 @Override
140 public int hashCode() {
141 return target.hashCode();
142 }
143
144 @Override
145 public boolean equals(Object obj) {
146 if (!(obj instanceof org.sonar.jpa.session.DriverProxy)) {
147 return false;
148 }
149 org.sonar.jpa.session.DriverProxy other = (org.sonar.jpa.session.DriverProxy) obj;
150 return this.target.equals(other.target);
151 }
152 }