File.getClass().getMethod(): how to get .class and method - java

I am using JUnit4 and I am trying to set up a test that can be used for multiple classes that are identical(not important why they all are), but I am passing in multiple java files to the test and from that i am trying to create objects that have both the .class and the name of a method in the method eg. list.add(new Object[]{testClass.class, testClass.class.methodName()}); It works fine if you enter the name of the .class and the name of the method exactly as is(as in the example above) but as I want to do this for a number of different classes I need to pass them in in a loop and i am using the following code list.add(new Object[]{currentFile.getClass(), currentFile.getClass().getMethod(addTwoNumbers,int, int)} where currentFile is the current file being processed and .getMethod(addTwoNumbers,int, int) addTwoNumbers is the name of the method which takes two ints eg. addTwoNumbers(int one, int two) but I am getting the following error
'.class' expected
'.class' expected
unexpected type
required: value
found: class
unexpected type
required: value
found: class
Here is my full code
CompilerForm compilerForm = new CompilerForm();
RetrieveFiles retrieveFiles = new RetrieveFiles();
#RunWith(Parameterized.class)
public class BehaviorTest {
#Parameters
public Collection<Object[]> classesAndMethods() throws NoSuchMethodException {
List<Object[]> list = new ArrayList<>();
List<File> files = new ArrayList<>();
final File folder = new File(compilerForm.getPathOfFileFromNode());
files = retrieveFiles.listFilesForFolder(folder);
for(File currentFile: files){
list.add(new Object[]{currentFile.getClass(), currentFile.getClass().getMethod(addTwoNumbers,int, int)});
}
return list;
}
private Class clazz;
private Method method;
public BehaviorTest(Class clazz, Method method) {
this.clazz = clazz;
this.method = method;
}
Does anyone see what I am doing wrong with this line list.add(new Object[]{currentFile.getClass(), currentFile.getClass().getMethod(addTwoNumbers,int, int)});
}?

I believe that you need to load the file first using a ClassLoader and then create it so you can use reflection on the class. Here's a similar post with an answer that has more info on this. How to load an arbitrary java .class file from the filesystem and reflect on it?
Here's some more info on this:
A Look At The Java Class Loader
Dynamic Class Loading and Reloading in Java
And here's a quick example using URLClassLoader
// Create a File object on the root of the directory containing the class file
File file = new File("c:\\myclasses\\");
try {
// Convert File to a URL
URL url = file.toURL(); // file:/c:/myclasses/
URL[] urls = new URL[]{url};
// Create a new class loader with the directory
ClassLoader cl = new URLClassLoader(urls);
// Load in the class; MyClass.class should be located in
// the directory file:/c:/myclasses/com/mycompany
Class cls = cl.loadClass("com.mycompany.MyClass");
} catch (MalformedURLException e) {
} catch (ClassNotFoundException e) {
}
the example was taken from:
Loading a Class That Is Not on the Classpath

Related

Java JavaCompiler.run() compiling anonymous classes as well

I am trying to load in text files on the fly and compile them.
File file = new File("Files/"+fileName+".java");
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
compiler.run(null, null, errStream, file.getAbsolutePath());
I then will load the compiled .class files later:
public Class loadStrategyClass(File strategyClassFile) throws IOException
{
FileChannel roChannel = new RandomAccessFile(strategyClassFile, "r").getChannel();
ByteBuffer buffer = roChannel.map(FileChannel.MapMode.READ_ONLY, 0, (int)roChannel.size());
return defineClass(strategyClassFile.getName(), buffer, (ProtectionDomain)null);
}
I am currently running into two issues:
The first is if the .java files I load in contain anonymous classes. It doesn't appear that the JavaCompiler class will compile these.
Exception in thread "main" java.lang.IllegalAccessException: Class Loader.ClassLoader can not access a member of class Files.myname.myclass$1 with modifiers ""
The second:
Is that sometimes I will get errors for NoClassDefFoundError:
Exception in thread "main" java.lang.NoClassDefFoundError: Files/myname/myclass
Despite the fact that other classes will load correctly and the .class file is in that path.
Apparently, your loadStrategyClass is defined within a custom ClassLoader. The problem is that it is not enough to call defineClass once for the class you’re interested in, your class loader must be able to resolve classes on demand, usually by implementing findClass, so the JVM can resolve dependencies, like the inner classes.
You didn’t specify, how you get the strategyClassFile argument for the loadStrategyClass method. Since you ran the compiler without any options, I suppose you simply looked up the file relative to the source file. To resolve other dependencies, the actual root of the class directory needs to be known. It becomes much easier when you define where to store the class files, e.g.
// customize these, if you want, null triggers default behavior
DiagnosticListener<JavaFileObject> diagnosticListener = null;
Locale locale = null;
JavaCompiler c = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fm
= c.getStandardFileManager(diagnosticListener, locale, Charset.defaultCharset());
// define where to store compiled class files - use a temporary directory
Path binaryDirectory = Files.createTempDirectory("compile-test");
fm.setLocation(StandardLocation.CLASS_OUTPUT,
Collections.singleton(binaryDirectory.toFile()));
JavaCompiler.CompilationTask task = c.getTask(null, fm,
diagnosticListener, Collections.emptySet(), Collections.emptySet(),
// to make this a stand-alone example, I use embedded source code
Collections.singleton(new SimpleJavaFileObject(
URI.create("string:///Class1.java"), Kind.SOURCE) {
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return "package test;\npublic class Class1 { public class Inner {} }";
}
}));
if(task.call()) try {
URLClassLoader cl = new URLClassLoader(new URL[]{ binaryDirectory.toUri().toURL() });
Class<?> loadedClass = cl.loadClass("test.Class1");
System.out.println("loaded "+loadedClass);
System.out.println("inner classes: "+Arrays.toString(loadedClass.getClasses()));
} catch(ClassNotFoundException ex) {
ex.printStackTrace();
}
In the example above, we know the root of the class directory, because we have defined it. This allows to simply use the existing URLClassLoader rather than implementing a new type of class loader. Of course, using a custom file manager, we also could use an in-memory storage for rather than a temporary directory.
You may use this API to discover what has been generated, which enables you to use the resulting class without knowing beforehand, which package or inner class declarations exist in the source file you’re going to compile.
public static Class<?> compile(
DiagnosticListener<JavaFileObject> diagnosticListener,
Locale locale, String sourceFile) throws IOException, ClassNotFoundException {
JavaCompiler c = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fm
= c.getStandardFileManager(diagnosticListener, locale, Charset.defaultCharset());
// define where to store compiled class files - use a temporary directory
Path binaryDirectory = Files.createTempDirectory("compile-test");
fm.setLocation(StandardLocation.CLASS_OUTPUT,
Collections.singleton(binaryDirectory.toFile()));
JavaCompiler.CompilationTask task = c.getTask(null, fm,
diagnosticListener, Collections.emptySet(), Collections.emptySet(),
fm.getJavaFileObjects(new File(sourceFile)));
if(task.call()) {
Class<?> clazz = null;
URLClassLoader cl = new URLClassLoader(new URL[]{binaryDirectory.toUri().toURL()});
for(JavaFileObject o: fm.list(
StandardLocation.CLASS_OUTPUT, "", Collections.singleton(Kind.CLASS), true)) {
String s = binaryDirectory.toUri().relativize(o.toUri()).toString();
s = s.substring(0, s.length()-6).replace('/', '.');
clazz = cl.loadClass(s);
while(clazz.getDeclaringClass() != null) clazz = clazz.getDeclaringClass();
if(Modifier.isPublic(clazz.getModifiers())) break;
}
if(clazz != null) return clazz;
throw new ClassNotFoundException(null,
new NoSuchElementException("no top level class generated"));
}
throw new ClassNotFoundException(null,
new NoSuchElementException("compilation failed"));
}
If you use this to dynamically bind plugins or modules, you may extend the search to look for a result class which implements a particular interface or has a certain annotation.

Java 9 - add jar dynamically at runtime

I've got a classloader problem with Java 9.
This code worked with previous Java versions:
private static void addNewURL(URL u) throws IOException {
final Class[] newParameters = new Class[]{URL.class};
URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
Class newClass = URLClassLoader.class;
try {
Method method = newClass.getDeclaredMethod("addNewURL", newParameters );
method.setAccessible(true);
method.invoke(urlClassLoader, new Object[]{u});
} catch (Throwable t) {
throw new IOException("Error, could not add URL to system classloader");
}
}
From this thread I learned that this has to be replaced by something like this:
Class.forName(classpath, true, loader);
loader = URLClassLoader.newInstance(
new URL[]{u},
MyClass.class.getClassLoader()
MyClass is the class I'm trying to implement the Class.forName() method in.
u = file:/C:/Users/SomeUser/Projects/MyTool/plugins/myNodes/myOwn-nodes-1.6.jar
String classpath = URLClassLoader.getSystemResource("plugins/myNodes/myOwn-nodes-1.6.jar").toString();
For some reason - I really can't figure out, why - I get a ClassNotFoundException when running Class.forName(classpath, true, loader);
Does someone know what I'm doing wrong?
From the documentation of the Class.forName(String name, boolean initialize, ClassLoader loader) :-
throws ClassNotFoundException - if the class cannot be located by the specified class loader
Also, note the arguments used for the API includes the name of the class using which the classloader returns the object of the class.
Given the fully qualified name for a class or interface (in the same format returned by getName) this method attempts to locate, load, and link the class or interface.
In your sample code, this can be redressed to something like :
// Constructing a URL form the path to JAR
URL u = new URL("file:/C:/Users/SomeUser/Projects/MyTool/plugins/myNodes/myOwn-nodes-1.6.jar");
// Creating an instance of URLClassloader using the above URL and parent classloader
ClassLoader loader = URLClassLoader.newInstance(new URL[]{u}, MyClass.class.getClassLoader());
// Returns the class object
Class<?> yourMainClass = Class.forName("MainClassOfJar", true, loader);
where MainClassOfJar in the above code shall be replaced by the main class of the JAR myOwn-nodes-1.6.jar.

Java - Strange behaviour with dynamic class loader

In my code I want to dynamically load Module class implementations from Jar files.
In my directory I have 3 files: A.jar, B.jar, C.jar
Each jar has one class called Main which extends Module class
A.jar code example:
public class Main extends Module {
private static String name = "A";
public Main() {
super(name);
}
}
(B and C files are the same but with "B" and "C" instead of "A" in the name property).
My Module class code is:
public abstract class Module{
private StringProperty nameProperty;
public Module(String name){
this.nameProperty = new SimpleStringProperty(name);
}
public StringProperty nameProperty(){
return nameProperty;
}
}
This is the code that I use to dynamically load the three classes:
for (File moduleFile : Data.modulesDir.listFiles()) {
try {
URL url = moduleFile.toURI().toURL();
Class[] parameters = new Class[] { URL.class };
URLClassLoader sysLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
Class<URLClassLoader> sysClass = URLClassLoader.class;
Method method = sysClass.getDeclaredMethod("addURL", parameters);
method.setAccessible(true);
method.invoke(sysLoader, new Object[] { url });
Constructor<?> cs = ClassLoader.getSystemClassLoader().loadClass("com.ehzlab.webreaper.module.Main")
.getConstructor();
Module instance = (Module) cs.newInstance();
System.out.println(instance.nameProperty.get());
} catch (Exception e) {
e.printStackTrace();
}
}
I expect this ouput:
A
B
C
but I get this instead:
A
A
A
It seems like that loads the same jar at each file list iteration. But debugging I noted that the URL changes every time.
I also tried inverting the order, for example, placing B.jar before the other jar, and the output is:
B
B
B
Why?
Simply because you are using same classloader each time, which doesn't reload underlying classes:
ClassLoader.getSystemClassLoader()...
In order to get access to specific classes, you have to use appropriate classloader used for loading particular jar file (may be it is sysLoader, not sure, as I didn't check):
Constructor<?> cs = sysLoader.loadClass("com.ehzlab.webreaper.module.Main")
.getConstructor();
Look at this question as well: How should I load Jars dynamically at runtime?

Why isn't my class being loaded through the external class loader?

I want to run the constructor of the Main.class in the package Test2, located in the folder C:\classes\
This is the code I'm using. It throws a class not found exception when it tries to turn it into a class. And then once it's part of the class object, will the constructor automatically be run, or do I have to instance it somehow? Test2 is inputted into this code as text.
if (Main.os.equals("Windows"))
{
String path = "C:\\classes\\";
}
else
{
String path = "~/classes/";
}
File file = new File(path);
try
{
URL url = file.toURI().toURL();
URL[] urls = new URL[]{url};
Main.print("Stage 1");
ClassLoader cl = new URLClassLoader(urls);
Main.print("Stage 2");
Class cls = cl.loadClass(text + ".Main");
Main.print(text + " was loaded into memory.");
close();
}
catch (MalformedURLException e)
{
e.printStackTrace();
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
I suspect your problem is one of the following:
file doesn't exist or hasn't been properly specified. Check via file.exists()
Your class file is not located in the correct directory. If the package declaration for the Main class is package Test2; then your class file must be in the following location: C:\classes\Test2\Main.class.
If Main is nested class, then you will need to refer to the enclosing class when loading it, eg cl.loadClass("Test2.EnclosingClass$Main");
My guess it that your problem is number 2! :)
Good luck.
Oh, and yes, you'll need to create an instance of your object if you want the constructor to be called: clazz.newInstance() is the simplest method for no-args constructors.
Can you post the exact error message.
But here is how I execute a main method of using a class loader
urlLoader = new URLClassLoader(urls);
Class runClass = urlLoader.loadClass(classToRun);
System.out.println("Starting Program !!!");
Object[] arguments = new Object[]{args};
Method mainMethod = runClass.getMethod("main", new Class[] {args.getClass()});
mainMethod.invoke(null, arguments);
Note: classToRun will be the full package/class definition
i.e. net.sf.RecordEditor.edit.FullEditor
Note: I use it to load from jar files, it will be similar for directories
It is taken from the run class here
http://record-editor.svn.sourceforge.net/viewvc/record-editor/Source/RecordEditor/src/net/sf/RecordEditor/utils/Run.java?revision=65&view=markup
An example of calling the class is here
http://record-editor.svn.sourceforge.net/viewvc/record-editor/Source/RecordEditor/src/net/sf/RecordEditor/RunFullEditor.java?revision=65&view=markup

java dynamic classloader

how can I dynamically load a class in Java with two parameters which are the absolute filepath of the class file and the name of the method I wish to call?
eg path: c:\foo.class
method: print()
I am just interested in the basics as a simple cmd line tool. A code example would b appreciated.
cheers hoax
Use URLClassLoader. The name of the method is irrelevant. You must pass the root directory of your package to the class loader. Then you can use the fully qualified class name (package + class name) in Class.forName() to get the Class instance. You can use the normal reflection calls to create an instance of this class and call methods on it.
To make your life more simple, have a look at commons-beanutils. It makes invoking methods much more simple.
Check out this example:
// Create a File object on the root of the directory containing the class file
File file = new File("c:\\myclasses\\");
try {
// Convert File to a URL
URL url = file.toURL(); // file:/c:/myclasses/
URL[] urls = new URL[]{url};
// Create a new class loader with the directory
ClassLoader cl = new URLClassLoader(urls);
// Load in the class; MyClass.class should be located in
// the directory file:/c:/myclasses/com/mycompany
Class cls = cl.loadClass("com.mycompany.MyClass");
} catch (MalformedURLException e) {
} catch (ClassNotFoundException e) {
}
After this, you could do something like this to first create a new instace using the default constructor and invoking the method "print" without arguments:
Object object = cls.newInstance();
cls.getMethod("print").invoke(object);

Categories