Results 1 to 14 of 14
  1. #1
    tomer1960 is offline Member
    Join Date
    Mar 2009
    Posts
    9
    Rep Power
    0

    Default java.sql.SQLException: No suitable driver .... when using Custom ClassLoader

    Hello everyone.

    I am using a custom class loader to load jar files form a specific directory.
    The jars are JDBC drivers.
    For my testing, I am using MySQL JDBC driver.
    I can see that the JDBC driver is loaded (I think is was loaded by the System class loader and not by my class loader, even so I used Class.forName(jdbcDriver, true, cl); where cl is my custom class loader.
    My problem is that when I try to get a connection I get java.sql.SQLException: No suitable driver found…


    I attached the source code for this post, and also a printing of the source and the result.

    Any suggestions ?

    Thanks,
    Tomer


    Here my Custom ClassLoader that includes the testing method:

    Java Code:
    public class JarFileClassLoader extends URLClassLoader {
    	public JarFileClassLoader(URL[] urls, ClassLoader parentClassLoader) {
    		super(urls, parentClassLoader);
    		Thread.currentThread().setContextClassLoader(this);
    	}
    
    	public JarFileClassLoader(URL[] urls) {
    		this(urls, Thread.currentThread().getContextClassLoader());
    		//this(urls, ClassLoader.getSystemClassLoader());
    	}
    
    	public JarFileClassLoader() {
    		this(new URL[0]);
    	}
    
    
    	public void loadJarFiles(String jarDir) throws MalformedURLException {
    		File file = new File(jarDir);
    		URL[] jarList = this._getJarFiles(file);
    
    		int size = jarList.length;
    		for (int i = 0; i < size; i++) {
    			this._addFile(jarList[i]);
    			Logger.getInstance().log("Adding '" + jarList[i] + "' into application classpath");
    			System.out.println("JarFileClassLoader.loadJarFiles() -Adding '" + jarList[i] + "' into application classpath");
    		}
    	}
    
    
    	private void _addFile(URL path) throws MalformedURLException {
    		URL urls[] = this.getURLs();
    		for (int i = 0; i < urls.length; i++) {
    			if (urls[i].equals(path)) {
    				System.out.println(path + " is already in the CLASSPATH");
    				return;
    			}
    		}
    
    		// String urlPath = "jar:file://" + path + "!/";
    
    		super.addURL(path);
    	}
    
    
    	
    	private static URL[] _getJarFiles(File path) throws MalformedURLException {
    
    		List valRet = new ArrayList();
    
    		File[] files = path.listFiles(new FileFilter() {
    			public boolean accept(File pathname) {
    				return  pathname.getName().endsWith(".jar") || 
    						pathname.getName().endsWith(".zip") || 
    						pathname.isDirectory();
    			}
    		});
    
    		for (int i = 0; i < files.length; i++) {
    			if (files[i].isDirectory()) {
    				valRet.addAll(Arrays.asList(_getJarFiles(files[i])));
    			} else {
    				valRet.add(files[i].toURL());
    			}
    
    		}
    		return (URL[]) valRet.toArray(new URL[0]);
    	}
    
    	public String toString() {
    		return this.getClass().getName() + " (" + this.getClass() + " Loaded by " + this.getClass().getClassLoader() + ")";
    	}
    
    
    	
    	// --------------------------------------------------------------
    	// For Test purpose only....
    	// --------------------------------------------------------------
    	public static void testLoadJars(String jarDir) {
    		try {
    			URL urls[] = {};
    
    			JarFileClassLoader cl = new JarFileClassLoader(urls);
    
    			System.out.println("JarFileClassLoader.testLoadJars() - cl: " + cl.getClass().getClassLoader());
    			cl.loadJarFiles(jarDir);
    
    			String jdbcDriver = "com.mysql.jdbc.Driver";
    			System.out.println(jdbcDriver);
    			// cl.loadClass(jdbcDriver);
    			Class.forName(jdbcDriver, true, cl);
    			System.out.println("Success Loading JDBC driver! ---> Loaded by classLoader: " + jdbcDriver.getClass().getClassLoader());
    			Connection con = getConnection();
    			System.out.println("Success Getting a Connection! ---> Loaded by classLoader: " + con.getClass().getClassLoader());
    			con.close();
    		} catch (Exception ex) {
    			System.out.println("Failed.");
    			ex.printStackTrace();
    		}
    
    	}
    
    	public static Connection getConnection() {
    		Connection connection = null;
    		try {
    			String dbUrl =	"jdbc:mysql://localhost:3306/myDB";
    			String dbUser = "myuser";
    			String dbPass = "mypass";
    			connection = DriverManager.getConnection(dbUrl, dbUser, dbPass);
    		} catch (SQLException e) {
    			e.printStackTrace();
    		}
    
    		return connection;
    	}
    	
    
    	public static void main(String args[]) {
    String jarDir = "C:\\SqlTools\\lib";
    		testLoadJars(jarDir);
    	}
    }
    The result of running this code is:

    JarFileClassLoader.testLoadJars() - cl: sqlTool.util.jar.JarFileClassLoader (class sqlTool.util.jar.JarFileClassLoader Loaded by sun.misc.Launcher$AppClassLoader@360be0)(Loaded by sun.misc.Launcher$AppClassLoader@360be0)
    JarFileClassLoader.loadJarFiles() -Adding ‘file:/C:/SqlTools/lib/mysql-connector-java-5.1.6-bin.jar' into application classpath
    Driver to load: com.mysql.jdbc.Driver
    Success Loading JDBC driver! ---> Loaded by classLoader: null
    java.sql.SQLException: No suitable driver found for jdbc:mysql://localhost:3306/almogdb
    at java.sql.DriverManager.getConnection(Unknown Source)
    at java.sql.DriverManager.getConnection(Unknown Source)
    at sqlTool.util.jar.JarFileClassLoader.getConnection( JarFileClassLoader.java:124)
    at sqlTool.util.jar.JarFileClassLoader.testLoadJars(J arFileClassLoader.java:108)
    at sqlTool.util.jar.JarFileClassLoader.main(JarFileCl assLoader.java:161)
    java.lang.NullPointerException
    at sqlTool.util.jar.JarFileClassLoader.testLoadJars(J arFileClassLoader.java:109)
    at sqlTool.util.jar.JarFileClassLoader.main(JarFileCl assLoader.java:161)

    Failed.
    Attached Files Attached Files
    Last edited by tomer1960; 03-14-2009 at 10:52 PM.

  2. #2
    masijade is offline Senior Member
    Join Date
    Jun 2008
    Posts
    2,571
    Rep Power
    9

    Default

    Load DriverManager in that ClassLoader as well, and then use that DriverManager to get the Connections. Note, however, that even this is not guaranteed to work. The problem you're having currently is that the Driver class is being loaded in your ClassLoader, which is the fourth class loader in the chain, and DriverManager is in rt.jar, which is in the System ClassLoader, which is the first ClassLoader in the chain. Now, ClassLoader can delegate down the chain (i.e. from the second ClassLoader to the first), but not up the chain (i.e. from the first ClassLoader to the second), so DriverManager cannot find the loaded Driver class.

  3. #3
    masijade is offline Senior Member
    Join Date
    Jun 2008
    Posts
    2,571
    Rep Power
    9

    Default

    Also doing
    Java Code:
    jdbcDriver.getClass().getClassLoader()
    won't help, as that will show the ClassLoader for the String, and not the Driver. If you want to see the ClassLoader for the driver, you must do
    Java Code:
    Class<?> c = Class.forName(jdbcDriver, true, cl);
    System.out.println(c.getClassLoader());

  4. #4
    tomer1960 is offline Member
    Join Date
    Mar 2009
    Posts
    9
    Rep Power
    0

    Default

    Tanks for the replay.

    How do I load DriverManager with my custom class loader ?

    Also, you said "...ClassLoader can delegate down the chain (i.e. from the second ClassLoader to the first), but not up the chain (i.e. from the first ClassLoader to the second), so DriverManager cannot find the loaded Driver class."
    so what is the solution to that ?

    Thx,
    Tomer

  5. #5
    masijade is offline Senior Member
    Join Date
    Jun 2008
    Posts
    2,571
    Rep Power
    9

    Default

    Load the DriverManager and the Driver in the same ClassLoader.

    Java Code:
    Class<DriverManager> c = Class.forName("java.sql.DriverManager"), true, cl);
    Class.forName(jdbcDriver, true, cl);
    c.getConnection("...");
    I did not take the time to test this, so I don't know if it will work, or not, but that is the principle. You will probably have to play around quite a bit to get it to work. I haven't tried manipulating the DB Driver stuff, yet, myself either.

    BTW, if you are doing this so that you can distribute things with your app, then learn how to configure the manifest file properly, and this is a moot point.

    Adding Classes to the JAR File's Classpath (The Java™ Tutorials > Deployment > Packaging Programs in JAR Files)

    If you are doing it, so that you don't have to designate every jarfile in a directory individually on the classpath, then as of Java 6, you no longer have to, you can do
    Java Code:
    -classpath=/full/path/to/dir/containing/jars/*
    If you are doing it as an academic exercise, more power to you.

  6. #6
    tomer1960 is offline Member
    Join Date
    Mar 2009
    Posts
    9
    Rep Power
    0

    Default

    Thank again.

    I still doesn't work :(


    When I use :
    Java Code:
    ...
    Class<DriverManager> c = Class.forName("java.sql.DriverManager"), true, cl);
    System.out.println("DriverManager Loader: " + c.getClassLoader());
    Class jdbcDriverClass = Class.forName(jdbcDriverName, true, cl);
    System.out.println("jdbcDriverClass Loader: " + c.getClassLoader());
    ...
    I got that DriverManager Loader in null and jdbcDriverClass Loader is my custom class loader.
    I belive that this is ok, because the DriverManager is loaded by the SystemClassLoader that is the the top of my custome ClassLoader chain.


    Any othe idea ?

    Thx,
    Tomer

  7. #7
    masijade is offline Senior Member
    Join Date
    Jun 2008
    Posts
    2,571
    Rep Power
    9

    Default

    Rather than trying to load the DriverManager into the custom classloader try doing
    Java Code:
    Class<?> c = Class.forName(jdbcDriverName, true, cl);
    DriverManager.registerDriver((Driver) c.newInstance());
    There will be a few exceptions to catch there.

  8. #8
    tomer1960 is offline Member
    Join Date
    Mar 2009
    Posts
    9
    Rep Power
    0

    Default

    Still doesn't work :(
    Getting the same exception
    java.sql.SQLException: No suitable driver found for jdbc:mysql://localhost:3306/almogdb
    at java.sql.DriverManager.getConnection(Unknown Source)


    so weird .....

    Tannks anyway.
    If you will think abount another idea, please let me know

    Tomer

  9. #9
    masijade is offline Senior Member
    Join Date
    Jun 2008
    Posts
    2,571
    Rep Power
    9

    Default

    The best way is to simply include the Driver jarfile on the classpath by startup.

    Why are you trying it this way?

  10. #10
    tomer1960 is offline Member
    Join Date
    Mar 2009
    Posts
    9
    Rep Power
    0

    Default

    You are right, but I develope SQL Tool and I want the user be able to connect to any RDBMS he like.

  11. #11
    masijade is offline Senior Member
    Join Date
    Jun 2008
    Posts
    2,571
    Rep Power
    9

    Default

    So instruct them to place the Driver jarfile in a specific directory and use the *. As of java 6 you can include all jarfiles in a directory using *.

  12. #12
    tomer1960 is offline Member
    Join Date
    Mar 2009
    Posts
    9
    Rep Power
    0

    Default

    I didn't want to limit my users the use JRE1.6. Some of then still using 1.4.
    Anyway, I think you are right, and that is what I will do: I will use 1.6 ability to include all jarfiles in a directory using *.

    But still it is interesting issue to me to understand :)

    Thnak you very much for all your help, suggestion and your patience.

    Tomer

  13. #13
    masijade is offline Senior Member
    Join Date
    Jun 2008
    Posts
    2,571
    Rep Power
    9

    Default

    One other possibility, although not very satisfying, is to rename the jarfile that is to be used, to a predetermined name, and include only that name on the classpath.

  14. #14
    tomer1960 is offline Member
    Join Date
    Mar 2009
    Posts
    9
    Rep Power
    0

Similar Threads

  1. java.sql.SQLException: Closed Connection
    By sunjavaboy in forum JDBC
    Replies: 4
    Last Post: 03-21-2012, 01:38 PM
  2. Replies: 6
    Last Post: 09-01-2010, 11:29 AM
  3. Replies: 2
    Last Post: 06-07-2010, 08:45 AM
  4. What database is more suitable to a java app?
    By hittite in forum New To Java
    Replies: 6
    Last Post: 11-14-2008, 04:33 AM
  5. Replies: 1
    Last Post: 10-16-2008, 02:09 PM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •