Java: load User-defined interface implementation (from config file) - java

I need to allow a user to specify the implementation of an interface at runtime via a config file, similar to in this question: Specify which implementation of Java interface to use in command line argument
However, my situation is different in that the implementations are not known at compile time, so I will have to use reflection to instantiate the class. My question is ... how do I structure my application such that my class can see the new implementation's .jar, so that it can load the class when I call:
Class.forName(fileObject.getClassName()).newInstance()
?

The comment is correct; as long as the .jar file is in your classpath, you can load the class.
I have used something like this in the past:
public static MyInterface loadMyInterface( String userClass ) throws Exception
{
// Load the defined class by the user if it implements our interface
if ( MyInterface.class.isAssignableFrom( Class.forName( userClass ) ) )
{
return (MyInterface) Class.forName( userClass ).newInstance();
}
throw new Exception("Class "+userClass+" does not implement "+MyInterface.class.getName() );
}
Where the String userClass was the user-defined classname from a config file.
EDIT
Come to think of it, it is even possible to load the class that the user specifies at runtime (for example, after uploading a new class) using something like this:
public static void addToClassPath(String jarFile) throws IOException
{
URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
Class loaderClass = URLClassLoader.class;
try {
Method method = loaderClass.getDeclaredMethod("addURL", new Class[]{URL.class});
method.setAccessible(true);
method.invoke(classLoader, new Object[]{ new File(jarFile).toURL() });
} catch (Throwable t) {
t.printStackTrace();
throw new IOException( t );
}
}
I remember having found the addURL() invocation using reflection somewhere here on SO (of course).

Related

NoSuchMethodException for methods using custom data type

Whenever dynamically loading a class using the URLClassLoader I get a NoSuchMethodException when trying to execute a method with a custom data type as a parameter. It finds methods with standard types like String and int but not the custom type.
Loaded Class:
public void execute(ProcessingData data){
System.out.println("entered execute(ProcessingData data");
Calling Class:
URLClassLoader loader =
new URLClassLoader(new URL[] {new File(alg.getPath()).toURI().toURL()}, AlgorithmLoader.class.getClassLoader());
// Load class into memory
Class<?> algClass = Class.forName(className, true, loader);
logger.logInfo("Loaded class. Attempting to invoke execute(data) on aircraft: "+ data.getFlightData().getAircraftId());
Method processMethod = null;
try {
Object obj = algClass.newInstance();
processMethod = algClass.getDeclaredMethod("execute", ProcessingData.class);
processMethod.invoke(obj, data);
} catch (final NoSuchMethodException exception) {
logger.logInfo(exception.toString());
}
loader.close();
You're invoking the method without any object (null parameter of invoke). This means that the method will be expected to be static.
If your method is not static then you first need to create an instance of the type algClass using getConstructors() and pass that object to the first parameter of the invoke call.
My guess would be that your UrlClassLoader gets ProcessingData loaded by different class loader than your executing code in the posted snippet. Recall that classes loaded via different class loaders are different classes as far as JVM is concerned.

Problems with inheritance of static variables in Java

I have three classes I can't modify. In short, I have a class Program, and other two classes, ProgramClient and ProgramServer, inheriting from Program. The class Program has a static variable.
Until now, I ran ProgramClient and ProgramServer in two different applications, without any problem.
Now I need to run the two classes inside the same application. Doing this, they share the static variable of their parent class, and so bad things happen.
How can I keep the two classes in their own "context" (JVM?) in order to ensure that the static variable is used by only one of the children classes?
Static variables, by definition cannot be overridden or duplicated between classes.
However the two classes can be separated by using two separate class loaders, that are not chained. This is exactly how J2EE containers provider separation between web applications.
In other words, you will load the base class Program into the JVM twice, and sandbox them apart. Thus giving each separate 'program' their own instance. More can be learnt about class loaders here.
Here is a very simple example bootstrap program. Make sure that the code for ProgramClient and ProgramServer are NOT on the system classpath when you start the java command. You will also need to change the url to the jar file which does contain the target program code.
public class Bootstrap {
public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
URLClassLoader cl1 = new URLClassLoader(
new URL[] {
new URL("file:///client.jar")
},
Bootstrap.class.getClassLoader()
);
URLClassLoader cl2 = new URLClassLoader(
new URL[] {
new URL("file:///server.jar")
},
Bootstrap.class.getClassLoader()
);
invokeAsync(cl1, "a.b.c.ProgramClient");
invokeAsync(cl2, "a.b.c.ProgramServer");
}
private static void invokeAsync(final ClassLoader cl, final String fqn, final String...progArgs) {
new Thread() {
public void run() {
try {
Class p1 = cl.loadClass(fqn);
Class argType = String[].class;
Method m = p1.getMethod("main", argType);
m.invoke(null, (Object) progArgs);
} catch ( Exception e ) {
e.printStackTrace();
}
}
}.start();
}
}
There is no inheritance or override of static method or variable because there is only one reference for it in all the program. That the aim if static.
Maybe you have to create a context class which is instantiate for each program class.

Extending a class that may not be in classpath

I have a class that extends from a package class that may or may not be in the classpath when the program is ran, it isn't called unless the dependency is met,
however it seems to annoy the java verifier that throws a NoClassDefFoundError on attempting to load the program,
Any way around this?
Any way around this?
In practice, no. The superclass has to be available on the classpath for the loading, linking and verification of the subclass to succeed. That has to happen before the class can be initialized, and instances of it created.
If you can't be sure that the superclass is going to be available, you need to remove all direct and indirect static dependencies on the subclass(es), and then load the subclasses dynamically using Class.forName(). That will fail if the superclass is "missing", but you will get a different exception (not an Error) and there is the possibility that your application can continue, if it is designed to cope with the missing classes.
Frameworks such as Spring which have "optionally used" code depending on other libraries, use a "Strategy pattern" to put that dependency-specific code into an "inner class" or into a separate class.
The outer class can be loaded & run fine; it's only when you try & instantiate the inner class that the NoClassDefFoundError will be thrown.
So the outer class typically tries (try-catch) instantiating one strategy to use, and then if that fails instantiates a fallback strategy.
public class MyService {
protected MyStrategy strategy;
// constructor;
// -- choose our strategy.
public MyService() {
try {
this.strategy = new ExternalLib_Strategy();
} catch (NoClassDefFoundError x) {
// external library not available.
this.strategy = new Standard_Strategy ();
}
}
// --------------------------------------------------------
protected interface MyStrategy {
public void doSomething();
}
protected static class ExternalLib_Strategy implements MyStrategy {
ExternalLib lib = org.thirdparty.ExternalLib.getInstance(); // linkage may
public void doSomething() {
// ... use the library for additional functionality.
}
}
protected static class Standard_Strategy {
public void doSomething() {
// ... basic/ fallback functionality.
}
}
}
As a work around to this problem, If your class (subclass) is available in the classpath, you can check whether the parent class is available in the classpath by loading the class file as a resource using the method ClassLoader.getResource(). This method will never throw a class not found exception. But this will return null if the class is not found. You can aviod using your class if the resource is null.
See this sample code below:
public class Test {
/**
* #param args
*/
public static void main(String[] args) {
Object instance = Test.class.getClassLoader().getResource("com/test/package/Base.class");
Derived derived = null;
if(instance !=null) {
derived = new Derived();
System.out.println(derived.getString()); // call the getString method in base class
}
else {
// The class is not available. But no Exception
System.out.println("No Hope");
}
}
}

How to access java classes in a subfolder

I'm trying to make a program that can load an unknown set of plugins from a sub-folder, "Plugins". All of these plugins implement the same interface. What I need to know is how do I find all of the classes in this folder so that I can instantiate and use them?
MyInterface.java
A stub interface.
package test;
public interface MyInterface {
public void printSomething();
}
TestClass.java
A test class to be loaded, implementing your interface.
import test.MyInterface;
public class TestClass implements MyInterface {
public void printSomething() {
System.out.println("Hello World, from TestClass");
}
}
(Compiled class file placed in "subfolder/".)
Test.java
A complete test program that loads all class files from "subfolder/" and instantiates and runs the interface method on it.
package test;
import java.io.File;
public class Test {
public static void main(String[] args) {
try {
ClassLoader cl = ClassLoader.getSystemClassLoader();
File subfolder = new File("subfolder");
for (File f : subfolder.listFiles()) {
String s = f.getName();
System.out.println("Loading " + s);
Class cls = cl.loadClass(s.substring(0, s.lastIndexOf('.')));
MyInterface o = (MyInterface) cls.newInstance();
o.printSomething();
}
} catch (ClassNotFoundException e) {
} catch (InstantiationException e) {
} catch (IllegalAccessException e) {
}
}
}
Output from Test program above:
Loading TestClass.class
Hello World, from TestClass
Check java.util.ServiceLoader
A service is a well-known set of interfaces and (usually abstract) classes. A service provider is a specific implementation of a service. The classes in a provider typically implement the interfaces and subclass the classes defined in the service itself. Service providers can be installed in an implementation of the Java platform in the form of extensions, that is, jar files placed into any of the usual extension directories. Providers can also be made available by adding them to the application's class path or by some other platform-specific means.
This article explains the details.
Look through the folder with File.listFiles() and use a JarClassLoader instance to load the classes in there.
Or, add a description.xml in each of those jars if they are on the classpath, and use getClass().getClassLoader().findResources("description.xml") to load all descriptions, and then you have all the plugin classes to load.
Annotate your implementation classes with a custom annotation and use scannotation it does byte code scanning of the class files, and is orders of magnitudes faster than anything else, you can use it to search the entirety of a very large classpath instantly.

how Java classloaders work for "regular" circumstances (nonexplicit use of classloaders)

I'm looking into dynamic modification of classpath. I found one solution that works nicely but it does so using an explicit call to addURL(). (presumably at startup)
However, I would like to intercept the class-loading process at runtime to locate classes if the default classloader can't seem to find them. I tried to subclass ClassLoader so it just delegates findClass() and loadClass() to the default, and print out a debug line telling me these methods have been called, but they never seem to get called when my class uses dependent classes via implicit classloading, e.g.
// regular object instantiation with 'new'
BrowserLauncher launcher;
launcher = new BrowserLauncher();
// static methods
Foobar.doSomethingOrOther();
// Class.forName()
Class cl = Class.forName("foo.bar.baz");
// reflection on a Class object obtained statically
Class<Foobar> cl = Foobar.class;
// do something with cl, like call static methods or newInstance()
How does classloading work under these circumstances? (vs. the simpler case where Classloader.loadClass() is called explicitly)
Here's my attempt at a custom classloader, below. If I use DynClassLoader0.main() with an arguments list of {"some.package.SomeClass", "foo", "bar", "baz"}, and some.package.SomeClass references other classes found in external .jar files, using one of the methods listed above, why doesn't my DynClassLoader0's findClass() and loadClass() get called? The only time loadClass gets called is the explicit call to loadClass in the main() function below.
package com.example.test.classloader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class DynClassLoader0 extends ClassLoader {
public DynClassLoader0()
{
super();
}
public DynClassLoader0(ClassLoader parent)
{
super(parent);
}
public void runMain(String classname, String[] args) throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException
{
// [***] here we explicitly use our classloader.
Class<?> cl = loadClass(classname);
Method main = cl.getMethod("main", String[].class);
main.invoke(null, new Object[] {args});
}
#Override protected Class<?> findClass(String name) throws ClassNotFoundException
{
System.out.println("findClass("+name+")");
return super.findClass(name);
}
#Override public Class<?> loadClass(String name) throws ClassNotFoundException
{
System.out.println("loadClass("+name+")");
return super.loadClass(name);
}
static public void main(String[] args)
{
// classname, then args
if (args.length >= 1)
{
String[] classArgs = new String[args.length-1];
System.arraycopy(args, 1, classArgs, 0, args.length-1);
ClassLoader currentThreadClassLoader
= Thread.currentThread().getContextClassLoader();
DynClassLoader0 classLoader = new DynClassLoader0(currentThreadClassLoader);
// Replace the thread classloader - assumes
// you have permissions to do so
Thread.currentThread().setContextClassLoader(classLoader);
try {
classLoader.runMain(args[0], classArgs);
}
catch (Exception e) {
e.printStackTrace();
}
}
else
{
System.out.println("usage: DynClassLoader {classname} [arg0] [arg1] ...");
}
}
}
edit: I have looked through these questions already:
How do you change the CLASSPATH within Java?
Is it possible to “add” to classpath dynamically in java?
Adding files to java classpath at runtime.
edit: I thought what kdgregory is saying below is correct, that once I use my classloader explicitly (see line in code with [***] as a comment), all the code that executes from that class will cause implicit classloading from the same classloader. Yet my DynClassLoader0.loadClass() never gets called except during the outermost explicit call.
To quote from the ClassLoader JavaDoc:
The methods and constructors of
objects created by a class loader may
reference other classes. To determine
the class(es) referred to, the Java
virtual machine invokes the loadClass
method of the class loader that
originally created the class.
In other words, once you load a class, that class tries to load other classes through the classloader that loaded it. In a normal Java application, that is the system classloader, which represents the classpath passed to the JVM, or the boot classloader, used to load the JVM runtime.
Depending on your needs, there's a variant of Class.forName() that takes a classloader as an argument. If you use this to load a particular class, then references within that class should use the specified classloader.
Edit: I started tracing through your example, but decided it would just be easier to give my own. If you're going to write your own classloader, I suggest starting with the existing URLClassLoader, because it handles a lot of the behind-the-scenes stuff.
So, MyClassLoader takes a single JARfile/directory and loads classes for that directory alone. I've overridden the three methods called to load a class, and simply log their invocation (using System.err because it doesn't buffer output, unlike System.out).
My example uses a library that I'm currently working on; it was convenient, but you can pick any library you want as long as it's not already in your classpath.
The main() method loads a class via MyLoader. Then I invoke a method on that class, in a way that I know will throw an exception that's also part of the library. Note that I invoke the method by reflection: since the library is not on my Eclipse classpath, I couldn't compile it with an explicit reference.
When I run this program (under Sun JDK 1.5 for Linux), I see a lot of calls to loadClass(), both for classes in my library and for those on the classpath. This is expected: the ParseUtil class references a lot of other classes, and will use MyLoader (ie, its classloader) to load them. For those classes that MyLoader can't find locally, it delegates up the loader tree.
The exception is thrown, and when I print out its classloader I see that it's the same as the MyLoader instance I created. I also print out the loader for Exception.class, and it's null -- which the JavaDoc for Class.getClassLoader() says indicates the boot classloader.
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
public class ClassLoaderExample
{
private static class MyClassLoader
extends URLClassLoader
{
public MyClassLoader(String path)
throws Exception
{
super(new URL[] { new File(path).toURL() });
}
#Override
protected Class<?> findClass(String name) throws ClassNotFoundException
{
System.err.println("findClass(" + name + ")");
return super.findClass(name);
}
#Override
protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
System.err.println("loadClass(" + name + "," + resolve + ")");
return super.loadClass(name, resolve);
}
#Override
public Class<?> loadClass(String name) throws ClassNotFoundException
{
System.err.println("loadClass(" + name + ")");
return super.loadClass(name);
}
}
public static void main(String[] argv)
throws Exception
{
ClassLoader myLoader = new MyClassLoader("/home/kgregory/Workspace/PracticalXml-1.1/target/classes/");
System.out.println("myLoader = " + myLoader);
Class<?> parseUtilKlass = myLoader.loadClass("net.sf.practicalxml.ParseUtil");
Method parseMethod = parseUtilKlass.getDeclaredMethod("parse", String.class);
try
{
parseMethod.invoke(null, "not at all valid XML");
}
catch (InvocationTargetException e)
{
Throwable ee = e.getCause();
System.out.println("exception:" + ee);
System.out.println("exception loader = " + ee.getClass().getClassLoader());
System.out.println("Exception.class loader = " + Exception.class.getClassLoader());
}
}
}
Edit #2, based on today's comments.
A classloader is expected to delegate requests to its parent before it attempts to fulfill the request itself (this is in the ClassLoader JavaDoc). There are a couple of benefits to this practice, foremost being that you won't unintentionally load incompatible instances of the same class.
J2EE classloaders amend this model: the classloader used to load a WAR will attempt to resolve classes before the loader for a containing EAR, which in turn attempts to resolve classes before the container's classloader. The goal here is isolation: if both the WAR and its EAR contain the same library, it's probably because the two need differing versions (that, or they have a sloppy build process). Even in the J2EE case, I believe that the container classloader delegates in the standard way.
In your code the call to super.loadClass() delegates the loading of the class to the parent classloader (just look at the implementation of java.lang.ClassLoader#loadClass). So it is not your instance of DynClassLoader0 that loads the class, but the currentThreadClassLoader (which you took from Thread.currentThread().getContextClassLoader()) that you passed as a constructor parameter to DynClassLoader0. And when the loaded class refers to other classes, they are then also loaded by that classloader and not your DynClassLoader0.

Categories