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 }