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 {
     }
     
     
-    
-    
 }

Reply via email to