I think I need some help with finding an enumeration class within another class using reflection in Java. I have been battling with this for far too long now. I have read this as well as a number of other posts and they all make me believe it should work as below.
public class ModelActivity {
public enum AttributeEnumeration { MODELID, MODELURGENCY, MODELDUEDATEANDTIME }
public static void main(String[] args) {
// Find the class with the given name
String className = "ModelActivity";
Class modelClass = null;
try {
// Retrieve the Class with the given className...
modelClass = Class.forName(className);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Class by name '" + className + "' not found.", e);
}
// Find the AttributeEnumeration within the class
String attributeEnumerationClassName = className + ".AttributeEnumeration";
Class attributeEnumerationClass = null;
try {
attributeEnumerationClass = Class.forName(attributeEnumerationClassName);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Class by name '" + attributeEnumerationClassName + "' not found.", e);
}
}
}
However, what actually happens is that the modelClass is found correctly, but the attributeEnumerationClass is not, that is, I get the second ClassNotFoundException as follows:
Exception in thread "main" java.lang.RuntimeException: Class by name 'ModelActivity.AttributeEnumeration' not found.
at ModelActivity.main(ModelActivity.java:27)
Caused by: java.lang.ClassNotFoundException: ModelActivity.AttributeEnumeration
at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:169)
at ModelActivity.main(ModelActivity.java:25)
Could anyone please point the--probably obvious--mistake out to me. Thank you.
See for yourself:
package foo.bar;
public class Outer{
public enum Inner{}
public static void main(final String[] args){
System.out.println(Inner.class.getName());
}
}
Output:
foo.bar.Outer$Inner
Inner class names are delimited with $, not with a period, so you want ModelActivity$AttributeEnumeration.
BTW:
The $ syntax is valid for class loading only. Use periods to access instances of the class in source as follows:
import foo.bar.Outer.Inner;
// ...
private Inner myEnumValue;
or like this:
private foo.bar.Outer.Inner myEnumValue;
Or, to put it this way:
assertEquals( // two ways to reference the same class
foo.bar.Outer.Inner.class,
Class.forName("foo.bar.Outer$Inner")
);
Related
Based on this tutorial I try to get a java agent to work.
https://www.baeldung.com/java-instrumentation#loading-a-java-agent
I do get [Agent] Transforming class TestApplication
I have no errors, but I can't see any effect of transforming the class.
Eventually I would like to get both static load and dynamic load to work, but for now I focus on the static way.
public class Static_Agent {
public static void premain(String agentArgs, Instrumentation inst) {
String[] tokens = agentArgs.split(";");
String className = tokens[0];
String methodName = tokens[1];
System.out.println(">> "+className);
System.out.println(">> "+methodName);
transformClass(className, methodName, inst);
}
public static void transformClass(String className, String methodName, Instrumentation instrumentation) {
Class<?> targetCls = null;
ClassLoader targetClassLoader = null;
// see if we can get the class using forName
try {
targetCls = Class.forName(className);
targetClassLoader = targetCls.getClassLoader();
transform(targetCls, methodName, targetClassLoader, instrumentation);
return;
} catch (Exception ex) {
ex.printStackTrace();
}
// otherwise iterate all loaded classes and find what we want
for(Class<?> clazz: instrumentation.getAllLoadedClasses()) {
if(clazz.getName().equals(className)) {
targetCls = clazz;
targetClassLoader = targetCls.getClassLoader();
transform(targetCls, methodName, targetClassLoader, instrumentation);
return;
}
}
throw new RuntimeException("Failed to find class [" + className + "]");
}
public static void transform(Class<?> clazz, String methodName, ClassLoader classLoader, Instrumentation instrumentation) {
Transformer dt = new Transformer(clazz.getName(), methodName, classLoader);
instrumentation.addTransformer(dt, true);
try {
instrumentation.retransformClasses(clazz);
} catch (Exception ex) {
throw new RuntimeException("Transform failed for class: [" + clazz.getName() + "]", ex);
}
}
}
public class Transformer implements ClassFileTransformer {
/** The internal form class name of the class to transform */
private String targetClassName;
/** The class loader of the class we want to transform */
private ClassLoader targetClassLoader;
private String targetMethodName;
public Transformer(String targetClassName, String targetMethodName, ClassLoader targetClassLoader) {
this.targetClassName = targetClassName;
this.targetClassLoader = targetClassLoader;
this.targetMethodName = targetMethodName;
}
#Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
byte[] byteCode = classfileBuffer;
String finalTargetClassName = this.targetClassName.replaceAll("\\.", "/");
if (!className.equals(finalTargetClassName)) {
return byteCode;
}
if (className.equals(finalTargetClassName) && loader.equals(targetClassLoader)) {
System.out.println("[Agent] Transforming class TestApplication");
try {
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get(targetClassName);
CtMethod m = cc.getDeclaredMethod(targetMethodName);
m.addLocalVariable("startTime", CtClass.longType);
m.insertBefore("startTime = System.currentTimeMillis();");
StringBuilder endBlock = new StringBuilder();
m.addLocalVariable("endTime", CtClass.longType);
m.addLocalVariable("opTime", CtClass.longType);
endBlock.append("endTime = System.currentTimeMillis();");
endBlock.append("opTime = (endTime-startTime)/1000;");
endBlock.append("System.out.println(\"[Application] Withdrawal operation completed in:\" + opTime + \" seconds!\");");
m.insertAfter(endBlock.toString());
byteCode = cc.toBytecode();
cc.detach();
} catch (Exception e) {
System.out.println("Exception"+e);
}
}
return byteCode;
}
}
public class TestApplication {
public static void main(String[] args) {
try {
TestApplication.run();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void run() throws Exception {
System.out.println("--- start ---");
while (true) {
test();
Thread.sleep(4_000);
}
}
static int count = 0;
public static void test() {
System.out.println(count++);
}
}
I launch with:
java -javaagent:static_agent.jar="doeke.application.TestApplication;test" -jar application.jar
In case it helps, the project is here:
https://github.com/clankill3r/java_agent
Edit:
In the Transformer.java near the end of the file I use e.printStackTrace(); now.
I get the following error:
[Agent] Transforming class TestApplication
javassist.NotFoundException: doeke.application.TestApplication at
javassist.ClassPool.get(ClassPool.java:436) at
doeke.transformer.Transformer.transform(Transformer.java:48) at
java.instrument/java.lang.instrument.ClassFileTransformer.transform(ClassFileTransformer.java:246)
at
java.instrument/sun.instrument.TransformerManager.transform(TransformerManager.java:188)
at
java.instrument/sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:563)
at
java.instrument/sun.instrument.InstrumentationImpl.retransformClasses0(Native
Method) at
java.instrument/sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:167)
at doeke.static_agent.Static_Agent.transform(Static_Agent.java:56)
at
doeke.static_agent.Static_Agent.transformClass(Static_Agent.java:34)
at doeke.static_agent.Static_Agent.premain(Static_Agent.java:22) at
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native
Method) at
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566) at
java.instrument/sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:513)
at
java.instrument/sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:525)
--- start ---
0
1
Thanks for raising this question to let me have chance to take a look of Java Instrumentation.
After spending some time to cross check your sample codes and the provided tutorial. The problem is not from the programming codes, but the way how to launch your program.
If you add some loggers to the transform() method in Transformer.java, you will find that the code path is broken after running:
ClassPool cp = ClassPool.getDefault();
And, after replacing the exception catching code in the same method from:
} catch (Exception e) {
to:
} catch (NotFoundException | CannotCompileException | IOException e) {
It would give your more hints as below:
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(Unknown Source)
at sun.instrument.InstrumentationImpl.loadClassAndCallPremain(Unknown Source)
Caused by: java.lang.NoClassDefFoundError: javassist/NotFoundException
at doeke.static_agent.Static_Agent.transform(Static_Agent.java:60)
at doeke.static_agent.Static_Agent.transformClass(Static_Agent.java:40)
at doeke.static_agent.Static_Agent.premain(Static_Agent.java:28)
... 6 more
Caused by: java.lang.ClassNotFoundException: javassist.NotFoundException
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 9 more
FATAL ERROR in native method: processing of -javaagent failed
Up to this point, the root cause is more apparent. It is because while launching the program, those javassist relevant classes (e.g. ClassPool, CtClass, CtMethod, etc.) cannot refer to its corresponding libraries during the runtime.
So, the solution is:
assuming you have exported the static_agent.jar in the same "build" folder as of application.jar
all other folder structure remain the same as shown in your provided github
let's "cd" to the build folder in the command console
revising the original program launching script as below
Windows OS:
java -javaagent:static_agent.jar="doeke.application.TestApplication;test" -cp ../libs/javassist-3.12.1.GA.jar;application.jar doeke.application.TestApplication
Unix/Linux OS:
java -javaagent:static_agent.jar="doeke.application.TestApplication;test" -cp ../libs/javassist-3.12.1.GA.jar:application.jar doeke.application.TestApplication
You would finally get your expected result:
[Agent] In premain method.
>> doeke.application.TestApplication
>> test
[Agent] Transforming class
--- start ---
0
[Application] Withdrawal operation completed in:0 seconds!
1
[Application] Withdrawal operation completed in:0 seconds!
EDIT
In addition, let me paste some codes regarding how to insert codes in the middle of a method through javassist.
In case the test() method in TestApplication.java is changed as:
line 30 public static void test() {
line 31 System.out.println(count++);
line 32
line 33 System.out.println("Last line of test() method");
line 34 }
Assume that we want to add a line between the count and the =========, let's say "This is line separator", which the result would look like:
1
-- This is line separator --
Last line of test() method
Then, in the transform(...) method of Transformer.java, you could add a code line as of below:
m.insertAt(32,"System.out.println(\"-- This is line separator --\");");
which makes it becomes:
#Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
byte[] byteCode = classfileBuffer;
String finalTargetClassName = this.targetClassName.replaceAll("\\.", "/");
if (!className.equals(finalTargetClassName)) {
return byteCode;
}
if (className.equals(finalTargetClassName) && loader.equals(targetClassLoader)) {
System.out.println("[Agent] Transforming class TestApplication");
try {
// Step 1 Preparation
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get(targetClassName);
CtMethod m = cc.getDeclaredMethod(targetMethodName);
// Step 2 Declare variables
m.addLocalVariable("startTime", CtClass.longType);
m.addLocalVariable("endTime", CtClass.longType);
m.addLocalVariable("opTime", CtClass.longType);
// Step 3 Insertion of extra logics/implementation
m.insertBefore("startTime = System.currentTimeMillis();");
m.insertAt(32,"System.out.println(\"-- This is line separator --\");");
StringBuilder endBlock = new StringBuilder();
endBlock.append("endTime = System.currentTimeMillis();");
endBlock.append("opTime = (endTime-startTime)/1000;");
endBlock.append("System.out.println(\"[Application] Withdrawal operation completed in:\" + opTime + \" seconds!\");");
m.insertAfter(endBlock.toString());
// Step 4 Detach from ClassPool and clean up stuff
byteCode = cc.toBytecode();
cc.detach();
} catch (NotFoundException | CannotCompileException | IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
return byteCode;
}
Finally, would get result like below of printing the code in the middle of a method:
[Agent] In premain method.
className=doeke.application.TestApplication
methodName=test
>> doeke.application.TestApplication
>> test
[Agent] Transforming class TestApplication
--- start ---
0
-- This is line separator --
=========
[Application] Withdrawal operation completed in:0 seconds!
1
-- This is line separator --
=========
[Application] Withdrawal operation completed in:0 seconds!
2
-- This is line separator --
=========
[Application] Withdrawal operation completed in:0 seconds!
I try to get some data from a dbus service and work with it in Java.
I can get the information in cli with the following command:
dbus-send --print-reply --system --dest=com.victronenergy.solarcharger.ttyUSB0 /Dc/0/Voltage com.victronenergy.BusItem.GetValue
The result is:
method return time=1538903662.321580 sender=:1.14 -> destination=:1.806 serial=335692 reply_serial=2
variant double 13.43
What I tried to get this data in Java, is:
After hours of reading, I created an Interface.
package javadbus;
import java.util.Map;
import org.freedesktop.dbus.DBusInterface;
import org.freedesktop.dbus.DBusSignal;
import org.freedesktop.dbus.Variant;
import org.freedesktop.dbus.exceptions.DBusException;
public interface BusItem extends DBusInterface
{
public static class PropertiesChanged extends DBusSignal
{
public final Map<String,Variant> changes;
public PropertiesChanged(String path, Map<String,Variant> changes) throws DBusException
{
super(path, changes);
this.changes = changes;
}
}
public String GetDescription(String language, int length);
public Variant GetValue();
public String GetText();
public int SetValue(Variant value);
public Variant GetMin();
public Variant GetMax();
public int SetDefault();
public Variant GetDefault();
}
Here I call getConnection() and getRemoteObject() successfully.
package javadbus;
import org.freedesktop.dbus.DBusConnection;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.Variant;
public class VictronEnergyDBusSolarCharger {
private String port;
private DBusConnection conn;
public VictronEnergyDBusSolarCharger(String port) {
this.port = port;
try {
this.conn = DBusConnection.getConnection(DBusConnection.SYSTEM);
} catch (DBusException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private String getData(String item) {
BusItem bi;
String data = null;
Variant vData = null;
try {
bi = (BusItem)conn.getRemoteObject("com.victronenergy.solarcharger." + this.port, item, BusItem.class);
vData = bi.GetValue();
//data = bi.GetText();
} catch (DBusException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return data;
}
...
}
It was a big task to resolve all dependecies and get the code compiled. But finaly I did it. So, javac now runs without errors.
But if I try to call the Method GetValue(), I get the following Exception:
[Sender] INFO org.freedesktop.dbus.MessageWriter - <= MethodCall(0,1) { Path=>/org/freedesktop/DBus, Interface=>org.freedesktop.DBus, Member=>Hello, Destination=>org.freedesktop.DBus } { }
[Sender] INFO org.freedesktop.dbus.MessageWriter - <= MethodCall(0,3) { Path=>/Dc/0/Voltage, Interface=>javadbus.BusItem, Member=>GetValue, Destination=>com.victronenergy.solarcharger.ttyUSB0 } { }
Exception in thread "main" org.freedesktop.DBus$Error$UnknownMethod: Method "GetValue" with signature "" on interface "javadbus.BusItem" doesn't exist
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.freedesktop.dbus.Error.getException(Error.java:141)
at org.freedesktop.dbus.Error.throwException(Error.java:171)
at org.freedesktop.dbus.RemoteInvocationHandler.executeRemoteMethod(RemoteInvocationHandler.java:158)
at org.freedesktop.dbus.RemoteInvocationHandler.invoke(RemoteInvocationHandler.java:222)
at com.sun.proxy.$Proxy1.GetValue(Unknown Source)
at javadbus.VictronEnergyDBusSolarCharger.getData(VictronEnergyDBusSolarCharger.java:28)
at javadbus.VictronEnergyDBusSolarCharger.getDcV(VictronEnergyDBusSolarCharger.java:38)
at javadbus.MainClass.main(MainClass.java:7)
Is it necessary to make a implementation of this Method GetValue? But why e.g. how should I do this? I only want to get this Information and not provide it like a Server.
Why was it a big task to get all dependencies?
dbus-java library and dependencies are all available at maven central, so a proper maven project should just work out-of-the-box.
Back to topic:
You don't have to implement GetValue(), but you need a suitable java interface for BusItem.
As far as I can see in the documentation of victronenergy (https://www.victronenergy.com/live/open_source:ccgx:d-bus) , your interface is not correct.
You provide SetDefault()/GetDefault() methods, which are only available on com.victronenergy.settings Objects, but you want to retrieve a com.victronenergy.BusItem (no part of the com.victronenergy.settings package).
This is one error. The second error is: you use the wrong package name for your BusItem class.
In your case DBus will try to resolve an object with the path javadbus.BusItem which is not provided by the connected BusAddress com.victronenergy.solarcharger.ttyUSB0.
The BusItem class has to be in package com.victronenergy or you have to use the annotation #DBusInterfaceName("com.victronenergy.BusItem").
The annotation will tell the DBus library to ignore the java package/class name and use the one provided in the annotation.
The Inteface BusItem had been created by CreateInterface-Script from https://dbus.freedesktop.org/doc/dbus-java/dbus-java/dbus-javase10.html and the XML from Introspect()
But you solved my real problem. I used the annotation #DBusInterfaceName("com.victronenergy.BusItem") now. No Exception anymore an i get data from my solarcharger. Thank you so much!
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 exported my plugin project as a Product and when I run the product (eclipse application), the main plugin (org.example.framework.core) cannot find a class defined in another plugin (org.example.abc) which implements an extension to an extension point provided by the main plugin. The class is one of the elements defined in the extension. However, when I run the project in Eclipse, everything runs fine!
Here is the log (atvste.ppt.ptfwDescription.abc.decoderInfo is a package in org.example.abc plugin):
0 [Worker-2] ERROR org.example.framework.core.ptfw.codec.decode.MsgDecoderInfo org.example.framework.core.ptfw.codec.decode.MsgDecoderInfo.createInstance(MsgDecoderInfo.java:114) : can not create class for :atvste.ppt.ptfwDescription.abc.decoderInfo.MsgDecoderInfoABC atvste.ppt.ptfwDescription.abc.decoderInfo.MsgDecoderInfoABC cannot be found by org.example.framework.core_1.0.0.201404111439
java.lang.ClassNotFoundException: atvste.ppt.ptfwDescription.abc.decoderInfo.MsgDecoderInfoABC cannot be found by org.example.framework.core_1.0.0.201404111439
at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:501)
at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:421)
at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:412)
at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(DefaultClassLoader.java:107)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Unknown Source)
at org.example.framework.core.ptfw.codec.decode.MsgDecoderInfo.createInstance(MsgDecoderInfo.java:104)
at org.example.framework.core.ptfw.codec.decode.DecoderInfo.<init>(DecoderInfo.java:56)
at org.example.framework.core.ptfw.codec.decode.DecoderInfo.createInstance(DecoderInfo.java:125)
at org.example.framework.core.ptfw.codec.ptfwObjectsFactory.decoderInfoItf_createInstance(ptfwObjectsFactory.java:200)
at org.example.framework.persistence.jaxb.ptfwDescriptionPersistenceJaxb.createptfwDescription(ptfwDescriptionPersistenceJaxb.java:326)
at org.example.framework.persistence.jaxb.ptfwDescriptionPersistenceJaxb.fillptfwDescription(ptfwDescriptionPersistenceJaxb.java:247)
at org.example.framework.persistence.jaxb.ptfwDescriptionPersistenceJaxb.createInstance(ptfwDescriptionPersistenceJaxb.java:232)
at org.example.framework.persistence.jaxb.ptfwDescriptionPersistenceJaxb.open(ptfwDescriptionPersistenceJaxb.java:146)
at org.example.framework.core.ptfw.codec.ptfwDescription.createInstance(ptfwDescription.java:152)
at org.example.framework.core.ptfw.codec.command.CmdLoadptfwDescription.loadptfwDescription(CmdLoadptfwDescription.java:50)
at org.example.framework.core.ptfw.codec.command.CmdLoadptfwDescription.execute(CmdLoadptfwDescription.java:40)
at org.example.framework.core.runtime.JobService$2.run(JobService.java:93)
at org.eclipse.core.internal.jobs.Worker.run(Worker.java:53)
EDIT: Function for creating instance of the class not found
public static IMessageDecoderInfo createInstance(XmlgMsgDecoderInfo pMsgDecoderInfoType,
IMessageDecoderInfo msgDecoder)
{
String className = pMsgDecoderInfoType.getClassName();
if(className!=null)
{
try
{
Class<?> formalArgs[] = new Class[1];
formalArgs[0] = XmlgMsgDecoderInfo.class;
Class<?> clazz;
if (msgDecoder != null)
{
clazz = msgDecoder.getClass();
}
else
{
clazz = Class.forName( className );
}
Constructor<?> constructor = clazz.getConstructor(formalArgs);
java.lang.Object actualArgs[] =
{ pMsgDecoderInfoType };
return (IMessageDecoderInfo)constructor.newInstance(actualArgs);
}catch(Exception e) {
String error = "can not create class for :" +className+ " " + e.getMessage();
if (LOGGER.isEnabledFor(Level.ERROR)) {
LOGGER.error(error, e);
}
throw new CreatePtfwElementRuntimeException(error, e);
}
}
return new MsgDecoderInfo(pMsgDecoderInfoType);
}`
Because of the complex class loader system used by Eclipse you cannot use Class.forName to load a class in another plugin.
If your code is processing an extension point definition you will have the IConfigurationElement for the configuration element that specifies the class name. You can use
IConfigurationElement configElement = ....;
Object classInstance = configElement.createExecutableExtension("class attribute name");
where 'class attribute name' is the name of the attribute in the extension point element that specifies the class to load. The class being created must have a no argument constructor.
Alternatively you can load a class using:
Bundle bundle = Platform.getBundle("plugin id");
Class<?> theClass = bundle.loadClass("class name");
... construct as usual ...
If you have the IConfigurationElement you can get the plugin id using
String pluginId = configElement.getContributor().getName();
I've got a ClassLoader extending class with following method
#Override
public Class<?> findClass(String className) throws ClassNotFoundException {
try {
/**
* Get a bytecode from file
*/
byte b[] = fetchClassFromFS(pathtobin + File.separator
+ className.replaceAll("\\.", escapeSeparator(File.separator)) + ".class");
return defineClass(className, b, 0, b.length);
} catch (FileNotFoundException ex) {
return super.findClass(className);
} catch (IOException ex) {
return super.findClass(className);
}
}
That as u can see uses defineClass() method from its parent - ClassLoader. The issue is when i'm trying to execute a class' (i recieve with my ClassLoader extension - let it be ru.xmppTesting.test.Disco) method getMethods() while getting an instance of this class i get the following
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/http/Header
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Unknown Source)
at java.lang.Class.privateGetPublicMethods(Unknown Source)
at java.lang.Class.getMethods(Unknown Source)
at DOTGraphCreator.createGraphFromClasses(DOTGraphCreator.java:85)
at DOTGraphCreator.generateDotGraphFile(DOTGraphCreator.java:56)
at DOTGraphCreator.main(DOTGraphCreator.java:46)
Caused by: java.lang.ClassNotFoundException: org.apache.http.Header
at java.lang.ClassLoader.findClass(Unknown Source)
at SourceClassLoader.findClass(SourceClassLoader.java:27)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 7 more
As far as i can see that is because class org.apache.http.Header could not be found as defined. Because it is not.
So here's a question:
how can and must i define and link this Header class (and lots of others from my .jar libs) along with definition of ru.xmppTesting.test.Disco and others similar to have them defined on the fly?
If your are importing org.apache.http.Header from your dinamic loaded class, you need it to be accesible at your classpath.
If you don't want to load all the potentially needed jars on your classpath, you could try with a hack i have found here:
import java.lang.reflect.*;
import java.io.*;
import java.net.*;
public class ClassPathHacker {
private static final Class[] parameters = new Class[]{URL.class};
public static void addFile(String s) throws IOException {
File f = new File(s);
addFile(f);
}//end method
public static void addFile(File f) throws IOException {
addURL(f.toURL());
}//end method
public static void addURL(URL u) throws IOException {
URLClassLoader sysloader = (URLClassLoader)ClassLoader.getSystemClassLoader();
Class sysclass = URLClassLoader.class;
try {
Method method = sysclass.getDeclaredMethod("addURL",parameters);
method.setAccessible(true);
method.invoke(sysloader,new Object[]{ u });
} catch (Throwable t) {
t.printStackTrace();
throw new IOException("Error, could not add URL to system classloader");
}//end try catch
}//end method
}//end class
But, I must say, it could not be portable to some JVMs (not always the SystemClassLoader is a subclass of URLClassLoader)...
*EDIT: * In fact, as you have replaced the classloader with your own, perhaps you have some troubles...