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    }