classLoader can't load classes implementing a class out of this module - java

I'm trying to load specific classes in my maven plugin using below class loader:
public ClassLoader getClassLoader(MavenProject project) {
try
{
List classpathElements = project.getCompileClasspathElements();
URL urls[] = new URL[classpathElements.size()];
for ( int i = 0; i < classpathElements.size(); ++i ) {
urls[i] = new File( (String) classpathElements.get( i ) ).toURL();
}
return new URLClassLoader( urls, this.getClass().getClassLoader() );
} catch ( Exception e ) {
System.out.println( "Couldn't get the classloader." );
return this.getClass().getClassLoader();
}
}
this loader works completely fine in a test simple project. but when I use it in a multi-module project it doesn't load specific classes. the classes that implement a class in another module(for example CardlessFacadeBean implements CardlessFacade, CardlessFacadeBean class is in this module and CardlessFacade class is in another module). but other classes that don't have this condition loads fine. is there any way to solve this issue in a simple way? Thanks so much

I simply added another module classpath to classpath Elements list as below and it recognized mentioned classes.
classpathElements.add(moduleDirectory);
any other solution would be appreciated.

Related

Load an external library inside a jar

I have a jar file that I load dynamically,
inside it there is in lib/ another jar (external library) that I use in main (import it.xxx.xx).
How do I load also this external library dynamically in classpath?
My code doesn't work:
public static void runOne(String jar, String class_name, Optional<String> test_name,
TestExecutionListener listener) throws Exception {
Launcher launcher = LauncherFactory.create();
ClassLoader loader = URLClassLoader.newInstance(
new URL[] { new File(pathJars+"/"+jar).toURI().toURL() },
ServiceUtil.class.getClassLoader()
);
loader.getClass();
addURL(loader); <--here i want add a jar to classpath!
Class cls=loader.loadClass(class_name);
Constructor constructor = cls.getConstructor();
constructor.newInstance();
LauncherDiscoveryRequest request;
if (test_name.isPresent()) {
Method m = cls.getMethod(test_name.get());
request = LauncherDiscoveryRequestBuilder.request()
.selectors(selectMethod(cls,m))
.build();
}
else{
request = LauncherDiscoveryRequestBuilder.request()
.selectors(selectClass(cls))
.build();
}
TestPlan testPlan = launcher.discover(request);
launcher.registerTestExecutionListeners(listener);
launcher.execute(request);
//launcher.execute(request);
loader=null;
System.gc();
}
public static void addURL(ClassLoader loader) throws IOException {
URL u=loader.getResource("lib/sem-platform-sdk-1.0-SNAPSHOT.jar");
Class[] parameters = new Class[]{URL.class};
URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader();
Class sysclass = URLClassLoader.class;
try {
Method method = sysclass.getDeclaredMethod("addURL", parameters);
method.setAccessible(true);
method.invoke(sysloader, new Object[]{u});
} catch (Throwable t) {
t.printStackTrace();
throw new IOException("Error, could not add URL to system classloader");
}//end try catch
}//end method
Thanks
This is generally done with a build tool (e.g. maven or gradle). I don't know if you are using one of these. They make life so much easier.
We use Maven with the Apache Shade plugin to do exactly this. Maven has commands to set up the configuration for you automatically, then you add the Shade plugin to the resulting configuration file (pom.xml).
https://maven.apache.org/plugins/maven-shade-plugin/index.html
If I understand your problem correctly, you want the classes loaded from the jar file to be able to access the classes in the nested jar file. You can accomplish this by creating a ClassLoader with one entry for the jar file and another entry for the nested jar file.
Java has a special URL scheme, jar:, for referring to a jar entry directly. (This scheme and syntax is described in the documentation of JarURLConnection.) So you can construct your ClassLoader this way:
URL jarURL = new File(pathJars+"/"+jar).toURI().toURL();
URL semURL = new URL("jar:" + jarURL + "!/"
+ "lib/sem-platform-sdk-1.0-SNAPSHOT.jar");
ClassLoader loader = URLClassLoader.newInstance(
new URL[] { jarURL, semURL },
ServiceUtil.class.getClassLoader()
);

Access project classes from a maven plugin

I'm making a maven plugin that run in test phase, in pom.xml configuration of a project that uses my plugin, I'm setting a class canonical name that I want to use to run that class from my plugin, basically I'm making a way to have a dynamic class loading of classes inside the project from my plugin.
Class clazz = Class.forName("... class from pom.xml ...")
When I run it I receive the expectable "ClassNotFoundException", seems the class loader are not the same or not shared.
There is a way to do it? Like capture the class loader from the project or receive it by dependency injection into my plugin? What is the best way?
We can use the Hibernate implementation in mojo can be used as a reference to make it:
Checkout the source code here: http://grepcode.com/file/repo1.maven.org/maven2/org.codehaus.mojo/hibernate3-maven-plugin/2.2/org/codehaus/mojo/hibernate3/HibernateExporterMojo.java#HibernateExporterMojo.getClassLoader%28%29
private ClassLoader getClassLoader(MavenProject project)
{
try
{
List classpathElements = project.getCompileClasspathElements();
classpathElements.add( project.getBuild().getOutputDirectory() );
classpathElements.add( project.getBuild().getTestOutputDirectory() );
URL urls[] = new URL[classpathElements.size()];
for ( int i = 0; i < classpathElements.size(); ++i )
{
urls[i] = new File( (String) classpathElements.get( i ) ).toURL();
}
return new URLClassLoader( urls, this.getClass().getClassLoader() );
}
catch ( Exception e )
{
getLog().debug( "Couldn't get the classloader." );
return this.getClass().getClassLoader();
}
}
To capture the "project" object, we can use the mojo dependency injection:
/**
* Dependency injected
*/
#Parameter(defaultValue = "${project}")
public MavenProject project;
And use it to load some class in project class loader:
getClassLoader(this.project).loadClass("com.somepackage.SomeClass")

Database driver-class dynamic loading

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!!!

Loading classes dynamically from jar

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?

custom classLoader issue

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" );

Categories

Resources