I'm wondering if and how one can load dex or class files dynamically
in dalvik, some quick'n'dirty test function I wrote was this:
public void testLoader() {
InputStream in;
int len;
byte[] data = new byte[2048];
try {
in = context.getAssets().open("f.dex");
len = in.read(data);
in.close();
DexFile d;
Class c = defineClass("net.webvm.FooImpl", data, 0, len);
Foo foo = (Foo)c.newInstance();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
whereas the Foo interface is this
public interface Foo {
int get42();
}
and f.dex contains some dx'ed implementation of that interface:
public class FooImpl implements Foo {
public int get42() {
return 42;
}
}
The above test driver throws at defineClass() and it doesn't
work and I investigated the dalvik code and found this:
http://www.google.com/codesearch/p?hl=en#atE6BTe41-M/vm/Jni.c&q=Jni.c...
So I'm wondering if anyone can enlighten me if this is possible in
some other way or not supposed to be possible. If it is not possible,
can anyone provide reasons why this is not possible?
There's an example of DexClassLoader in the Dalvik test suite. It accesses the classloader reflectively, but if you're building against the Android SDK you can just do this:
String jarFile = "path/to/jarfile.jar";
DexClassLoader classLoader = new DexClassLoader(
jarFile, "/tmp", null, getClass().getClassLoader());
Class<?> myClass = classLoader.loadClass("MyClass");
For this to work, the jar file should contain an entry named classes.dex. You can create such a jar with the dx tool that ships with your SDK.
Related
Lets say I have the following setup
an interface
public interface TestInterface {
public void draw();
}
and two implementations
public class Square implements TestInterface {
#Override
public void draw() {
System.out.println("Square");
}
}
and
public class Circle implements TestInterface {
#Override
public void draw() {
System.out.println("Circle");
}
}
Now I can easily do
TestInterface a = new Square();
a.draw();
and I correctly get Square. Next, I wanted to try out reflection.
Class<?> clazz = null;
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Constructor<?> constructor = null;
try {
constructor = clazz.getConstructor();
} catch (NoSuchMethodException | SecurityException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
Object instance = null;
try {
instance = constructor.newInstance();
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Method method = null;
try {
method = instance.getClass().getMethod(methodName);
} catch (NoSuchMethodException | SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
method.invoke(instance);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
and for className as some.package.Square and methodName as draw I again get Square. Which is correct.
The problem is that my application has access to the interface but not the actual implementations. Hence I know which methods to invoke, but I have to specify the implemented classes as well and I dont know which package they may reside in. What if I only know the name of the class but not the package?
Is there a way where I can still use the initialization form of
TestInterface a = new <some_parameter>();
a.draw();
Is there a way to generalize it? Or is the approach using reflection that I showed above, the only way to achieve something like this? Lastly, would it make any difference if I used an abstract class instead of an interface?
You need to pass:
#param className the fully qualified name of the desired class.
When you have for example three class with the Same name but in diferent packages
--package1
----Test
--package2
----Test
Main
Test
And in Main you have:
public static void main(String[] args) throws UnsupportedEncodingException {
Class<?> clazz = null;
try {
clazz = Class.forName("Test");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
It will call the one that is level of Main. For calling the others you will need to pass the fully quallifed name.
clazz = Class.forName("package1.Test");
clazz = Class.forName("package2.Test");
What if I only know the name of the class but not the package? So you need to know in what level you want. Because as you know that different packages the classes can have same names. So which Class do you need if you have that issue.
You have to know the full name of the class. Only the name of the class is not enough to load it in the memory and to use it through reflection. However, you can determine the implementations of your interface using the reflections library.
Maybe this discussion thread can help you: How can I get a list of all the implementations of an interface programmatically in Java?
I am creating java files from json Objects using a library called jsonschema2pojo-core.jar. It successfully creates the required files for me. Now I need to access the newly(dynamically) created file and creates its instance to use it further.
But as the newly created class is still not in the classpath I am unable to do this. Tried to do my part of research and figured out that Eclipse jars allows such refresh only in plugin projects. Can anyone suggest some thing for this?
public static void main(String[] args){
String fileName = "MyJavaFile";
POJOBuilder pojo = new POJOBuilder();
pojo.buildPOJO("file:///C:/mypath/myJSON.json", fileName); //generates the java file MyJavaFile.java
Object obj = null;
try {
obj = Class.forName("com.mypackage."+fileName).newInstance(); // Java file not available yet
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Can this be done through threads? I mean wait until the creation of the POJO is done and then start with the rest after that?
I know we can load a Object from .class file and my question is how to do it reversely.
I use Class.forName("classname") to get a Class object and how can I write this back to the .class file?
Serialization class is not the point for this issue because the loaded file may not be implement Serializable interface.
The reason why I ask this is I need convert Class object to java source text string. If anyone knows how to convert Object class to source directly, it might be great helpful.
If you want to add functions etc to a class you can use.
http://www.jboss.org/javassist
example:
clazz = fullclass name, method = "public void doxxx(){ int x =0;x++}"
private static void createMethod(String clazz,String method){
ClassPool pool = ClassPool.getDefault();
try {
Class<?> class1 = Class.forName(clazz);
class1.getProtectionDomain().getCodeSource().getLocation();
pool.insertClassPath(new ClassClassPath(class1));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
CtClass pt;
try {
pt = pool.get(clazz);
CtMethod m = CtNewMethod.make(method, pt);
pt.addMethod(m);
pt.writeFile();
pt.toClass();
} catch (NotFoundException e) {
e.printStackTrace();
} catch (CannotCompileException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
Below is the code snippet, I am trying to invoke the usingClass method using REFLECTION. Calling the usingClass() method directly(w/o reflection) works when I pass an object of type Child, though when I try to achieve the same thing using Reflection it throws NoSuchMethodFoundException. Would like to understand if I am missing something or is there any logic behind this? Please help
package Reflection;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TestMethodInvocation {
/**
* #param args
*/
public static void main(String[] args) {
TestMethodInvocation test = new TestMethodInvocation();
Child child = new Child();
Parent parent = (Parent)child;
Class<? extends Parent> argClassType = parent.getClass();
Class<? extends TestMethodInvocation> thisClassType = test.getClass();
test.usingClass(child);
Method methodToCall;
try {
methodToCall = thisClassType.getDeclaredMethod("usingClass", argClassType);
methodToCall.invoke(test, parent);
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void usingClass(Parent p){
System.out.println("UsingClass: " + p.getClass());
}
}
Output is as below.
UsingClass: class Reflection.Child
java.lang.NoSuchMethodException: Reflection.TestMethodInvocation.usingClass(Reflection.Child)
at java.lang.Class.getDeclaredMethod(Unknown Source)
at Reflection.TestMethodInvocation.main(TestMethodInvocation.java:20)
The reason your code does not work is that getClass() is dynamically bound. Casting to Parent does not affect the runtime type of your object and so the variables child and parent contain the same class object.
Unless you explicitly query your instance for its parent class via getGenericSuperclass() or something similar, you will have to use the static way mentioned by dystroy.
You should use
methodToCall = thisClassType.getDeclaredMethod("usingClass", Parent.class);
because the precise exact class of parent (which is Child), is used at runtime and the type of the variable holding it changes nothing.
Another (too heavy) way to solve it would be :
Class<? extends Parent> argClassType2 = (new Parent()).getClass();
...
methodToCall = thisClassType.getDeclaredMethod("usingClass", argClassType2);
My QA team is doing business lifecycle testing (i.e. aging, expiring, due, past due etc) , that requires application clock to be moved. I can change all my code to refer to a adjusted clock (that I control). The issues is the (web) applications uses several 3rd party tools (e.g. Spring Batch, Activiti etc.) that relies on current time and uses System.currentTimeMillis() directly or indirectly through Date or Calendar.
Option 1 - Spring AOP. When I tried this option it seemed it only instruments Spring loaded beans only (?) Since System class was loaded outside of Spring framework it could not instrument it.
Option 2 - JMockit. Somewhat unconventional to have JMockit jar past JUnit.
Option 3 - Use Java 6 instrumentation (common piece between Option 1 and Option 2). Back to the basics... (find the relevant code below).
However, the assert in the test code always fails.
I have hit a roadblock with all the three options. Can't believe no one have done this before, but can't find an reasonable solution either.
Thanks in advance.
public class InstrumentationAgent {
private static Instrumentation instrumentation = null;
/**
* JVM hook to dynamically load InstrumentationAgent at runtime.
*
* The agent class may have an agentmain method for use when the agent is
* started after VM startup.
*
* #param agentArgument
* #param instrumentation
*/
public static void agentmain(String agentArgument, Instrumentation instrumentation) {
InstrumentationAgent.instrumentation = instrumentation;
}
/**
* Programmatic hook to dynamically load modified byte codes. This method initializes/load the agent if necessary.
*
* #param definitions
* #throws Exception
*/
public static void redefineClasses(ClassDefinition... definitions) throws Exception {
if (InstrumentationAgent.instrumentation == null) {
loadAgent();
}
InstrumentationAgent.instrumentation.redefineClasses(definitions);
}
private synchronized static void loadAgent() throws Exception {
if (InstrumentationAgent.instrumentation != null) {
return;
}
// Build the agent.jar file
final File jarFile = File.createTempFile("agent", ".jar");
jarFile.deleteOnExit();
final Manifest manifest = new Manifest();
final Attributes mainAttributes = manifest.getMainAttributes();
mainAttributes.put(Attributes.Name.MANIFEST_VERSION, "1.0");
mainAttributes.put(new Attributes.Name("Agent-Class"), InstrumentationAgent.class.getName());
mainAttributes.put(new Attributes.Name("Can-Retransform-Classes"), "true");
mainAttributes.put(new Attributes.Name("Can-Redefine-Classes"), "true");
final JarOutputStream jos = new JarOutputStream(new FileOutputStream(jarFile), manifest);
final JarEntry agent = new JarEntry(InstrumentationAgent.class.getName().replace('.', '/') + ".class");
jos.putNextEntry(agent);
final ClassPool pool = ClassPool.getDefault();
final CtClass ctClass = pool.get(InstrumentationAgent.class.getName());
jos.write(ctClass.toBytecode());
jos.closeEntry();
jos.close();
// Attach to VM and load the agent
VirtualMachine vm = VirtualMachine.attach(getPidFromRuntimeMBean());
vm.loadAgent(jarFile.getAbsolutePath());
vm.detach();
}
private static String getPidFromRuntimeMBean() throws Exception {
RuntimeMXBean mxbean = ManagementFactory.getRuntimeMXBean();
Field jvmField = mxbean.getClass().getDeclaredField("jvm");
jvmField.setAccessible(true);
VMManagement management = (VMManagement) jvmField.get(mxbean);
Method method = management.getClass().getDeclaredMethod("getProcessId");
method.setAccessible(true);
Integer processId = (Integer) method.invoke(management);
return processId.toString();
}
}
public class SystemTimeInstrumentation {
private static long timeAdjustment = 200000L;
private static byte[] originalClassByteArray;
public static void startAdjustedClock() {
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = null;
byte[] instrumentedClassByteArray = null;
try {
originalClassByteArray = pool.get(System.class.getName()).toBytecode();
ctClass = pool.makeClass(new java.io.ByteArrayInputStream(originalClassByteArray), false);
CtMethod ctMethod = ctClass.getDeclaredMethod("currentTimeMillis");
ctMethod.setBody("return 0L;");
instrumentedClassByteArray = ctClass.toBytecode();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (CannotCompileException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (ctClass != null) {
ctClass.detach();
}
}
try {
InstrumentationAgent.redefineClasses(new ClassDefinition[] { new ClassDefinition(System.class,
instrumentedClassByteArray) });
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void stopAdjustedClock() {
if (originalClassByteArray == null) {
throw new RuntimeException("The stopAdjustedClock() called before startAdjustedClock()");
} else {
try {
InstrumentationAgent.redefineClasses(new ClassDefinition[] { new ClassDefinition(System.class,
originalClassByteArray) });
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
originalClassByteArray = null;
}
}
public class SystemTimeInstrumentationTest extends TestCase {
#Test
public void testModifiedClock() throws Exception {
long unmodifiedTime = System.currentTimeMillis();
SystemTimeInstrumentation.startAdjustedClock();
long modifiedTime = System.currentTimeMillis();
SystemTimeInstrumentation.stopAdjustedClock();
assertTrue("The difference should me more than 200000", (modifiedTime-unmodifiedTime)>200000L);
}
}