NoClassDefFoundError (change class name) - java

I tried to change a classname by overriding the loadClass(String, Boolean) method.
I am creating a BukkitPlugin. The Bukkitsource can be found here
The classloader itself is working fine, i tested it and all the classes worked fine, the error started to occur after i started to change the classnames.
Here is the Method:
import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
import org.bukkit.plugin.java.JavaPlugin;
public class PluginClassLoader extends URLClassLoader {
private final HashMap<String, String> replace;
public PluginClassLoader(JavaPlugin p, HashMap<String, String> replace) throws Exception {
super(new URL[0], p.getClass().getClassLoader());
this.replace = replace;
File f = null;
Field file = JavaPlugin.class.getDeclaredField("file");
file.setAccessible(true);
f = (File) file.get(p);
addURL(f.toURI().toURL());
}
private final HashMap<String, Class<?>> classes = new HashMap<>();
#Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
String s = replace.get(name);
if(s != null)
name = s;
Class<?> c;
try {
c = findClass(name);
} catch (Exception e) {
c = super.loadClass(name);
}
return c;
}
#Override
protected Class<?> loadClass(String name, boolean b)
throws ClassNotFoundException {
String s = replace.get(name);
if(s != null)
name = s;
Class<?> c;
try {
c = findClass(name);
} catch (ClassNotFoundException e) {
c = super.loadClass(name, b);
}
return c;
}
#Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if ((name.startsWith("org.bukkit."))
|| (name.startsWith("net.minecraft."))) {
throw new ClassNotFoundException(name);
}
Class<?> result = classes.get(name);
if (result == null) {
result = super.findClass(name);
classes.put(name, result);
}
return result;
}
}
And when i use the classloader i get a NoClassDefFoundError everytime i use one of the replaced classes. My best guess is that i have to override another method, but I dont know what it could be.
The ErrorLog:
java.lang.NoClassDefFoundError: org/bukkit/craftbukkit/v1_5_R3/CraftServer
at me.leo.itemeffects.IEPlugin.onEnable(IEPlugin.java:25)
at org.bukkit.plugin.java.JavaPlugin.setEnabled(JavaPlugin.java:217)
at org.bukkit.plugin.java.JavaPluginLoader.enablePlugin(JavaPluginLoader.java:457)
at org.bukkit.plugin.SimplePluginManager.enablePlugin(SimplePluginManager.java:381)
at org.bukkit.craftbukkit.v1_6_R2.CraftServer.loadPlugin(CraftServer.java:282)
at org.bukkit.craftbukkit.v1_6_R2.CraftServer.enablePlugins(CraftServer.java:264)
at net.minecraft.server.v1_6_R2.MinecraftServer.l(MinecraftServer.java:313)
at net.minecraft.server.v1_6_R2.MinecraftServer.f(MinecraftServer.java:290)
at net.minecraft.server.v1_6_R2.MinecraftServer.a(MinecraftServer.java:250)
at net.minecraft.server.v1_6_R2.DedicatedServer.init(DedicatedServer.java:151)
at net.minecraft.server.v1_6_R2.MinecraftServer.run(MinecraftServer.java:391)
at net.minecraft.server.v1_6_R2.ThreadServerApplication.run(SourceFile:582)
IEPlugin.java:25 is this:
CraftServer server = (CraftServer) Bukkit.getServer();
The jvm is searching for the wrong classDefinition, it looks for the one with package 'v1_5_R3', but I replaced the name of the class in loadClass() with 'v1_6_R2'
I am replacing the classname 'org.bukkit.craftbukkit.v1_5_R3.CraftServer with 'org.bukkit.craftbukkit.v1_6_R2.CraftServer'
The class in the package with v1_5_R3 does not exist, that why i change it to v1_6_R2 in the loadClass(String, Boolean) Method
Thanks for reading and for your hopefully forthcoming solutions.

To start with, when you override a method it is preffered to write before it "#Override". Some programs like Eclipse, will tell you if the overriding is not corrent (if you ommited to obay some of the overriding rules)
Second of all, your class is protected, which means that it will be seen only in the package it is implemented. Are you sure the overriden class is in the same package as this one? It maybe that the reason for which you get such an error.
It is more likely to get help if you post a bigger part of your code. In that way, maybe someone will be able to tell you exactly where the problem is.

Typically ClassNotFoundException means the current class is not found and NoClassDefFoundError means a dependent class for the currently loaded class is not found (See this stackoverflow thread). Probably your replaced class has a dependency which is not present in the classpath.

Related

Why doesn't ASM call my ``visitCode``?

I'll add my code to the end of this post.
I'm using byteBuddy 1.7.9 and whatever ASM version comes with that.
In a nutshell
I have
byte[] rawClass = ...;
ClassReader cr = new ClassReader(rawClass);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
MethodAdder ma = new MethodAdder(Opcodes.ASM5,cw);
cr.accept(ma,ClassReader.EXPAND_FRAMES);
Where in MethodAdder, I want to add a static initialiser:
#Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
if(mv != null){
if(!name.equals(CLINIT_NAME)) return mv;
else{
hasStaticInitialiser = true;
return new ClinitReplacer(api,mv,classname);
}
}else return null;
}
hasStaticInitialiser = true is reached, but ClinitReplacer.visitCode is never executed.
Why?
the whole story
Let's say I want to generate class B from this example using byteBuddy.
Why bytebuddy? Well, for one it's supposedly convenient, and for another, I need its class reloading functionality.
But as you can see in the tutorial, there are some inconveniences with using "pure" byte buddy code. Most importantly,
if you really need to create byte code with jump instructions, make sure to add the correct stack map frames using ASM since Byte Buddy will not automatically include them for you.
I don't want to do that.
And even if I wanted to, I tried
builder = builder
.defineMethod("<clinit>",void.class, Modifier.STATIC)
.withParameters(new LinkedList<>())
.withoutCode()
;
and all it got me was an
Exception in thread "main" java.lang.IllegalStateException: Illegal explicit declaration of a type initializer by class B
at net.bytebuddy.dynamic.scaffold.InstrumentedType$Default.validated(InstrumentedType.java:901)
at net.bytebuddy.dynamic.scaffold.MethodRegistry$Default.prepare(MethodRegistry.java:465)
at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.make(SubclassDynamicTypeBuilder.java:162)
at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.make(SubclassDynamicTypeBuilder.java:155)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase.make(DynamicType.java:2639)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Delegator.make(DynamicType.java:2741)
at Main.main(Main.java)
So what I do instead is, I stop after I've added all my fields, get the bytecode for that and load the class.
Then I have ASM add the methods for me. ( In the actual application, I also need to run the bytecode through some other ASM visitors anyway.)
And then reload the re-instrumented bytecode using ByteBuddy.
The reloading fails with
Exception in thread "main" java.lang.ClassFormatError
at sun.instrument.InstrumentationImpl.redefineClasses0(Native Method)
at sun.instrument.InstrumentationImpl.redefineClasses(InstrumentationImpl.java:170)
at net.bytebuddy.dynamic.loading.ClassReloadingStrategy$Strategy$1.apply(ClassReloadingStrategy.java:261)
at net.bytebuddy.dynamic.loading.ClassReloadingStrategy.load(ClassReloadingStrategy.java:171)
at Main.main(Main.java)
And the reason for that seems to be that B looks like this when disassembled:
super public class B
extends A
version 51:0
{
public static final Field foo:"Ljava/util/Set;";
public Method "<init>":"()V"
stack 1 locals 1
{
aload_0;
invokespecial Method A."<init>":"()V";
return;
}
static Method "<clinit>":"()V";
} // end Class B
Comparing that to the rawClass bytecode, we notice that
static Method "<clinit>":"()V";
didn't exist and was indeed added by the MethodAdder.
However, the Visitor returned in
return new ClinitReplacer(api,mv,classname);
is never used. And therefore the static initialiser body remains empty resulting in the wrongful classification as native.
Code
A.java
import java.util.HashSet;
import java.util.Set;
public class A{
public static final Set foo;
static{
foo = new HashSet<String>();
foo.add("A");
}
}
Main.java
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
import net.bytebuddy.jar.asm.*;
import net.bytebuddy.jar.asm.commons.InstructionAdapter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class Main {
public static void main(String[] args) {
ByteBuddyAgent.install();
String targetClassname = "B";
Class superclass = A.class;
ByteBuddy byteBuddy = new ByteBuddy();
DynamicType.Builder builder = byteBuddy
.subclass(superclass)
.name(targetClassname)
;
for(Field f : superclass.getFields()){
builder = builder.defineField(f.getName(),f.getType(),f.getModifiers());
}
DynamicType.Unloaded<?> loadable = builder.make();
byte[] rawClass = loadable.getBytes();
loadable.load(A.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION);
ClassReader cr = new ClassReader(rawClass);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
MethodAdder ma = new MethodAdder(Opcodes.ASM5,cw);
cr.accept(ma,ClassReader.EXPAND_FRAMES);
byte[] finishedClass = cw.toByteArray();
Class unfinishedClass;
try {
unfinishedClass = Class.forName(targetClassname);
}catch(ClassNotFoundException e){
throw new RuntimeException(e);
}
ClassReloadingStrategy.fromInstalledAgent()
.load(
A.class.getClassLoader(),
Collections.singletonMap((TypeDescription)new TypeDescription.ForLoadedType(unfinishedClass), finishedClass)
);
Set<String> result;
try {
result = (Set<String>)Class.forName("B").getField("foo").get(null);
}catch(ClassNotFoundException | NoSuchFieldException | IllegalAccessException e){
throw new RuntimeException(e);
}
System.out.println(result);
}
private static void store(String name, byte[] finishedClass) {
Path path = Paths.get(name + ".class");
try {
FileChannel fc = null;
try {
Files.deleteIfExists(path);
fc = new FileOutputStream(path.toFile()).getChannel();
fc.write(ByteBuffer.wrap(finishedClass));
} finally {
if (fc != null) {
fc.close();
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
static class MethodAdder extends ClassVisitor implements Opcodes{
private static final String INIT_NAME = "<init>";
private static final String INIT_DESC = "()V";
private static final int CLINIT_ACCESS = ACC_STATIC;
private static final String CLINIT_NAME = "<clinit>";
private static final String CLINIT_DESC = "()V";
private static final String CLINIT_SIG = null;
private static final String[] CLINIT_EXCEPT = null;
public MethodAdder(int api, ClassVisitor cv) {
super(api, cv);
}
private String classname = null;
private boolean hasStaticInitialiser = false;
#Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
classname = name;
hasStaticInitialiser = false;
cv.visit(version, access, name, signature, superName, interfaces);
}
#Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
if(mv != null){
if(!name.equals(CLINIT_NAME)) return mv;
else{
hasStaticInitialiser = true;
return new ClinitReplacer(api,mv,classname);
}
}else return null;
}
#Override
public void visitEnd() {
if(!hasStaticInitialiser) visitMethod(CLINIT_ACCESS,CLINIT_NAME,CLINIT_DESC,CLINIT_SIG,CLINIT_EXCEPT);
if(!hasStaticInitialiser) throw new IllegalStateException("ClinitReplacer not created");
super.visitEnd();
}
private static class ClinitReplacer extends InstructionAdapter implements Opcodes{
private final String classname;
public ClinitReplacer(int api, MethodVisitor mv, String classname) {
super(api, mv);
this.classname = classname;
}
#Override
public void visitCode() {
mv.visitCode();
InstructionAdapter mv = new InstructionAdapter(this.mv);
mv.anew(Type.getType(HashSet.class));
mv.dup();
mv.dup();
mv.invokespecial(Type.getInternalName(HashSet.class),INIT_NAME,INIT_DESC,false);
mv.putstatic(classname,"foo",Type.getDescriptor(Set.class));
mv.visitLdcInsn(classname);
mv.invokevirtual(Type.getInternalName(HashSet.class),"add","(Ljava/lang/Object;)Z",false);
mv.visitInsn(RETURN);
}
}
}
}
The problem is that your source class file doesn’t have a <clinit> method, hence, ASM doesn’t invoke visitMethod at all; it is you who does in
#Override
public void visitEnd() {
if(!hasStaticInitialiser) visitMethod(CLINIT_ACCESS,CLINIT_NAME,CLINIT_DESC,CLINIT_SIG,CLINIT_EXCEPT);
if(!hasStaticInitialiser) throw new IllegalStateException("ClinitReplacer not created");
super.visitEnd();
}
Here, you are invoking visitMethod for <clinit> if you didn’t encounter it before, but you’re not doing anything with the returned MethodVisitor, hence, no-one is doing anything with it.
If you want to treat an absent <clinit> like visiting an empty initializer, to be transformed, you have to perform the appropriate method calls yourself, i.e.
#Override
public void visitEnd() {
if(!hasStaticInitialiser) {
MethodVisitor mv = visitMethod(CLINIT_ACCESS,CLINIT_NAME,CLINIT_DESC,CLINIT_SIG,CLINIT_EXCEPT);
mv.visitCode();
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
if(!hasStaticInitialiser) throw new IllegalStateException("ClinitReplacer not created");
super.visitEnd();
}
But note that then, you can’t do hot code replacement, as it doesn’t support adding any methods, including <clinit>. Further, hot code replacement won’t (re-)execute class initializers anyway.
But in your code, there is no need to load the type before performing your ASM transformation. You may remove the line
loadable.load(A.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION);
and then just use the resulting finishedClass bytecode, e.g.
ClassLoadingStrategy.Default.INJECTION.load(A.class.getClassLoader(),
Collections.singletonMap(loadable.getTypeDescription(), finishedClass));
Note that you won’t see much effect, as you are injecting code creating a HashMap, but not doing anything useful with it. You likely want to assign it to a field…
And, by the way, your code for writing a byte array is unnecessarily complicated:
private static void store(String name, byte[] finishedClass) {
Path path = Paths.get(name + ".class");
try {
FileChannel fc = null;
try {
Files.deleteIfExists(path);
fc = new FileOutputStream(path.toFile()).getChannel();
fc.write(ByteBuffer.wrap(finishedClass));
} finally {
if (fc != null) {
fc.close();
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
Just use
private static void store(String name, byte[] finishedClass) {
Path path = Paths.get(name + ".class");
try {
Files.write(path, finishedClass);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
Both, “create if it doesn’t exist” and “overwrite/truncate if it exists” are the default behavior.
To answer the part about defining a type initializer in Byte Buddy, this can be done using:
builder = builder.invokable(isTypeInitializer()).intercept(...);
You cannot explicitly define a type initializer as those initializers are for example never exposed by the reflection API and this helps to keep Byte Buddy's type description model coherent. Instead, you match the type initializer and Byte Buddy makes sure that an intializer is added as it seems appropriate.

java - How to retrieve anything inside method

From what i know, java cannot retrieve anything inside method. so i using option -g or -g:vars in javac.
for e.g :
class Test {
int a=0;
void method(boolean boo){
String b;
try
{
new Thread().sleep(1000);
}
catch(InterruptedException e){}
JOptionPane.showMessageDialog(null,"test");
BufferedImage image=ImageIO.read(new File("C:\\file.png"));
}
}
So, i use BCEL to retrieve local variable.
import org.apache.bcel.classfile.*;
import org.apache.bcel.Repository;
class javap
{
public static void main(String[]args)
{
try
{
JavaClass jc = Repository.lookupClass("test");
ConstantPool constantPool = jc.getConstantPool();
Method [] method=jc.getMethods();
for (Method m : method)
{
LocalVariableTable lvt=m.getLocalVariableTable();
LocalVariable[] lv=lvt.getLocalVariableTable();
for(LocalVariable l : lv)
{
System.out.println(l.getName()+" : "+l.getSignature());
}
}
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
}
But it doesn't work if variable is not initialized like String b. Additionally I want to track constructor calls like new Thread() or new File() as well as invocations of static methods and inside intialize inside JFileChooser like new File and JOptionPane too. So I want to see in output Thread, String b, JOptionPane, ImageIO, and File.
What should I do, to make them are printed in my program?
You simply cannot get the b variable, because java compilers (at least javac and ecj) do not put it into the generated class file at all: if variable is not assigned, no variable slot is allocated and it's not stored in the LocalVariableTable. You can create unused variable with longer name like String blahblah;, compile the class, open the compiled .class-file in text editor and search for blahblah string. You will not found it. So BCEL cannot help you to find the variable which is absent.
If you want to track new objects creation and static methods invocation, you can do it scanning the method bytecode. The easiest way to do it with BCEL is to utilize the MethodGen (even though you don't want to generate the new method). Here's the full code:
import org.apache.bcel.Constants;
import org.apache.bcel.Repository;
import org.apache.bcel.classfile.ConstantMethodref;
import org.apache.bcel.classfile.ConstantPool;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.LocalVariable;
import org.apache.bcel.classfile.LocalVariableTable;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.INVOKESTATIC;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.NEW;
class javap
{
public static void main(String[]args)
{
try
{
JavaClass jc = Repository.lookupClass("Test");
ConstantPool constantPool = jc.getConstantPool();
Method [] method=jc.getMethods();
for (Method m : method)
{
LocalVariableTable lvt=m.getLocalVariableTable();
LocalVariable[] lv=lvt.getLocalVariableTable();
for(LocalVariable l : lv)
{
System.out.println(l.getName()+" : "+l.getSignature());
}
}
ConstantPoolGen cpg = new ConstantPoolGen(constantPool);
for(Method m : method)
{
MethodGen mg = new MethodGen(m, m.getName(), cpg);
for(InstructionHandle ih = mg.getInstructionList().getStart();
ih != null; ih = ih.getNext())
{
if(ih.getInstruction() instanceof NEW)
{
NEW newInst = ((NEW)ih.getInstruction());
String className = constantPool.getConstantString(
newInst.getIndex(), Constants.CONSTANT_Class);
System.out.println("Class instantiation: "+className);
}
if(ih.getInstruction() instanceof INVOKESTATIC)
{
INVOKESTATIC newInst = ((INVOKESTATIC)ih.getInstruction());
String className = constantPool.getConstantString(
((ConstantMethodref) constantPool
.getConstant(newInst.getIndex()))
.getClassIndex(),
Constants.CONSTANT_Class);
System.out.println("Static call: "+className);
}
}
}
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
}
The output is the following:
this : LTest;
this : LTest;
boo : Z
Class instantiation: java/lang/Thread
Static call: java/lang/Thread
Static call: javax/swing/JOptionPane
Class instantiation: java/io/File
Static call: javax/imageio/ImageIO
Note that you have java/lang/Thread twice, because new Thread() is catched as object creation and Thread.sleep() is catched as static method invocation.

Java8, how discover the class and method name in visitMethodInvocation?

With Java7 and Java8, I would like to generate a warning if some methods was called.
The warning will be print if a specific jar is present when then user compile.
I write an Annotation Processor and catch the visitMethodInvocation(). Now, I want extract the class and method names will be invoked.
Is it possible to do that ?
Or how to approach this?
You can do something like:
package mystuff;
import com.sun.source.tree.*;
import com.sun.source.util.*;
import java.util.*;
import javax.annotation.processing.*;
import javax.lang.model.element.*;
import javax.tools.*;
#SupportedAnnotationTypes("*")
public class Proc extends AbstractProcessor{
#Override
public boolean process(Set<?extends TypeElement>annotations,RoundEnvironment roundEnvironment){
final Trees trees=Trees.instance(processingEnv);
for(Element element:roundEnvironment.getRootElements()){
TreePath path=trees.getPath(element);
final CompilationUnitTree compilationUnit=path.getCompilationUnit();
compilationUnit.accept(new TreeScanner<Object,Object>(){
#Override
public Object visitMethodInvocation(MethodInvocationTree tree,Object data){
tree.getMethodSelect().accept(new SimpleTreeVisitor<Object,Object>(){
#Override
public Object visitMemberSelect(MemberSelectTree tree,Object data){
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,String.format("class: %1$s\nmethod: %2$s",tree.getExpression(),tree.getIdentifier()));
return null;
}
},null);
return null;
}
},null);
}
return true;
}
}
I used that processor to process the below class
package stuff;
import java.util.*;
#MyAnnotation
class MyProgram{
public void run(){
System.out.println("Hello World!");
}
}
and achieved this result:
class: System.out
method: println
I am pretty sure that the method name generated is what you are looking for. I am pretty sure that the "class" is not exactly what you are looking for, but is a pretty good start.
In my example you probably wanted it to print "java.io.PrintStream" for the class. To get that you could use processingEnv.getElementUtils().getTypeElement("java.lang.System") to get a TypeElement representing the system class. Then you can use processingEnv.getElementUtils().getAllMembers() to get every single member of the system class. Iterate through that to find out. Use the asType method to get its type.
The preceding paragraph was a gross simplification. The processor did not know a priori that out is a static member of a class that is part of the implicitly imported java.lang package. So your code will have to try and fail to find the following classes System and java.util.System (because it is in the imports), System.out, java.util.System.out, and java.lang.System.out.
I only dealt with MemberSelect. You will have to deal with other possibilities including MethodInvocation. For example new Object().toString().hashCode() should be class=Object, method=hashCode.
As an alternative to the great answer from #emory, you can consider using the pluggable type-checking annotation processing provided by the Checker Framework. The advantage is it can help you to easily determinate the type of the method invoker. Here is an example processor based on the checker framework (add checker.jar to the classpath when compile).
#SupportedAnnotationTypes("*")
#SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyTypeProcessor extends AbstractTypeProcessor {
class MyTreePathScanner extends TreePathScanner<Void, Void> {
private final Trees trees;
private final TreePath root;
public MyTreePathScanner(TreePath root) {
this.trees = Trees.instance(processingEnv);
this.root = root;
}
#Override
public Void visitMemberSelect(MemberSelectTree node, Void aVoid) {
ExpressionTree expression = node.getExpression();
TreePath expr = TreePath.getPath(root, expression);
TypeMirror type = trees.getTypeMirror(expr);
Element typeElement = processingEnv.getTypeUtils().asElement(type);
Optional<? extends Element> invoker = typeElement.getEnclosedElements().stream().filter(
e -> e.getSimpleName().equals(node.getIdentifier())).findFirst();
if (invoker.isPresent() && invoker.get().getKind() == ElementKind.METHOD) {
System.out.println("Type: " + typeElement + ", method: " + invoker.get());
}
return super.visitMemberSelect(node, aVoid);
}
}
#Override
public void typeProcess(TypeElement typeElement, TreePath root) {
new MyTreePathScanner(root).scan(root, null);
}
}
Which is processing the following input source.
public class Test {
public void foo() {
}
public static void main(String[] args) {
System.out.println("Hello world!");
Test t = new Test();
t.foo();
}
}
Here is the output:
Type: java.io.PrintStream, method: println()
Type: Test, method: foo()

Custom class loader returns NoSuchMethodException in listFilesAndDirs() method call

I am trying to invoke the method listFilesAndDirs() of org.apache.commons.io.FileUtils using my own custom class loader. But it returns NoSuchMethodException.
Code used for method invoke.
MyLoader c=new MyLoader();
Class cls=c.loadClass("org.apache.commons.io.FileUtils");
//to display the available methods
Method m[] = cls.getDeclaredMethods();
for (int i = 0; i < m.length; i++)
System.out.println(m[i].toString());
// to get a listFilesAndDirs method
Method me=cls.getMethod("listFilesAndDirs",new Class[] { File.class, IOFileFilter.class,
IOFileFilter.class });
Code used for class loader
public class MyLoader extends ClassLoader {
private String classPath;
public MyLoader()
{
}
private String jarFile = "D:/Project/lib/commons-io-2.4.jar";; //Path to the jar file
private Hashtable classes = new Hashtable(); //used to cache already defined classes
public Class loadClass(String className) throws ClassNotFoundException {
return findClass(className);
}
public Class findClass(String className) {
//System.out.println(className+" is loaded by Custom class Loader");
byte classByte[];
Class result = null;
result = (Class) classes.get(className); //checks in cached classes
if (result != null) {
return result;
}
try {
JarFile jar = new JarFile(jarFile);
classPath=className.replaceAll("\\.", "/");
JarEntry entry = jar.getJarEntry(classPath + ".class");
if(entry==null)
{
return findSystemClass(className);
}
else
{
InputStream is = jar.getInputStream(entry);
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
int nextValue = is.read();
while (-1 != nextValue) {
byteStream.write(nextValue);
nextValue = is.read();
}
classByte = byteStream.toByteArray();
result = defineClass(className, classByte, 0, classByte.length, null);
classes.put(className, result);
return result;
}
} catch (Exception e) {
return null;
}
}
}
call of cls.getDeclaredMethods() will return the method
org.apache.commons.io.FileUtils.listFilesAndDirs(java.io.File,org.apache.commons.io.filefilter.IOFileFilter,org.apache.commons.io.filefilter.IOFileFilter)
but cls.getMethod("listFilesAndDirs",new Class[] { File.class, IOFileFilter.class,
IOFileFilter.class });
returns the following error
java.lang.NoSuchMethodException: org.apache.commons.io.FileUtils.listFilesAndDirs(java.io.File, org.apache.commons.io.filefilter.IOFileFilter, org.apache.commons.io.filefilter.IOFileFilter)
at java.lang.Class.getDeclaredMethod(Class.java:1937)
at Sample.main(Sample.java:30)
So, getDeclaredMethods() displays that the class in question has the required method listFilesAndDirs(java.io.File,org.apache.commons.io.filefilter.IOFileFilter,org.apache.commons.io.filefilter.IOFileFilter),
BUT
the parameter types are different from what you pass into cls.getMethod("listFilesAndDirs",new Class[]{...}) even if it seems that they reference the proper classes.
Your compiled code uses org.apache.commons.io.filefilter.IOFileFilter class from the context class loader while cls expects the same classes from the custom class loader.
One way to make it work is to load IOFileFilter from the custom class loader too and then use that class as the second and third item when calling getMethod.
Class ioFileFilterClass = c.loadClass( "org.apache.commons.io.filefilter.IOFileFilter" );
Method me = cls.getMethod( "listFilesAndDirs", new Class[] { File.class, ioFileFilterClass, ioFileFilterClass} );
It will solve this particular problem but you will have to write similar code each time you are trying to access classes and methods loaded by the custom class loader.

How to find the number of declared functions in a Java program

I am doing project in Core Java which identifies the similarity between two files, in that one part is to identify the declared functions length. I have tried the following code to find the declared methods in a given class.
import java.lang.reflect.*;
import java.io.*;
import java.lang.String.*;
public class Method1 {
private int f1(
Object p, int x) throws NullPointerException
{
if (p == null)
throw new NullPointerException();
return x;
}
public static void main(String args[])throws Exception
{
try {
Class cls = Class.forName("Anu");
int a;
Method methlist[]= cls.getDeclaredMethods();
for (int i = 0; i < methlist.length;i++) {
Method m = methlist[i];
System.out.println(methlist[i]);
System.out.println("name = " + (m.getName()).length());
}
}
catch (Throwable e) {
System.err.println(e);
}
}
}
But i have to find all the classes for a program. Shall i give input as a program since have to identify the declared methods in each class. Secondary it is working only if the given class is compiled, ie class file exist for given class.
Can any one help me for identifying the declared methods in the given program.
And i have to identify the comment lines in the program, please help me for that too.
You need to write you program to read the original code as you cannot only find the comments there. You can parse the text yourself to find comments and method signatures.
You might be able to google for libraries wich help you do this.
Using JavaCompiler class, reading file as string and execute it as below:
public class SampleTestCase {
public static void main(String[] args) {
String str = "public class sample {public static void doSomething() {System.out.println(\"Im here\");}}";
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
SimpleJavaFileObject obj = new SourceString("sample", str);
Iterable<? extends JavaFileObject> compilationUnits = Arrays
.asList(obj);
CompilationTask task = compiler.getTask(null, null, diagnostics, null,
null, compilationUnits);
boolean success = task.call();
if (success) {
try {
Method[] declaredMethods = Class.forName("sample")
.getDeclaredMethods();
for (Method method : declaredMethods) {
System.out.println(method.getName());
}
} catch (ClassNotFoundException e) {
System.err.println("Class not found: " + e);
}
}
}
}
class SourceString extends SimpleJavaFileObject {
final String code;
SourceString(String name, String code) {
super(URI.create("string:///" + name.replace('.', '/')
+ Kind.SOURCE.extension), Kind.SOURCE);
this.code = code;
}
#Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return code;
}
}

Categories