I want to develop a DB-Agnostic application in java. I have chosen hibernate as ORM. The problem with jdbc is that , it is just an interface and we need the driver class of the db in the class path. Since the database should be configurable i have to go for loading the driver class of DB dynamically. (User should keep the driver class in a folder and it should be loaded dynamically )
Below is my code.
File driverJar = new File("E:\\Jomon\\backup_2017_05_25\\2.2\\WS\\2.2_1\\lib\\Drivers\\postgresql-42.1.1.jar");
URL[] urls = new URL[] { driverJar.toURL() };
URLClassLoader classLoader = new URLClassLoader(urls,DBUtils.class.getClassLoader());
Class.forName("org.postgresql.Driver", true, classLoader);
No error till now.
But after this, while initializing hibernate connection, I am getting error java.lang.ClassNotFoundException: org.postgresql.Driver.
May I know what is the problem here.
Finally I got the solution by myself,
The problem here is , I have created a new classloader and loaded the jar into it.
The hibernate is searching for driver class in system class loader , not in the user defined class loaders.
The problem here can be solved by load the jar into system class loader as below.
File driverJar = new File("E:\\Jomon\\backup_2017_05_25\\2.2\\WS\\2.2_1\\lib\\Drivers\\postgresql-42.1.1.jar");
URL myJarFile = new URL("jar", "", "file:" + driverJar.getAbsolutePath() + "!/");
URLClassLoader sysLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
Class sysClass = URLClassLoader.class;
Method sysMethod = sysClass.getDeclaredMethod("addURL", new Class[] { URL.class });
sysMethod.setAccessible(true);
sysMethod.invoke(sysLoader, new Object[] { myJarFile });
Class.forName("org.postgresql.Driver", true, classLoader); // Now no error in this line.
Hibernate will always to attempt the load the driver from the current thread classloader and in your case, it doesn't have the driver.
You own class loader works fine and just before you initialize sessionFactory ,set your custom loader into contextClassLoader like this.
File f = new File( "E:\\Jomon\\backup_2017_05_25\\2.2\\WS\\2.2_1\\lib\\Drivers\\postgresql-42.1.1.jar" );
URLClassLoader urlCl = new URLClassLoader( new URL[] { f.toURL() }, System.class.getClassLoader() );
Class postGreDriver = urlCl.loadClass( "org.postgresql.Driver" );
System.out.println( postGreDriver.newInstance() );
Thread.currentThread().setContextClassLoader(postGreDriver);
//Hibernate can start
//you should restore your old classloader when hibernate services end
This may not be the best solution. Have got this snippet from this discussion
Hope this helps!!!
Related
I need to load the Client.class using an ClassLoader .
When I use this code:
ClassLoader clientClassLoader = new URLClassLoader(
new URL[] { new File("client.jar").toURL() });
Class<?> clientClass = clientClassLoader.loadClass("pkhonor.Client");
I'm getting java.lang.ClassNotFoundException: /pkhonor/Client.
How should i Fix this problem?
I'm trying to get my custom class loaded in FXML JS.
First of all I've added URLClassLoader with my JAR to the FXMLLoader instance:
FXMLLoader loader = new FXMLLoader();
loader.setController(TabController.this);
URLClassLoader fxmlClassLoader = (URLClassLoader) loader.getClassLoader();
loader.setClassLoader(URLClassLoader.newInstance((URL[]) ArrayUtils.addAll(new URL[]{ new File("/home/sk_/projects/mjolnirr/.hive/static/calculator/origJar.jar").toURI().toURL() }, fxmlClassLoader.getURLs())));
And then in FXML JavaScript:
importClass(com.mjolnirr.sample.SomeTestClass);
It fails with error:
sun.org.mozilla.javascript.internal.EvaluatorException: Function importClass must be called with a class; had "[JavaPackage com.mjolnirr.sample.SomeTestClass]" instead. (<Unknown source>#2) in <Unknown source> at line number 2
Anyone faced that?
Okay, I've found one hack solution there. In short - I've just added my URL to the system classloader dynamically, like this:
public static void addURLToSystemClassLoader(URL url) throws IntrospectionException {
URLClassLoader systemClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
Class<URLClassLoader> classLoaderClass = URLClassLoader.class;
try {
Method method = classLoaderClass.getDeclaredMethod("addURL", new Class[]{URL.class});
method.setAccessible(true);
method.invoke(systemClassLoader, new Object[]{url});
} catch (Throwable t) {
t.printStackTrace();
throw new IntrospectionException("Error when adding url to system ClassLoader ");
}
}
And then in my render method
FXMLLoader loader = new FXMLLoader();
loader.setController(TabController.this);
try {
addURLToSystemClassLoader(new URL("hive://" + pageURL.getHost() + ":" + pageURL.getPort() + "/" + pageURL.getApplicationName() + "/origJar.jar"));
} catch (Exception e) {
e.printStackTrace();
}
But it looks like a bad practise.
I think that the original problem is that - JavaFX scripting engine has differect class loader, FXML loader doesn't pass it's own to the script engine. Anyone knows how to set the classloader for the script engine?
How about explicitly setting the class loaded for the current thread to the new class loader?
For example:
URL[] urlsForJarFiles = getUrls();
ClassLoader myClassLoader = new URLClassLoader(urlsForJarFiles, ClassLoader.getSystemClassLoader();
Thread.currentThread.setContextClassLoader(myClassLoader);
I know that we can load classes dynamically by using custom class loaders.
But here my problem is my Class itself is depends upon other classes
My task is to get PigServer object .So I have used following code to load PigServer class
_pigServerClass = _classLoader.loadClass("org.apache.pig.PigServer");
But here PigServer class itself is depends upon so many other classes.
So when i am trying to get instance of PigServer class then it is showing following errors
java.lang.ClassNotFoundException: org.apache.commons.logging.LogFactory
java.lang.ClassNotFoundException:org.apache.log4j.AppenderSkeleton
etc..
Can anyone tell how to solve this?
There seems to be a misunderstanding. If you have all the jars required in a folder, say "lib", you can for example set up a class loader like this:
File libs = new File("lib");
File[] jars = libs.listFiles(new FileFilter() {
public boolean accept(File pathname) {
return pathname.getName().toLowerCase().endsWith(".jar");
}
});
URL[] urls = new URL[jars.length];
for (int i=0; i<jars.length; i++) {
urls[i] = jars[i].toURI().toURL();
}
ClassLoader uc = new URLClassLoader(urls,this.getClass().getClassLoader());
Class<?> pigServerClz = Class.forName("org.apache.pig.PigServer", false, uc);
Object pigServer = pigServerClz.newInstance();
// etc...
How you created your ClassLoader?
Did you specified another "parent" classloader, on wich classloading can be delegated?
the problem is next: i took the base classLoader code from here. but my classLoader is specific from a point, that it must be able to load classes from a filesystem(let's take WinOS), so in classLoader must be some setAdditionalPath() method, which sets a path(a directory on a filesystem), from which we'll load class(only *.class, no jars). here is code, which modifies the loader from a link(you can see, that only loadClass is modified), but it doesn't work properly:
public void setAdditionalPath(String dir) {
if(dir == null) {
throw new NullPointerException("");
}
this.Path = dir;
}
public Loader(){
super(Loader.class.getClassLoader());
}
public Class loadClass(String className) throws ClassNotFoundException {
if(Path.length() != 0) {
File file = new File(Path);
try {
// Convert File to an URL
URL url = file.toURL();
URL[] urls = new URL[]{url};
// Create a new class loader with the directory
ClassLoader cl = new URLClassLoader(urls);
ClassLoader c = cl.getSystemClassLoader();
Class cls = c.loadClass(className);
return cls;
} catch (MalformedURLException e) {
} catch (ClassNotFoundException e) {
}
}
return findClass(Path);
}
I'd grateful if anyone helps :)
You can just use framework provided java.net.URLClassLoader. No need to write your own. It supports loading of classes from directories and JAR files.
Any URL that ends with a '/' is assumed to refer to a directory.
Otherwise, the URL is assumed to refer to a JAR file which will be
opened as needed.
It also supports a parent class loader. If this class loader does not suite your requirements, perhaps you can specify in more detail what you need. And in any case, you can look at the source and derive your own class loader class based on that.
Here is a short working snippet of code that should demostrate how to load a class by name from a URLClassLoader:
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
// This URL for a directory will be searched *recursively*
URL classes =
new URL( "file:///D:/code/myCustomClassesAreUnderThisFolder/" );
ClassLoader custom =
new URLClassLoader( new URL[] { classes }, systemClassLoader );
// this class should be loaded from your directory
Class< ? > clazz = custom.loadClass( "my.custom.class.Name" );
// this class will be loaded as well, because you specified the system
// class loader as the parent
Class< ? > clazzString = custom.loadClass( "java.lang.String" );
What would be a good way to dynamically load java class files so that a program compiled into a jar can read all the class files in a directory and use them, and how can one write the files so that they have the necessary package name in relation to the jar?
I believe it's a ClassLoader you're after.
I suggest you start by looking at the example below which loads class files that are not on the class path.
// Create a File object on the root of the directory containing the class file
File file = new File("c:\\myclasses\\");
try {
// Convert File to a URL
URL url = file.toURI().toURL(); // file:/c:/myclasses/
URL[] urls = new URL[]{url};
// Create a new class loader with the directory
ClassLoader cl = new URLClassLoader(urls);
// Load in the class; MyClass.class should be located in
// the directory file:/c:/myclasses/com/mycompany
Class cls = cl.loadClass("com.mycompany.MyClass");
} catch (MalformedURLException e) {
} catch (ClassNotFoundException e) {
}
Class myclass = ClassLoader.getSystemClassLoader().loadClass("package.MyClass");
or
Class myclass = Class.forName("package.MyClass");
or loading the class from different folder which is not in the classpath:
File f = new File("C:/dir");
URL[] cp = {f.toURI().toURL()};
URLClassLoader urlcl = new URLClassLoader(cp);
Class myclass = urlcl.loadClass("package.MyClass");
For further usage of the loaded Class you can use Reflection if the loaded Class is not in your classpath and you can not import and cast it. For example if you want to call a static method with the name "main":
Method m = myclass.getMethod("main", String[].class);
String[] args = new String[0];
m.invoke(null, args); // invoke the method
If you add a directory to your class path, you can add classes after the application starts and those classes can be loaded as soon as they have been written to the directory.