JDBC driver can be loaded from external location using URLClassLoader

Signed-off-by: Toivo Adams <toivo.adams@gmail.com>
Signed-off-by: Mark Payne <markap14@hotmail.com>
This commit is contained in:
Toivo Adams 2015-03-14 12:34:40 +02:00 committed by Mark Payne
parent 864e0996ca
commit 1682d62d7f
3 changed files with 113 additions and 19 deletions

View File

@ -20,6 +20,8 @@ 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.SQLException;
import java.util.ArrayList;
import java.util.Collections;
@ -184,7 +186,7 @@ public class DBCPConnectionPool extends AbstractControllerService implements DBC
// Optional driver URL, when exist, this URL will be used to locate driver jar file location
String urlString = context.getProperty(DB_DRIVER_JAR_URL).getValue();
dataSource.setDriverClassLoader( getDriverClassLoader(urlString) );
dataSource.setDriverClassLoader( getDriverClassLoader(urlString, drv) );
String dburl = dbsystem.buildUrl(host, port, dbname);
@ -210,14 +212,27 @@ public class DBCPConnectionPool extends AbstractControllerService implements DBC
* using Thread.currentThread().getContextClassLoader();
* will ensure that you are using the ClassLoader for you NAR.
* @throws InitializationException
*/
protected ClassLoader getDriverClassLoader(String urlString) throws InitializationException {
*/
protected ClassLoader getDriverClassLoader(String urlString, String drvName) throws InitializationException {
if (urlString!=null && urlString.length()>0) {
try {
URL[] urls = new URL[] { new URL(urlString) };
return new URLClassLoader(urls);
URLClassLoader ucl = new URLClassLoader(urls);
// Workaround which allows to use URLClassLoader for JDBC driver loading.
// (Because the DriverManager will refuse to use a driver not loaded by the system ClassLoader.)
Class<?> clazz = Class.forName(drvName, true, ucl);
if (clazz==null)
throw new InitializationException("Can't load Database Driver " + drvName);
Driver driver = (Driver) clazz.newInstance();
DriverManager.registerDriver( new DriverShim(driver) );
return ucl;
} catch (MalformedURLException e) {
throw new InitializationException("Invalid Database Driver Jar Url", e);
} catch (Exception e) {
throw new InitializationException("Can't load Database Driver", e);
}
}
else

View File

@ -0,0 +1,74 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.dbcp;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Properties;
import java.util.logging.Logger;
/**
* Workaround which allows to use URLClassLoader for JDBC driver loading.
* (Because the DriverManager will refuse to use a driver not loaded by the system ClassLoader.)
*
*/
class DriverShim implements Driver {
private Driver driver;
DriverShim(Driver d) {
this.driver = d;
}
@Override
public boolean acceptsURL(String u) throws SQLException {
return this.driver.acceptsURL(u);
}
@Override
public Connection connect(String u, Properties p) throws SQLException {
return this.driver.connect(u, p);
}
@Override
public int getMajorVersion() {
return this.driver.getMajorVersion();
}
@Override
public int getMinorVersion() {
return this.driver.getMinorVersion();
}
@Override
public DriverPropertyInfo[] getPropertyInfo(String u, Properties p) throws SQLException {
return this.driver.getPropertyInfo(u, p);
}
@Override
public boolean jdbcCompliant() {
return this.driver.jdbcCompliant();
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return driver.getParentLogger();
}
}

View File

@ -51,7 +51,7 @@ public class DBCPServiceTest {
* Unknown database system.
*
*/
// @Test
@Test
public void testUnknownDatabaseSystem() throws InitializationException {
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
final DBCPConnectionPool service = new DBCPConnectionPool();
@ -64,7 +64,7 @@ public class DBCPServiceTest {
/**
* Missing property values.
*/
// @Test
@Test
public void testMissingPropertyValues() throws InitializationException {
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
final DBCPConnectionPool service = new DBCPConnectionPool();
@ -78,7 +78,7 @@ public class DBCPServiceTest {
* Connect, create table, insert, select, drop table.
*
*/
// @Test
@Test
public void testCreateInsertSelect() throws InitializationException, SQLException {
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
final DBCPConnectionPool service = new DBCPConnectionPool();
@ -120,7 +120,7 @@ public class DBCPServiceTest {
* Connect, create table, insert, select, drop table.
*
*/
// @Test
@Test
public void testExternalJDBCDriverUsage() throws InitializationException, SQLException {
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
final DBCPConnectionPool service = new DBCPConnectionPool();
@ -136,7 +136,7 @@ public class DBCPServiceTest {
runner.setProperty(service, DBCPConnectionPool.DB_DRIVER_JAR_URL, "file:///var/tmp/mariadb-java-client-1.1.7.jar");
runner.setProperty(service, DBCPConnectionPool.DB_HOST, "127.0.0.1"); // localhost
runner.setProperty(service, DBCPConnectionPool.DB_HOST, "localhost"); // localhost
runner.setProperty(service, DBCPConnectionPool.DB_NAME, "testdb");
runner.setProperty(service, DBCPConnectionPool.DB_USER, "tester");
runner.setProperty(service, DBCPConnectionPool.DB_PASSWORD, "testerp");
@ -163,7 +163,7 @@ public class DBCPServiceTest {
* Get many times, after a while pool should not contain any available connection
* and getConnection should fail.
*/
// @Test
@Test
public void testExhaustPool() throws InitializationException, SQLException {
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
final DBCPConnectionPool service = new DBCPConnectionPool();
@ -197,7 +197,7 @@ public class DBCPServiceTest {
* Get many times, release immediately
* and getConnection should not fail.
*/
// @Test
@Test
public void testGetManyNormal() throws InitializationException, SQLException {
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
final DBCPConnectionPool service = new DBCPConnectionPool();
@ -226,7 +226,7 @@ public class DBCPServiceTest {
}
// @Test
@Test
public void testDriverLoad() throws ClassNotFoundException {
Class<?> clazz = Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
assertNotNull(clazz);
@ -244,29 +244,36 @@ public class DBCPServiceTest {
ClassLoader parent = Thread.currentThread().getContextClassLoader();
URLClassLoader ucl = new URLClassLoader(urls,parent);
// URLClassLoader ucl = new URLClassLoader(urls);
Class<?> clazz = Class.forName("org.mariadb.jdbc.Driver", true, ucl);
assertNotNull(clazz);
Driver driver = (Driver) clazz.newInstance();
Driver shim = new DriverShim(driver);
DriverManager.registerDriver( shim );
// Driver is found when using URL ClassLoader
assertTrue( isDriverAllowed(driver, ucl) );
// Driver is not found when using parent ClassLoader
// unfortunately DriverManager will use caller ClassLoadar and driver is not found !!!
assertTrue( isDriverAllowed(driver, parent) );
// assertTrue( isDriverAllowed(driver, parent) );
// DriverManager.registerDriver( (Driver) clazz.newInstance());
Enumeration<Driver> drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
driver = (Driver) drivers.nextElement();
System.out.println(driver);
}
Driver driver2 = DriverManager.getDriver("jdbc:mariadb://localhost:3306/testdb");
assertNotNull(driver2);
Connection connection = DriverManager.getConnection("jdbc:mariadb://localhost:3306/testdb","tester","testerp");
assertNotNull(connection);
connection.close();
// Driver driver = DriverManager.getDriver("jdbc:mariadb://127.0.0.1:3306/testdb");
// assertNotNull(driver);
DriverManager.deregisterDriver(shim);
}
@ -299,7 +306,7 @@ public class DBCPServiceTest {
}
//==================================== problem solving - no suitable driver found, mariadb =========================================
// method isDriverAllowed is from DriverManager
private static boolean isDriverAllowed(Driver driver, ClassLoader classLoader) {
boolean result = false;
if(driver != null) {
@ -318,6 +325,4 @@ public class DBCPServiceTest {
}
}