001    /*
002     * Sonar, open source software quality management tool.
003     * Copyright (C) 2008-2011 SonarSource
004     * mailto:contact AT sonarsource DOT com
005     *
006     * Sonar is free software; you can redistribute it and/or
007     * modify it under the terms of the GNU Lesser General Public
008     * License as published by the Free Software Foundation; either
009     * version 3 of the License, or (at your option) any later version.
010     *
011     * Sonar is distributed in the hope that it will be useful,
012     * but WITHOUT ANY WARRANTY; without even the implied warranty of
013     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014     * Lesser General Public License for more details.
015     *
016     * You should have received a copy of the GNU Lesser General Public
017     * License along with Sonar; if not, write to the Free Software
018     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
019     */
020    package org.sonar.jpa.session;
021    
022    import org.apache.commons.configuration.Configuration;
023    import org.apache.commons.lang.StringUtils;
024    import org.sonar.api.database.DatabaseProperties;
025    
026    import java.sql.Connection;
027    import java.sql.Driver;
028    import java.sql.DriverManager;
029    import java.sql.SQLException;
030    import java.util.Properties;
031    
032    public class DriverDatabaseConnector extends AbstractDatabaseConnector {
033    
034      private ClassLoader classloader;
035    
036      public DriverDatabaseConnector(Configuration configuration) {
037        super(configuration, true);
038        this.classloader = getClass().getClassLoader();
039      }
040    
041      public DriverDatabaseConnector(Configuration configuration, ClassLoader classloader) {
042        super(configuration, true);
043        this.classloader = classloader;
044      }
045    
046      public String getDriver() {
047        String driver = getConfiguration().getString(DatabaseProperties.PROP_DRIVER);
048        if (driver == null) {
049          driver = getConfiguration().getString(DatabaseProperties.PROP_DRIVER_DEPRECATED);
050        }
051        if (driver == null) {
052          driver = DatabaseProperties.PROP_DRIVER_DEFAULT_VALUE;
053        }
054        return driver;
055      }
056    
057      public String getUrl() {
058        return getConfiguration().getString(DatabaseProperties.PROP_URL, DatabaseProperties.PROP_URL_DEFAULT_VALUE);
059      }
060    
061      public String getUsername() {
062        String username = getConfiguration().getString(DatabaseProperties.PROP_USER);
063        if (username == null) {
064          username = getConfiguration().getString(DatabaseProperties.PROP_USER_DEPRECATED);
065        }
066        if (username == null) {
067          username = DatabaseProperties.PROP_USER_DEFAULT_VALUE;
068        }
069        return username;
070      }
071    
072      public String getPassword() {
073        return getConfiguration().getString(DatabaseProperties.PROP_PASSWORD, DatabaseProperties.PROP_PASSWORD_DEFAULT_VALUE);
074      }
075    
076      public Connection getConnection() throws SQLException {
077        try {
078          /*
079            The sonar batch downloads the JDBC driver in a separated classloader.
080            This is a well-know problem of java.sql.DriverManager. The workaround
081            is to use a proxy.
082            See http://stackoverflow.com/questions/288828/how-to-use-a-jdbc-driver-from-an-arbitrary-location
083           */
084          Driver driver = (Driver)classloader.loadClass(getDriver()).newInstance();
085          DriverManager.registerDriver(new DriverProxy(driver));
086    
087        } catch (Exception e) {
088          SQLException ex = new SQLException("SQL driver not found " + getDriver());
089          ex.initCause(e);
090          throw ex;
091        }
092        return DriverManager.getConnection(getUrl(), getUsername(), getPassword());
093      }
094    
095      @Override
096      public void setupEntityManagerFactory(Properties factoryProps) {
097        factoryProps.put("hibernate.connection.url", getUrl());
098        factoryProps.put("hibernate.connection.driver_class", getDriver());
099        factoryProps.put("hibernate.connection.username", getUsername());
100        if (StringUtils.isNotEmpty(getPassword())) {
101          factoryProps.put("hibernate.connection.password", getPassword());
102        }
103      }
104    }
105    
106    /**
107     * A Driver that stands in for another Driver.
108     * This is necessary because java.sql.DriverManager
109     * examines the Driver class class loader.
110     */
111    final class DriverProxy implements Driver {
112      private final Driver target;
113    
114      DriverProxy(Driver target) {
115        if (target == null) {
116          throw new NullPointerException();
117        }
118        this.target = target;
119      }
120    
121      public Driver getTarget() {
122        return target;
123      }
124    
125      public boolean acceptsURL(String url) throws SQLException {
126        return target.acceptsURL(url);
127      }
128    
129      public Connection connect(
130          String url, Properties info
131      ) throws SQLException {
132        return target.connect(url, info);
133      }
134    
135      public int getMajorVersion() {
136        return target.getMajorVersion();
137      }
138    
139      public int getMinorVersion() {
140        return target.getMinorVersion();
141      }
142    
143      public java.sql.DriverPropertyInfo[] getPropertyInfo(
144          String url, Properties info
145      ) throws SQLException {
146        return target.getPropertyInfo(url, info);
147      }
148    
149      public boolean jdbcCompliant() {
150        return target.jdbcCompliant();
151      }
152    
153      @Override
154      public String toString() {
155        return "Proxy: " + target;
156      }
157    
158      @Override
159      public int hashCode() {
160        return target.hashCode();
161      }
162    
163      @Override
164      public boolean equals(Object obj) {
165        if (!(obj instanceof org.sonar.jpa.session.DriverProxy)) {
166          return false;
167        }
168        org.sonar.jpa.session.DriverProxy other = (org.sonar.jpa.session.DriverProxy) obj;
169        return this.target.equals(other.target);
170      }
171    }