I am using JavaCompiler to compile CustomProcessor.java from within a web application at runtime
package com.notmycompany;
import com.mycompany.Processor;
import com.mycompany.Event;
public class CustomProcessor extends Processor {
#Override
public void process( Event evt) {
// Do you own stuff
System.out.println( "My Own Stuff");
}
}
Compilation goes well, I end up with a class file that I am trying to load using URLClassLoader
URL[] urls = new URL[]{ new URL("file://d:/temp/") };
URLClassLoader ucl = new URLClassLoader(urls);
Class clazz = ucl.loadClass("com.notmycompany.CustomProcessor");
The problem is I am hitting a ClassNotFoundException on Processor which I am using elsewhere in the application (as in I known it's there).
What do I need to do so com.mycompany.Processor is visible at runtime?
Caused by: java.lang.ClassNotFoundException: com.mycompany.Processor
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 30 more
Thanks.
It is a matter of using a URLClassLoader fed with a complete classpath
As per this question I used
URLClassLoader ucl2 = new URLClassLoader( new URL[] { new URL( "file://d:/temp/")}, urlClassLoader);
Class<?> clazz = ucl2.loadClass("com.notmycompany.CustomProcessor");
Related
I have a jar file which contains only testcase.class files. I need to parse that and find all the classes defined in that jar file and need to generate one report. I tried to do it with the reflection concept in Java. Some how, when loading a class is fine, but trying to get the declared methods of it, I am getting the following error. (Note class is loaded, I am able to get the name and canonical name of the loaded class but I error at trying to get declared methods).
for (String clazz: classes) {
String className = clazz.replaceAll("/",
".").replaceAll(".class","");
try {
URL url = file.toURI().toURL();
URL[] urls = new URL[]{url};
URLClassLoader cl = new URLClassLoader(urls);
Class cls = cl.loadClass(className);
System.out.println("##############################");
cls.getDeclaredMethods();
//clazzes.add(getClassObject(cls));
//break;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
Exception in thread "main" java.lang.NoClassDefFoundError: javax/mail/Authenticator
at java.base/java.lang.ClassLoader.defineClass1(Native Method)
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1016)
at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:174)
at java.base/java.net.URLClassLoader.defineClass(URLClassLoader.java:555)
at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:458)
at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:452)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:451)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:588)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
at com.springboot.example.springBootExample.service.JarParserService.getTypes(JarParserService.java:99)
at com.springboot.example.springBootExample.service.JarParserService.constructPackages(JarParserService.java:86)
at com.springboot.example.springBootExample.service.JarParserService.getClassTypes(JarParserService.java:78)
at com.springboot.example.springBootExample.service.JarParserService.scanJar(JarParserService.java:61)
at com.springboot.example.springBootExample.service.JarParserService.getAllPackages(JarParserService.java:34)
at com.springboot.example.springBootExample.service.FindClassesFromJarService.parseJar(FindClassesFromJarService.java:61)
at com.springboot.example.springBootExample.service.FindClassesFromJarService.main(FindClassesFromJarService.java:31)
Caused by: java.lang.ClassNotFoundException: javax.mail.Authenticator
at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:476)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:588)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
... 17 more
Process finished with exit code 1
I have a base and sub class. The requirement is to invoke sub class' method using reflection. Both programs are below.
base.java
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
public class base {
public static void main(String[] args) {
URLClassLoader loader = null;
Class<?> cls;
try {
File file = new File("sub.jar" );
URL[] urls = { file.toURI().toURL() };
loader = new URLClassLoader(urls);
cls = loader.loadClass("sub");
base obj = (base) cls.newInstance();
obj.print();
}
catch(Exception e) {
System.out.println("Exception occured:" + e);
e.printStackTrace();
}
}
public void print() {
System.out.println("In Base class");
}
}
sub.java
public class sub extends base {
public void print() {
System.out.println("In subclass");
}
}
compile both in to jars.
javac base.java;
jar cvf base.jar base.class
javac sub.java;
jar cvf sub.jar sub.class
If I invoke the base.jar as "java -cp", it works fine
java -cp base.jar base
output: "In subclass"
But if I invoke it with "hadoop jar" command, I get
hadoop jar base.jar base
Exception in thread "main" java.lang.NoClassDefFoundError: base
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at base.main(base.java:15)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.hadoop.util.RunJar.run(RunJar.java:221)
at org.apache.hadoop.util.RunJar.main(RunJar.java:136)
Caused by: java.lang.ClassNotFoundException: base
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
Any help is greatly appreciated.
I believe your issue is on line 15 where you try to case the loader as your base class. I believe the compiler doesn't know how to handle that. Additionally, I'm not sure why you're trying to run this program with Hadoop since it does not have any of the hadoop implementations or classes
The problem is solved by placing base.jar in hadoop classpath. Run "hadoop classpath" command which lists all classpath dirs. Place the jar in one of these directories.
I want to modifly the class in app which is running. So I use java agent to do this. My java code like this:
/**
*
* #param args
* #param inst
*/
public static void agentmain(String args, Instrumentation inst) {
try {
inst.addTransformer(new GameClassFileTransformer(), true);
parseFiles();
List<Class<?>> retransformClasses = new ArrayList<>();
for (JarClassInfo jarClassInfo : GameAgentMain.bugFiles.values()) {
retransformClasses.add(Class.forName(jarClassInfo.getClassName()));
}
Class<?>[] classes = new Class[retransformClasses.size()];
retransformClasses.toArray(classes);
inst.retransformClasses(classes);
} catch (Exception e) {
LogUtil.error(e);
}
}
The JarClassInfo is a class about the class I want to reload.There hava two fields in JarClassInfo ,first is className,second is bytecode(java type is byte[]),The classname is which class want to reload.But the class is loaded by
custom classLoad
.When the app run at :
retransformClasses.add(Class.forName(jarClassInfo.getClassName()));
That is a exception be thrown:
Exception in thread "Attach Listener" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:382)
at sun.instrument.InstrumentationImpl.loadClassAndCallAgentmain(InstrumentationImpl.java:407)
Caused by: java.lang.ClassFormatError: Incompatible magic value 2329931675 in class file com/jingqi/game/module/chat/command/SendChatMsgCMD
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:800)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
at java.net.URLClassLoader.access$1(URLClassLoader.java:409)
at java.net.URLClassLoader$2.run(URLClassLoader.java:361)
at java.net.URLClassLoader$2.run(URLClassLoader.java:1)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:190)
at com.jingqi.game.agent.GameAgentMain.agentmain(GameAgentMain.java:60)
... 6 more
My custom classloader(EncrypClassLoader) is used to decode my class.
So the methd call stack indicate sun.misc.Launcher$AppClassLoader to load SendChatMsgCMD .The SendChatMsgCMD is not a normal class file(because I encrypt this class file).
So ,what method can be solved 。
Your custom classloader isn't doing what it's supposed to do: decrypt classes.
That's why you're getting the java.lang.ClassFormatError about the invalid magic value.
EDIT: you need to also realise that Class.forName() will load the class referred by the classname argument using the current class's classloader.
I have a Java program based on ASM 5.0.2 to extract dependency between classes. The program works fine with an ordinary Java application. However, when I run the program as a plugin then it crashes with the bug: java.lang.ClassNotFoundException.
As an example if the example class uses junit.Assert, then when I run the project as an ordinary java application, it find this dependency, but when as plugin the below error:
java.lang.ClassNotFoundException: org.junit.Assert
at java.net.URLClassLoader$1.run(URLClassLoader.java:372)
at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.net.FactoryURLClassLoader.loadClass(URLClassLoader.java:798)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:340)
Part of code that I think error is because of that is as below, and the whole code can be find in this link:
class ClassCollector extends Remapper {
static Set<Class<?>> getClassesUsedBy(final String name, final String prefix, File root) throws IOException {
final ClassReader reader = new ClassReader(name);
final Set<Class<?>> classes = new TreeSet<Class<?>> (new Comparator<Class<?>>() {
#Override
public int compare (final Class<?> o1, final Class<?> o2) {
return o1.getName().compareTo (o2.getName());
}
});
final Remapper remapper = new ClassCollector(classes, prefix, root);
final ClassWriter inner = new ClassWriter(ClassWriter.COMPUTE_MAXS);
final RemappingClassAdapter visitor = new RemappingClassAdapter(inner, remapper);
try {
reader.accept(visitor, ClassReader.EXPAND_FRAMES);
}
catch (Exception ex) {
ex.toString();
}
return classes;
}
Important: when I initialized inner (as below) with null, then the program does not crash, but cannot detect all dependencies, and for example cannot detect assert dependency in the above example.
final ClassVisitor inner = null; //new ClassWriter(ClassWriter.COMPUTE_MAXS);
Please let me know if any one knows why the program is correct as an ordinary java application, but crash as plugin.
ClassReader(String name) uses the ClassLoader.loadSystemResourceAsStream() method to access the bytes for a requested class. If the classes you want to analyze are not in the class path, this won't work, since the class path is what loadSystemResourceAsStream searches.
I am making a program that operates as multiple JAR file dependencies. Basically, the thing loops through the .class files in a JAR file and gets a Class object for each of them. Each JAR has a Plugin.class file that I don't want to be available, but I want all the Classes to be accessible by other JAR dependencies as well as the main program. For example, in one JAR I have the class something.somethingelse.SomeClass, and from a second one (I made sure it is loaded second) I want to be able to import (at execution because it's in a separate JARfile) something.somethingelse.SomeClass and use it. I Have tried this after loading it into a Class object but it gives me ClassNotFound errors. I am using the newest java update and the newest version of eclipse IDE. I have three projects, "main", "aaa", and "aab". I have aaa and aab exported to JARs of which the contents are loaded into Class objects by main. aaa is loaded before aab, and I want aab to be able to access the classes from aaa through import aaa.Class. How can I (from main) make the classes of both jarfiles available to each other?
Here is my load plugin function:
public static void load(File file) throws Exception
{
JarFile jarFile = new JarFile(file);
Enumeration e = jarFile.entries();
URL[] urls = new URL[] { file.toURI().toURL() };
ClassLoader cl = new URLClassLoader(urls);
while (e.hasMoreElements()) {
JarEntry je = (JarEntry) e.nextElement();
if(je.isDirectory() || !je.getName().endsWith(".class") || je.getName() == "Plugin.class"){
continue;
}
// -6 because of .class
String className = je.getName().substring(0,je.getName().length()-6);
className = className.replace('/', '.');
Class c = cl.loadClass(className);
}
ClassLoader loader = new URLClassLoader(urls);
Class c = loader.loadClass("Plugin");
Object cobj = c.newInstance();
Method[] allMethods = c.getDeclaredMethods();
Method method = null;
boolean found = false;
for (Method m : allMethods) {
String mname = m.getName();
if (mname == "startPlugin"){
method = m;
found = true;
}
}
if(found)
{
method.invoke(cobj);
}
else
{
//skip class
}
}
And then my first JAR (aaa.jar) declares a class called hlfl.ui.UserInterface.
My second JAR's Plugin class is as follows:
import hlfl.ui.*;
public class Plugin {
//THIS DEPENDENCY EXPORTS TO: aab.jar
public void startPlugin()
{
System.out.println("Plugin Loading Interface Loaded [AAB]");
UserInterface c = new UserInterface();
}
}
But when I run it it gives me the following:
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun. reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at sf.htmlguy.hlaunch.PluginLoader.load(PluginLoader.java:58)
at sf.htmlguy.hlaunch.PluginLoader.loadAll(PluginLoader.java:22)
at sf.htmlguy.hlaunch.HLaunch.main(HLaunch.java:14)
Caused by: java.lang.NoClassDefFoundError: hlfl/ui/UserInterface
at Plugin.startPlugin(Plugin.java:7)
... 7 more
Caused by: java.lang.ClassNotFoundException: hlfl.ui.UserInterface
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 8 more
Just in case, the code is on SourceForge (the three projects are in subdirectories, "hlaunch for linux" is the main one.):
https://sourceforge.net/p/hlaunch/code
As far as I can tell your load method is creating a URLClassLoader containing just one JAR file. So you're going to end up with a classloader structure like this
main
/ \
/ \
UCL with aaa.jar UCL with aab.jar
thus classes in aaa and in aab can both see classes in main, but aaa and aab cannot see each other. If you want each plugin to be able to see the classes of those plugins that were loaded before it, then you need to arrange things so that each plugin you load uses the classloader of the previous plugin as its parent
main
|
UCL with aaa.jar
|
UCL with aab.jar
To do this you'd have to cache the loader you create when you load one plugin, and then pass that as a parameter when you create the next plugin's classloader.
private static ClassLoader lastPluginClassLoader = null;
public static void load(File file) throws Exception {
//...
ClassLoader loader = null;
if(lastPluginClassLoader == null) {
loader = new URLClassLoader(urls);
} else {
loader = new URLClassLoader(urls, lastPluginClassLoader);
}
lastPluginClassLoader = loader;
// ...
}
But all this (a) is not thread safe unless synchronized and (b) makes the behaviour critically dependent on the order in which the plugins are loaded. To do things properly you'd need some way to declare which plugins depend on which other plugins, and set up the classloader tree appropriately, etc. etc.
... and if you go too far down that road you've just re-invented OSGi.