JDBC driver can be loaded from external location using URLClassLoader Signed-off-by: Toivo Adams <[email protected]> Signed-off-by: Mark Payne <[email protected]>
Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/1682d62d Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/1682d62d Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/1682d62d Branch: refs/heads/develop Commit: 1682d62d7f46ba854f77923352a77ff9f6a1b746 Parents: 864e099 Author: Toivo Adams <[email protected]> Authored: Sat Mar 14 12:34:40 2015 +0200 Committer: Mark Payne <[email protected]> Committed: Wed May 20 08:35:55 2015 -0400 ---------------------------------------------------------------------- .../apache/nifi/dbcp/DBCPConnectionPool.java | 23 ++++-- .../java/org/apache/nifi/dbcp/DriverShim.java | 74 ++++++++++++++++++++ .../org/apache/nifi/dbcp/DBCPServiceTest.java | 35 +++++---- 3 files changed, 113 insertions(+), 19 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/1682d62d/nifi/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-dbcp-service/src/main/java/org/apache/nifi/dbcp/DBCPConnectionPool.java ---------------------------------------------------------------------- diff --git a/nifi/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-dbcp-service/src/main/java/org/apache/nifi/dbcp/DBCPConnectionPool.java b/nifi/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-dbcp-service/src/main/java/org/apache/nifi/dbcp/DBCPConnectionPool.java index c2511c3..e2c0fea 100644 --- a/nifi/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-dbcp-service/src/main/java/org/apache/nifi/dbcp/DBCPConnectionPool.java +++ b/nifi/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-dbcp-service/src/main/java/org/apache/nifi/dbcp/DBCPConnectionPool.java @@ -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 http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/1682d62d/nifi/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-dbcp-service/src/main/java/org/apache/nifi/dbcp/DriverShim.java ---------------------------------------------------------------------- diff --git a/nifi/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-dbcp-service/src/main/java/org/apache/nifi/dbcp/DriverShim.java b/nifi/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-dbcp-service/src/main/java/org/apache/nifi/dbcp/DriverShim.java new file mode 100644 index 0000000..8905d9d --- /dev/null +++ b/nifi/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-dbcp-service/src/main/java/org/apache/nifi/dbcp/DriverShim.java @@ -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(); + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/1682d62d/nifi/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-dbcp-service/src/test/java/org/apache/nifi/dbcp/DBCPServiceTest.java ---------------------------------------------------------------------- diff --git a/nifi/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-dbcp-service/src/test/java/org/apache/nifi/dbcp/DBCPServiceTest.java b/nifi/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-dbcp-service/src/test/java/org/apache/nifi/dbcp/DBCPServiceTest.java index 07d1398..cb9c208 100644 --- a/nifi/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-dbcp-service/src/test/java/org/apache/nifi/dbcp/DBCPServiceTest.java +++ b/nifi/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-dbcp-service/src/test/java/org/apache/nifi/dbcp/DBCPServiceTest.java @@ -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 { } - - }
