How does one access a method from an external jar at runtime? - java

This is a continuation of the question posted in: How to load a jar file at runtime
I am uncertain as to how to continue to the method invocation level. From my understanding,
from the clazz object, I would used getMethod or getDeclaredMethod to get a Method object from which I would call invoke. Of course, invoke requires an instance. Would that then be what is called doRun in the example code?
Do I need to perform the doRun.run() method call even though I want to execute a different method than main (assuming that it is main on the doRun object that is called with the run invocation)?
Just for more clarification of the original post, I ask:
Does doRun.run() start a new thread executing the instance of the class object of type clazz?
Thanks for helping to clear this up for me.
I did look at "how-should-i-load-jars-dynamically-at-runtime" (sorry, only allowed one hyperlink), however this looked to violate the Class.newInstance evilness admonition in the first post I referenced.

Here is some reflection code that doesn't cast to an interface:
public class ReflectionDemo {
public void print(String str, int value) {
System.out.println(str);
System.out.println(value);
}
public static int getNumber() { return 42; }
public static void main(String[] args) throws Exception {
Class<?> clazz = ReflectionDemo.class;
// static call
Method getNumber = clazz.getMethod("getNumber");
int i = (Integer) getNumber.invoke(null /* static */);
// instance call
Constructor<?> ctor = clazz.getConstructor();
Object instance = ctor.newInstance();
Method print = clazz.getMethod("print", String.class, Integer.TYPE);
print.invoke(instance, "Hello, World!", i);
}
}
Writing the reflected classes to an interface known by the consumer code (as in the example) is generally better because it allows you to avoid reflection and take advantage of the Java type system. Reflection should only be used when you have no choice.

The code example
ClassLoader loader = URLClassLoader.newInstance(
new URL[] { yourURL },
getClass().getClassLoader()
);
Class<?> clazz = Class.forName("mypackage.MyClass", true, loader);
Class<? extends Runnable> runClass = clazz.asSubclass(Runnable.class);
// Avoid Class.newInstance, for it is evil.
Constructor<? extends Runnable> ctor = runClass.getConstructor();
Runnable doRun = ctor.newInstance();
doRun.run();
assumes that the class you are loading implements a particular interface Runnable, and therefore it's reasonale to cast to that type using asSubclass() and invoke run().
What do you know about the classes you are loading? Can you assume that they implement a particualr interface? If so adjust the asSubClass() line to reference the interafce you prefer.
Then, yes if you are working with instance methods create an instance using the contructor, ctor in the example.
There is no starting of a thread in the example. Creating a new thread would just have needed a couple of lines more code
Thread myThread = new Thread(doRun);
myThread.start();

Sample Program:
Project Printer:
public class Printer {
public void display(String printtext)
{
System.out.println(printtext);
}
}
This project is exported as Printer.jar.
Printer Class has method display() which takes string as input.
Invoking code:
URL url = new URL("file:Printer.jar");
URLClassLoader loader = new URLClassLoader (new URL[] {url});
Class<?> cl = Class.forName ("Printer", true, loader);
String printString = "Print this";
Method printit = cl.getMethod("display", String.class);
Constructor<?> ctor = cl.getConstructor(); //One has to pass arguments if constructor takes input arguments.
Object instance = ctor.newInstance();
printit.invoke(instance, printString);
loader.close ();
Output:
Print this

Related

Get value of System.out.println from main method called via reflection

I'm calling the main method of a class via reflection. For example:
Object o = clasz.getDeclaredConstructor().newInstance();
Method method = clasz.getMethod("main", String[].class);
method.invoke(o, new String[1]);
The called code looks as:
public class Test {
public static void main(String[] args) {
System.out.println("This is a test");
}
}
The reflection works fine and I can see the message in the console.
Is there a way to register something like a binding to the method invocation, for example a PrintWriter or a custom decorated Writer, so I can get the print value as a String?
You can change what System.out is bound to using System.setOut();. You can then make your own:
public class MyTeeingPrinter extends OutputStream {
private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
private final PrintStream original;
public MyTeeingPrinter(PrintStream original) {
this.original = original;
}
#Override public void write(int b) {
original.write(b);
buffer.write(b);
}
public String getAndClear() {
String s = buffer.toString(StandardCharsets.UTF_8);
buffer.reset();
return s;
}
}
And then:
MyTeeingPrinter tee = new MyTeeingPrinter();
System.setOut(new PrintStream(tee));
and now you can invoke tee.getAndClear().
It's a bit of a slog, because whatever code you are running like this is presumably badly designed - it should have instead taken a PrintStream or preferrably an Appendable or Writer, and would write into this writer. Then a trivial one-liner main can be made that just tosses System.out into a writer and hands that to this code you're attempting to run for the case where you just want that code to run and write to sysout, and you can make your own (and stop using reflecting to invoke that main method) and hand that to this code you are running in this scenario.
Note that your reflective code 'works' but is bizarre. There is no need to make a new instance; main is static. The right way is:
Method method = clasz.getMethod("main", String[].class);
method.invoke(null, new String[1]);
That main() method is called in the same process, hence, you can just provide your own stdout implementation/decorator via java.lang.System.setOut(PrintStream) before the reflection magic
An empty string array would work: new String[1] -> new String[0]
You don't need to create a new object to call the static method. Even though java allows calling static methods via objects, this is a bad style and sometimes might cause problems because of name shadowing. Consider the example below:
public class Parent {
public static void main(String[] args) {
Parent child = new Child();
child.test();
}
public static void test() {
System.out.println("Parent.test()");
}
}
class Child extends Parent {
public static void test() {
System.out.println("Child.test()");
}
}
It actually calls Parent.test() even though it's invoked on a Child object

Java Class.forName won't compile. Getting "cannot find symbol symbol : method"

I'm trying to use Class.forName and my Intellij is throwing a compile error. My IntelliJ highlights "theResponse" in red (in testMethod) and gives me this error:
cannot find symbol symbol : method
Here is the code (and test) I'm working with...
package http.response;
public class TestClass {
public TestClass() {
PublicRoute publicRoute = new PublicRoute();
}
public String testMethod() throws ClassNotFoundException {
Class c = Class.forName("http.response.PublicRoute");
return c.theResponse("hi");
}
}
package http.response;
import org.junit.Test;
import static junit.framework.Assert.assertEquals;
public class TestClassTest {
#Test
public void test() throws ClassNotFoundException {
TestClass testClass = new TestClass();
assertEquals("public", testClass.testMethod());
}
}
UPDATE: What I was trying to do was "polymorphically" call theResponse from the class that is returned as a String from a HashMap. How would I do this? I'm (loosely) following this example but I didn't understand it fully (http://sourcemaking.com/refactoring/replace-conditional-with-polymorphism). Here is a simplified version of what I'm trying to do. Hopefully that makes sense.
package http.response;
import java.util.HashMap;
public class TestClass {
HashMap map;
public TestClass(HashMap map) {
this.map = map;
}
public String testMethod(String lookupValue) throws ClassNotFoundException {
String className = map.get(lookupValue);
Class c = Class.forName("http.response." + className);
return c.theResponse();
}
}
Class.forName() returns an object of type java.lang.Class. java.lang.Class has no method theResponse, as you can see from its Javadoc.
It sounds like what you actually want to do is construct an instance of the PublicRoute class, and call the method on the instance. But you've already constructed such an instance: it's the publicRoute variable you create in your constructor. Why not just use that object instead?
Edit: Ah, I see what you're trying to do. You basically want a form of the Service Locator pattern.
Create an interface, like so:
public interface ResponseProvider {
String theResponse();
}
Then make all your classes implement that interface:
public class PublicRoute implements ResponseProvider {
#Override
public String theResponse() {
// do whatever
}
}
Then, when you load your Class<?>, you can use the asSubclass() method to turn your Class<?> into a Class<? extends ResponseProvider> -- then newInstance() will give you back a ResponseProvider object that you can call theResponse() on, like so:
String className = ...;
Class<?> klass = Class.forName(className);
Class<? extends ResponseProvider> responseProviderClass
= klass.asSubclass(ResponseProvider.class);
ResponseProvider responseProvider = responseProviderClass.newInstance();
return responseProvider.theResponse();
But don't do that by hand -- instead, use the java.util.ServiceLoader class, which is designed for exactly this purpose. You create a special META-INF/services/com.my.package.ResponseProvider file, with a list of all the possible classes that implement that interface, and then ServiceLoader can give you back instances of each of them.
But... consider not doing that, either. The types of problems that you can solve with the Service Locator pattern are often better solved by using Dependency Injection (see also my answer to another question about Dependency Injection). The Guice DI framework, for example, offers a feature called multibindings which looks like exactly what you need.
If theResponse() belongs to http.response.PublicRoute then it should have been
Class c = Class.forName("http.response.PublicRoute");
return ((PublicRoute) c.newInstance()).theResponse("hi");
But, then there's really no need for Class.forName() as you could use constructor as
return new PublicRoute().theResponse("hi");
The class Class does not have a method named theResponse. From the rest of your code, it doesn't look like you should be using reflection here; you're already referring statically to the PublicRoute class, so there's no point loading it dynamically.
I think you just want to write either this:
return PublicRoute.theResponse("hi");
or this:
return new PublicRoute().theResponse("hi");
(depending whether theResponse is a static method or an instance method).
Let me see if I understand what you're trying to do. You've got a hashmap that will contain a list of classes that you're going to try to call the theResponse(String response) method on, right? I'm assuming you won't know the String that will be put into the hashmap either, right?
Others are right in that you can't just do:
Class c = Class.forName("http.response.PublicRoute");
c.theResponse("hi"); // errors because c has no knowledge of theResponse()
You'll need to cast c to http.response.PublicRoute but then as #Ravi Thapliyal pointed out, you won't need Class.forName anymore! You've got a hashmap of names that could potentially be anything so this won't work.
If I'm understanding you correctly to do what you need, you'll need to use reflection in order to attempt to instance the class then call the method on it.
Here's how you'd do it assuming the theResponse method is a public non-static method and has only 1 parameter.
// Declare the parameter type
Class[] paramString = new Class[1];
paramString[0] = String.class;
String className = map.get(lookupValue);
// Instance the class
Class cls = Class.forName("http.response." + className);
Object obj = cls.newInstance();
// Call the method and pass it the String parameter
method = cls.getDeclaredMethod("theResponse", paramString);
method.invoke(obj, new String("hi"));
Of course you'll need to handle Exceptions but you'd surround the above code with the loop for your hashmap.
I hope this helps!

Reflection type mismatch

I have compiled a class at runtime which I would like to use on the fly, provided that its constructor takes one parameter
package com.notmycompany;
import com.mycompany.Manager;
import com.mycompany.Processor;
import com.mycompany.Event;
public class CustomProcessor extends Processor {
public CustomProcessor( Manager m) {
super( m);
}
#Override
public void process( Event evt) {
// Do you own stuff
System.out.println( "My Own Stuff");
}
}
Compilation goes fine and I can load the class right away. But the constructor is giving me a hard time.
Class<?> clazz = urlClassLoader.loadClass("com.notmycompany.CustomProcessor");
Constructor<?> constructor = clazz.getConstructor( com.mycompany.Manager.class);
this.customProcessor = (Processor) constructor.newInstance( this.manager);
In this case, getConstructor throws a NoSuchMethodException
I tried using getConstructors instead, which only gets me one step further with IllegalArgumentException during newInstance call (of course this.manager is com.mycompany.Manager)
Constructor<?> list[] = clazz.getConstructors();
Constructor<?> constructor = list[0];
this.customProcessor = (Processor) constructor.newInstance( this.manager);
Watever I do, there is a mismatch between Manager object at runtime and compilation
How can I fix this constructor signature?
Edit 1: getParameterTypes output
for( Class<?> c : constructor.getParameterTypes()) {
System.out.println( c);
}
outputs
class com.mycompany.Manager
Edit 2: I removed constructor parameter as a temporary workaround
Now the code throws ClassCastException complaining that com.notmycompany.CustomProcessor cannot be cast to com.mycompany.Processor when constructor is invoked:
Constructor<?> constructor = clazz.getConstructor();
this.customProcessor = (Processor) constructor.newInstance();
This all seems to be part of the same problem where runtime classes seem inconsistent with compilation's, although names match.
Your CustomProcessor class has no constructor because the name of the method you believe to be your constructor is different.
public CustomLatencyProcessor(Manager m) {
super(m);
}
Should be changed to
public CustomProcessor(Manager m) {
super(m);
}
Because the name of your class is CustomProcessor. Constructor's names must match the name of their containing class exactly.
I have been able to get it to work eventually after using a URL that uses the currentThread as parent (as opposed to a URLClassLoader created from scratch with URLs)
URLClassLoader ucl = (URLClassLoader)Thread.currentThread().getContextClassLoader();
URLClassLoader ucl2 = new URLClassLoader( new URL[] { new URL( "file://d:/temp/")},ucl);
Class<?> clazz = ucl2.loadClass("com.notmycompany.CustomProcessor");
I hope this can save you 2 days!

Trouble invoking a non-static method through reflection (Java)

I'm having problems invoking non-static methods through reflection. My code is below. When I try to do "ClassnameRemoved.printMessageToLogger(Level.INFO, "Test");", I get "Could not find method 'log' in class Logger. This is a normal Java class, so you are probably using a modified/outdata Java version.". Thanks in advance!
private static void printMessageToLogger(Level lvl, String message) {
try{
Class<?> clazz = Class.forName("net.packgeName.omitted.Main");
Field logger = clazz.getDeclaredField("tcLog");
Method logMethod = logger.getDeclaringClass().getDeclaredMethod("log", Level.class, String.class);
logMethod.invoke(logger, lvl, message);
}
// catch methods omitted to save space
}
If the method is not static, you need an instance of the class.
Look at this example:
Class classDefinition = Class.forName(className);
object = classDefinition.newInstance();

Overriding default accessor method across different classloaders breaks polymorphism

I come across to a strange behavior while trying to override a method with default accessor (ex: void run()).
According to Java spec, a class can use or override default members of base class if classes belongs to the same package.
Everything works correctly while all classes loaded from the same classloader.
But if I try to load a subclass from separate classloader then polymorphism don't work.
Here is sample:
App.java:
import java.net.*;
import java.lang.reflect.Method;
public class App {
public static class Base {
void run() {
System.out.println("error");
}
}
public static class Inside extends Base {
#Override
void run() {
System.out.println("ok. inside");
}
}
public static void main(String[] args) throws Exception {
{
Base p = (Base) Class.forName(Inside.class.getName()).newInstance();
System.out.println(p.getClass());
p.run();
} {
// path to Outside.class
URL[] url = { new URL("file:/home/mart/workspace6/test2/bin/") };
URLClassLoader ucl = URLClassLoader.newInstance(url);
final Base p = (Base) ucl.loadClass("Outside").newInstance();
System.out.println(p.getClass());
p.run();
// try reflection
Method m = p.getClass().getDeclaredMethod("run");
m.setAccessible(true);
m.invoke(p);
}
}
}
Outside.java: should be in separate folder. otherwise classloader will be the same
public class Outside extends App.Base {
#Override
void run() {
System.out.println("ok. outside");
}
}
The output:
class App$Inside
ok. inside
class Outside
error
ok. outside
So then I call Outside#run() I got Base#run() ("error" in output). Reflections works correctly.
Whats wrong? Or is it expected behavior?
Can I go around this problem somehow?
From Java Virtual Machine Specification:
5.3 Creation and Loading
...
At run time, a class or interface is
determined not by its name alone, but
by a pair: its fully qualified name
and its defining class loader. Each
such class or interface belongs to a
single runtime package. The runtime
package of a class or interface is
determined by the package name and
defining class loader of the class or
interface.
5.4.4 Access Control
...
A field or method R is accessible to a class
or interface D if and only if any of
the following conditions is true:
...
R is either protected or package private (that is, neither public nor
protected nor private), and is
declared by a class in the same
runtime package as D.
The Java Language Specification mandates that a class can only override methods that it can access. If the super class method is not accessible, it is shadowed rather than overridden.
Reflection "works" because you ask Outside.class for its run method. If you ask Base.class instead, you'll get the super implementation:
Method m = Base.class.getDeclaredMethod("run");
m.setAccessible(true);
m.invoke(p);
You can verify that the method is deemed inaccessible by doing:
public class Outside extends Base {
#Override
public void run() {
System.out.println("Outside.");
super.run(); // throws an IllegalAccessError
}
}
So, why is the method not accessible? I am not totally sure, but I suspect that just like equally named classes loaded by different class loaders result in different runtime classes, equally named packages loaded by different class loaders result in different runtime packages.
Edit: Actually, the reflection API says that it's the same package:
Base.class.getPackage() == p.getClass().getPackage() // true
I found the (hack) way to load external class in main classloader so this problem is gone.
Read a class as bytes and invoke protected ClassLoader#defineClass method.
code:
URL[] url = { new URL("file:/home/mart/workspace6/test2/bin/") };
URLClassLoader ucl = URLClassLoader.newInstance(url);
InputStream is = ucl.getResourceAsStream("Outside.class");
byte[] bytes = new byte[is.available()];
is.read(bytes);
Method m = ClassLoader.class.getDeclaredMethod("defineClass", new Class[] { String.class, byte[].class, int.class, int.class });
m.setAccessible(true);
Class<Base> outsideClass = (Class<Base>) m.invoke(Base.class.getClassLoader(), "Outside", bytes, 0, bytes.length);
Base p = outsideClass.newInstance();
System.out.println(p.getClass());
p.run();
outputs ok. outside as expected.

Categories

Resources