I want to ask about UrlClassLoaders. I write a main .jar file which works well. Now I want to make it able to load plugins from one of its folders. I wrote it until that it finds the .jar files in the folder, can list their classes inside (their name, path). The problem is when I ask him to find that class (for create an instance of it) they won't find the class which it inherites from my main jar file like this:
IN MAIN JAR:
-class Expansion { ... }
In a plugin jar:
-class ExpansionA extends MainJar.Expansion { ... }
So when I ask an UrlClassLoader to resolve the "ExpansionA" class it will search for the "Expansion" class in the plugin jar instead of the MainJar, and there is no "Expansion" class in the plugin jar because it inherites from the MainJar as a resource library... and throws NoDefFoundForClassError exception while the defineClass method. How can I tell him to check after the classes in the MainJar too? (Its not only about 1 class but I want the plugin to be able to inherite any class from the MainJar). Can u help me?
ClassLoaders have a parent to which load requests are delegated first. To ensure that the new class loader you are creating can access the class Expansion you should set the parent of the new ClassLoader to the ClassLoader of your class Expansion:
ClassLoader myLoader=newURLClassLoader(urls, Expansion.class.getClassLoader());
This does not only ensure that the Expansion class will be found by myLoader, it will also guaranty that it resolves to the same runtime class (as the parent is asked first). This is important if the plugin jar contains a copy of the class.
Related
So i've been working on a project of mine which works in modules. A module is simply an abstract class which all the modules extend from and replace the empty methods they need to. No modules have public functions that arent defined in the abstract class and each function is called depending on specific events.
What i'd like to do is externalize each module (for this exemple ill refer to Autorole as an exemple module) into its own jar that the main class will fetch and use. That would allow for Autorole to be hotswapped/hot updated by simply replacing it's old jar file with the new one and reload the main program (rescan the jars in the modules folder and load them, without turning off the main program)
Edit1 : Very much like craftBukkit loads it's plugins
Currently my approach is to export each module as an executable jar and start it with a command using the runtime environment of the main program. (java -jar Autorole.jar for eg)
The main function of each module simply adds an instance of their module (here Autorole extended from the module class) into a static list in the main class of the main jar. And it works fine, its able to execute the Autorole jar properly and use it like it used to when everything was in the same jar.
But here comes the problem.
Autorole depends on the main class and thus has it as a dependency, and i dont know how to make it able to recognize the main file as their dependency, meaning right now i need to have a copy of the main class for each module which is far from convenient or optimized.
What i wanted to try at first was specify the path the main file is in but i got stuck rather quickly. The path cant be hardcoded since the main file has versions and modules arent version specific, plus the name might not be the exact same as expected, so i thought of passing the path of the main jar as a java arg, and that's where i'm at as i dont know what to do once i have the string in hands
tl-dr:
How can i load a jar from another like craftBukkit loads it's plugins
The Class Loaders allow you to dynamically load classes into a JVM process. URLClassLoader is a class loader that loads classes from a given URL e.g. external jar on the file system.
Just call ClassLoader#loadClass(String name) with the fully qualified class name, e.g: Class<*> theClass = classLoader.loadClass("com.packagename.ClassName"); and you will receive a Class. Then you'll have to get a constructor which you want to use to create this class's instance, e.g. Constructor<*> theConstructor = theClass.getConstructor(String.class); Finally invoke the constructor using Constructor#newInstance(Object... initargs) providing arguments required by the constructor.
Most likely, you will also want to set your plugin's class loader as a parent class loader.
Full example:
URL[] jars = new URL[1];
jars[0] = new File("path/to/file.jar").toURI().toURL();
URLClassLoader classLoader = new URLClassLoader(jars, YourPlugin.java.getClassLoader());
Class<*> loadedClass = classLoader.loadClass("com.example.ClassName");
Object classInstance = loadedClass.getConstructor().newInstance();
I want to load classes that are inside a jar, that is located within another jar. Following is a hierarchy tree illustrating the location of the .class.
A.jar
|---A.class
|---B.jar
|---InterfaceA.class
So I have a class 'A'. This class implements an interface, 'Interface B' which is inside B.jar. A third class named 'C' loads at runtime an instance of A.class. However, I end up with a java.lang.ClassNotFoundException. Any idea on how to fix this issue? I've seen plenty of posts that explains how to dynamically load classes from jar files, however I haven't seen anything that explains how to load classes from a jar file that is within another jar file.
Thanks for your time
Lets say we want to package our Java application into a jar file (including the dependencies). The dependencies are copied into one separate directory (lets say libs/). I have read that are two approaches to make the java launcher find these classes:
1) Implement a custom class loader code (loader/launcher pattern) that will load necessary classes before the main code of the application is executed (it is described here: http://vladimirvivien.wordpress.com/2011/10/07/86/)
2) Add the libs/ directory to the "Class-Path:" header in meta-inf/manifest.fm
Which solution is better and why?
I seem to be having a problem with loading classes in a module loader for an application I'm developing. Basically, all classes I'm going to be loading with it extend another class, which is located in a package in the actual application. For our purposes, we'll call it Module. Modules are located in a separate folder outside the actual application.
The loader iterates through a folder and executes the loadFile() method on any file with the extension .class. All classes have the package declaration as the Module class, as well as the extends Module declaration in the class header.
This is the loadFile() method, header and exception clauses excluded:
String fileName = file.getName();
String className = fileName.replace(".class", ""); //Strips extension
Class<?> aClass = Class.forName(className, true, new URLClassLoader(new URL[] { file.toURI().toURL() }));
Class<? extends Module> modClass = aClass.asSubclass(Module.class);
return modClass.getConstructor().newInstance();
I keep getting a ClassNotFoundException on the third line. And past that, if it ClassNotFoundException weren't thown, would all dependencies be resolved?
From the documentation for URLCLassLoader:
This class loader is used to load classes and resources from a search path of URLs referring to
both JAR files and directories. 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.
So, you must use URLs for either directories or .jars
Two solutions:
Force your users to give you .jar files, including a manifest of some sort inside with the classname that they wish to be loaded. This approach is used by the Bukkit developers. Having used this method in the past, the dependencies should all be packaged in the .jar file and thus in the URLClassLoader's search path and able to be loaded.
Use the URL of the file's directory, and search that directory for .class files. I'm not sure if dependencies will be loaded using this method.
In the URLClassLoader, do not pass the file, but the parent folder. However, this works correctly if classes are all in the "default package", so the .class files you are loading must not have a package declaration on top.
By default, a class loader will also trigger loading of all classes required to properly build the class: it will try to load the super class, the super super class etc... all the interfaces and super interfaces, the classes needed for static fields and methods, the classes needed for method signatures (return types and parameters). It will not usually try to load classes used internally by methods, not until you execute those methods.
However, usually a class loader does not "contain" all those classes, for example your class will end up inheriting from java.lang.Object, and your URLClassLoader will not contain the Object.class file. So, class loaders delegate to their parent class loaders.
You are currently creating a URLClassLoader without specifying a parent, in Java 7 at least the parent will default to the "system class loader", which is fine as long as you are in a plain java application, and not executing your code itself inside a specific hierarchy of class loaders. If however you are running that code in a web application, or in an OSGI container etc.. the you should give the URLClassLoader a proper parent to delegate to, for example Thread.currentThread().getContextClassLoader() or this.getClass().getClassLoader().
I suppose you need all of this because you need to load those class dynamically at runtime.
I have troubles using the java ServiceLoader in a NetBeans module application. Here is what I'm trying to do (and it works in a normal java application in Eclipse):
I have an interface.jar, which declares the interface. And I have implementations.jar, which has several implementations of this interface, all specified in the spi/META-INF/services/my.package.name.MyInteface file (this file is in the implemenations.jar).
I also have a class ImplementationHandler (in yet another handler.jar), which has the following method to load all implementations:
private static List<MyInterface<?>> loadAllImplementations() {
List<MyInterface<?>> foundImplementations = Lists.newArrayList();
try {
for (MyInterface implementation : ServiceLoader.load(MyInterface.class)) {
foundImplementations.add(implementation);
}
} catch (ServiceConfigurationError error) {
system.out.println("Exception happened");
}
return foundImplementations;
}
This code returns all implementations in Eclipse normal application (the foundImplementations.size() > 0).
However under NetBeans, it can't find anything (foundImplementations.size() == 0).
More details:
I have the source of a NetBeans module application (open source, not written by me), which I need to extend by using some of MyInterface implementations. The interface.jar, implementations.jar and the handler.jar are created in Eclipse and they are part of another application.
In the NetBeans, I opened the module which needs to use the new impplementations and I added all my 3 jars as external libraries (NetBeans copied them into its ext folder, which I don't want but I can't do anything about - I want them in another myext folder, but that's another story). Then I rebuilt everything and tried to use one of my implementations, but it was not found... The code that gets an implementation is in the ImplementationHandler class and looks like:
public static final <T> MyInteface<T> getByName(String name) {
for (MyInteface implementation : loadAllImplementations()) {
if (implementation.getName().equals(name)) {
return implementation;
}
}
throw new IllegalArgumentException("Unable to find MyInterface class for: " + name);
}
I got my exception "Unable to find MyInteface class for: myImplementationName"...
My knowledge about NetBeans is very limited and I was wondering is there something more that I need to do in order to get this working?
In order to work, you have to make Netbeans create this services sub folder inside META-INF. It's very easy to do, but the information is easily accessible.
To add something to META-INF, you need to create a folder of this name in your src/ (the source directory [spi?]) folder. In this case you also need the services folder and in it, create a text file with the same fully qualified name as your service interface. In the end you should have this structure: src/META-INF/services/my.package.MyInterface.
Finally, this [my.package.MyInterface] file's content should list all the implementation classes (one per line).
With this setup, Netbeans will create the appropriate jar when building your app.
Take a look at this ServiceLoader example. It's a complete example, although it does not explain the Netbeans integration I just described.
The ServiceLoader.load(Class) uses the current thread's context class loader to load all the implementations. It may be that in your case your implementation classes in your jar file (implementation.jar) are not in that class loader's classpath.
You may have to try different approaches for this :
You may either need to have all the jars in the netbeans module's classpath or,
You may need to create a class loader (probably a URLClassLoader having those jars in its classpath) and use the ServiceLoader.load(Class, ClassLoader) and pass a that classloader.
There is another option you could try but I am not sure about this: The jar file spec allows you to specify Class-Path manifest attribute, to which you can add 'implementation.jar' entry. More details here
Most likely, the handler.jar and implementations.jar are not loaded by the same class loader. Also you may want to take a look as to why your files are getting to ext folder.
Hope this helps.
Edit:
Also try calling the ServiceLoader.load(Class, null) which uses the System class loader (the one that started the application). Probably that classloader may be able to find classes in jars located in the ext directory.
I gave up, and replaced ClassLoader with JSPF. This works out of the box and I don't need to know about the internals of a third party program, written as NetBeans module and how this affects the classpath given to the class loaders.