How to add a class to the ClassPath at runtime? - java

A have a jar file which has a main method. I compile a new class and I want to add this new class to CP.
/folder
/my_jar_file.jar
/MyClass.class
How can I do that kind of thing in Java without recompiling of jar file?

Adding to class path is will not load your class at runtime.
You need to explicitly load your jar in the code as below,
URLClassLoader classLoader = new URLClassLoader (yourJar.toURL(), this.getClass().getClassLoader());
Class classToLoad = Class.forName ("com.company.abc", true, classLoader);

Related

How to load classes dynamically from jar file

If i have a jar file that contain many class, how to get classes, and create instances at runtime. In this code i don't understand the Class.forName line, the MyClass is that class what contain the jar file or that class what will create after the jar file load?
URLClassLoader child = new URLClassLoader(
new URL[] {myJar.toURI().toURL()},
this.getClass().getClassLoader()
);
Class classToLoad = Class.forName("com.MyClass", true, child);
Method method = classToLoad.getDeclaredMethod("myMethod");
Object instance = classToLoad.newInstance();
Object result = method.invoke(instance);
Class.forName loads a class.
The first parameter is the (canonical) name of the class. In this case, you try to load the class com.MyClass.
The second parameter specifies that the class should be initialized at that point(static variables are initialized and static blocks are run.
The third parameter is the ClassLoader the class will be loaded from. In your case, it will try to find the class from myJar but if the class isn't found there, it will try to load the class from the same ClassLoader the calling class has been loaded.

Load a class from a jar having dependency on another jar

My project structure is the following (very simplified of course):
So under lib-ext i download on a daily basis from a Jenkins server 2 jar files 'jar1 and jar2' to be checked by my program, i need one file from 'jar1' lets call it: "Class2Bloaded".
The issue is that this file implements an interface that is to be found in 'jar2', lets call this 'Dependency'
What i would like to do is, from my class under src "ClassThatLoads.java", load 'Class2Bloaded.class' and tell the class loader to look into 'jar2' to search for the implementing interface "Dependency.class"
My code so far (omitting exceptions handling):
//Create the URL pointing to Jar1
private URL getJarUrl(JarFile jarFile)
{
return new File(jarFile.getName()).toURI().toURL();
}
URL jar1Url = getJarUrl(jar1);
ClassLoader jar1classLoader = new URLClassLoader(new URL[] { jar1Url });
Class<?> Class2Bloaded = Class.forName(fullClassName, false, jar1classLoader );
So the problem happens within the Class.forName invocation, because the class i want to load implements an interface that is in jar 2.
Exception in thread "main" java.lang.NoClassDefFoundError: com/packagewithinJar2/Dependency
So eventually i have prepared another class loader that points to 'jar2', and i have even got the actual Interface i need:
URL jar2Url = getJarUrl(jar2);
ClassLoader jar2classLoader = new URLClassLoader(new URL[] { jar2Url });
Class<?> Interface2Bloaded = Class.forName(fullClassName, false, jar2classLoader );
Where 'fullClassName' in the second case is the fully qualified name of the interface from which 'Class2Bloaded' depends on.
Is just that i cant find anything in the javadocs of ClassLoader that allows me to 'inject' an additional class loader for the dependencies.
I hope my explanation is clear.
The first thing to do would be to add jar2 to the list of jars your URLClassLoader reads:
ClassLoader jarclassLoader = new URLClassLoader(new URL[] { jar1Url, jar2Url });
BUT the normal thing to do would be to add jar1 and jar2 on your classpath from the beginning.
To do so you would use the -cp parameter of the java executable.
for example, if you compile your classes into the bin directory:
java -cp libext/jar1.jar:libext/jar2.jar:bin ClassThatLoads
That way, you could use the classes seamless in your own java source and get rid of the cumbersome loading part :
public class ClassThatLoads {
public static void main(String[] args) {
Class2Bloaded stuff = new Class2Bloaded();
//use stuff from here...
}
}

Loading external class files without regard to class-paths or packages

I am attempting to load a class object from some compiled class file sitting in my Desktop dir.
I am feeding in two arguments to main in my program which uses URLClassLoader to get an instance of a class from a compiled file TheClassToLoad.class.
I have, in Main of the classLoading program: (args[0] is for something unrelated)
String classFile_FilePath = args[1];
String className = args[2];
URL classUrl = new URL(classFile_FilePath);
URLClassLoader ucl = new URLClassLoader(new URL[]{classUrl});
When running this program from the shell while in the project directory:
Me:ClassLoadingProgramRootDir Me$ java com.company.Main argZero file:///Users/Me/Desktop/ TheClassToLoad.class
I find a raised exception:
Exception in thread "main" java.lang.ClassNotFoundException: TheClassToLoad.class
So, there is a file TheClassToLoad.class in Desktop/ yet URLClassLoader raises an exception without providing the detail I need to debug the situation.
I am new to Java and am aware that class paths like com.company.Class is often needed to refer to a class's true class name based on package directory structure. However, in this case, I am simply requested that URLClassLoader give me an instance of the Class Object for an arbitrary compiled class file sitting somewhere on a machine.
For URLClassLoader, the URL should be of the directory containing the class+package structure, not the class file itself. In your case, it should be file:///Users/Me/Desktop/.
The argument to loadClass should be the name of the class, not the name of the class file. In your case, it should be TheClassToLoad.
If the class is in a package (e.g., my.pkg.TheClassToLoad), then you should use that class name as the argument to loadClass, and the URL for URLClassLoader should still be the root of the package structure (e.g., file:///Users/Me/Desktop if the file is file:///Users/Me/Desktop/my/pkg/TheClassToLoad.class).

Spring webapp loading plugins: ClassNotFoundException

I am writing a Spring web application, which loads plugin files at runtime. These plugins are classes that implement IPlugin. However, when loading the plugin at runtime I get a ClassNotFoundException(IPlugin cannot be found).
The interface IPlugin is located in a package in my web app. In order to build a plugin I exported the interface to a jar file and included it in the plugin's build path.
In my web app, the plugin is loaded using a URLClassLoader:
URL fileUrl = jar.toURI().toURL();
String jarUrl = "jar: " + fileUrl + "!/";
URL[] urls = new URL[] {new URL(jarUrl)};
URLClassLoader loader = new URLClassLoader(urls);
IPlugin plugin = (IPlugin) Class.forName(clazz, true, loader).newInstance();
How do I make the interface available at runtime?
Edit:
It does work if I load the jar containing IPlugin.class together with the plugin. But is that really necessary?
I don't think you can use IPlugin without importing on the top
Simply bring the IPlugin.java to your code with the same package name, nothing wrong in this
You can directly use full file path to load a JAR via URLClassLoader
Try the following code
URL[] classLoaderUrls = new URL[]{new URL("file:///<your directory path>/<your filename>.jar")};
URLClassLoader urlClassLoader = new URLClassLoader(classLoaderUrls);
Class<?> beanClass = urlClassLoader.loadClass("com.my.MyImplementation");
Constructor<?> constructor = beanClass.getConstructor();
Object beanObj = constructor.newInstance();
Try to type cast, if you have IPlugin.java in your import on top this will work
IPlugin plugin = (IPlugin)beanObj;
Otherwise you need to use Reflection
Method method = beanClass.getMethod("myMethod");
method.invoke(beanObj);
I found the solution, I had to set the parent class loader like this: new URLClassLoader(URLs, getClass().getClassLoader()). It is then sufficient to have the IPlugin interface present in the webapp, no additional jar is needed in WEB-INF/lib.

Classloader java.lang.NoClassDefFoundError?

So I have a classloader loading a class like so:
ClassLoader cl = new URLClassLoader(urls);
Class cls = cl.loadClass("modules.Test");
Method method = cls.getDeclaredMethod("getModule", noparams);
Class<?> type = method.getReturnType();
if(type.newInstance() instanceof Module){
System.out.println("Accessed field with type: Module");
}
The class Module is in another jar at runtime. And the Test.class was generated within that main jar then i unarchived it, so the dependency would be there.
How can I access other dependencies from the external .class file I have loaded?
The exception:
java.lang.NoClassDefFoundError: com/xxxxxxx/xxxx/objects/Module
Caused by: java.lang.ClassNotFoundException: com.xxxxxxx.xxxx.objects.Module
I think that the problem is happening because your modules.Test class depends the Modules class, but your custom class loader can't find that class.
I think that is because you have instantiated the custom classloader incorrectly. You wrote:
ClassLoader cl = new URLClassLoader(urls);
That creates a classloader whose parent classloader is the default system classloader. But the error implies that the default classloader is not the one that knows about Modules. Try this instead:
Classloader cl = new URLClassLoader(
urls, this.getClass().getClassLoader());
This should at least give you a classloader that knows about Modules.
Note: adding the URL for the JAR containing Modules to the urls array is a non-solution. You are liable to end up loading the Modules class twice, and that is liable to lead to other problems. (The instanceof won't work, for example.)

Categories

Resources