Java Forum / General / October 2007
empty interfaces via reflection
Aryeh M. Friedman - 13 Oct 2007 21:40 GMT Is it possible to force a class to implement a (empty) interface via reflection only... the problem is I want to create a Proxy but want the class/interface being proxied to be totally unaware of it (eg. TestProxy does some debugging and checks on all classes Class.newInstance() calls after it is constructor is called [from the caller not from within itself])
Roedy Green - 13 Oct 2007 22:49 GMT On Sat, 13 Oct 2007 20:40:50 -0000, "Aryeh M. Friedman" <Aryeh.Friedman@gmail.com> wrote, quoted or indirectly quoted someone who said :
>Is it possible to force a class to implement a (empty) interface via >reflection only... the problem is I want to create a Proxy but want >the class/interface being proxied to be totally unaware of it (eg. >TestProxy does some debugging and checks on all classes >Class.newInstance() calls after it is constructor is called [from the >caller not from within itself]) I would think you would have to modify the bytes of a the class file to add the dummy interface and load it with your own class loader to do that. There may be an easier way.
 Signature Roedy Green Canadian Mind Products The Java Glossary http://mindprod.com
Patricia Shanahan - 13 Oct 2007 23:04 GMT > Is it possible to force a class to implement a (empty) interface via > reflection only... the problem is I want to create a Proxy but want > the class/interface being proxied to be totally unaware of it (eg. > TestProxy does some debugging and checks on all classes > Class.newInstance() calls after it is constructor is called [from the > caller not from within itself]) Have you considered aspect oriented programming, and AspectJ in particular?
Patricia
Aryeh M. Friedman - 13 Oct 2007 23:17 GMT > > Is it possible to force a class to implement a (empty) interface via > > reflection only... the problem is I want to create a Proxy but want [quoted text clipped - 5 lines] > Have you considered aspect oriented programming, and AspectJ in > particular? For other projects yes but this is one has Java as one of it's requirements
Patricia Shanahan - 13 Oct 2007 23:33 GMT >>> Is it possible to force a class to implement a (empty) interface via >>> reflection only... the problem is I want to create a Proxy but want [quoted text clipped - 7 lines] > For other projects yes but this is one has Java as one of it's > requirements AspectJ is not Java enough for the purpose? I believe the classes you are operating on can be written in conventional Java. It is only the code that imposes the checks and debugging that needs the AspectJ extensions on top of Java.
Patricia
Aryeh M. Friedman - 14 Oct 2007 09:29 GMT > >>> Is it possible to force a class to implement a (empty) interface via > >>> reflection only... the problem is I want to create a Proxy but want [quoted text clipped - 12 lines] > code that imposes the checks and debugging that needs the AspectJ > extensions on top of Java. The reason for saying this is it needs to be 100% backwards compatible with Java (i.e. you do not need AsepectJ to run or maintain any component except the ones mentioned)... reason it is a commerical open- source unit testing framework for java and part of the marketing is all our products are 100% java.
Aryeh M. Friedman - 14 Oct 2007 09:57 GMT > The reason for saying this is it needs to be 100% backwards compatible > > with Java (i.e. you do not need AsepectJ to run or maintain any > > component except the ones mentioned)... reason it is a commerical open- > > source unit testing framework for java and part of the marketing is > > all our products are 100% java. In think it over I decided to post a high level summary of the problem:
1. The framework has it's own standalone GUI see http://www.flosoft-systems.com/thisTest_screens.php for screen shots
2. When clicking on "run tests" any recompiling done since the last test run (either from with in the GUI or restarting the app) needs to be honored
3. I currently solved item 2 in a very adhoc and bug prone way: a. Have a custom class loader (see other treads) since the system class loader will not honor updated .class files b. A side effect of using a custom class loader is you can not directly cast to an instance created using the system class loader. For example:
public class Main { public static void main(String[] args) throws Throwable { ClassLoader loader=new MyClassLoader(); Class klass=loader.loadClass("MyClass");
MyClass m=(MyClass) klass.newInstance(); } }
Produces: Exception in thread "main" java.lang.ClassCastException: MyClass cannot be cast to MyClass at Main.main(Main.java:11)
Here is the support code:
import java.io.*; import java.lang.reflect.*;
public class MyClassLoader extends ClassLoader { public Class loadClass(String name) { try { if(name.startsWith("java.")) return super.loadClass(name);
FileInputStream fis=new FileInputStream(name +".class"); byte[] b=new byte[fis.available()];
fis.read(b); fis.close();
return defineClass(name,b,0,b.length); } catch(Throwable e) { e.printStackTrace(); }
return null; } }
public class MyClass { public MyClass() { ack=new Integer(0); }
public Integer getAck() { return ack; }
private int foo; private Integer ack; }
If you want more detail and the threads proving the correctness of the code see:
http://groups.google.com/group/comp.lang.java.programmer/browse_thread/thread/5c d333290dc92e74/0169c9ea83253940#0169c9ea83253940
http://groups.google.com/group/comp.lang.java.programmer/browse_thread/thread/9f 84cb0f0a2ab367/f28ba3e0de4cc60f#f28ba3e0de4cc60f
One solution I have found to this problem (which I do in a adhoc and bug baity way in the production code) is to create a second instance of the class using the system class loader then copy the fields over that way any getter/setter operates on stuff created by the system class loader and not the custom class loader (this is fine because by definition Unit tests only test the top level containing class). Recently I have come up with the following experimental code to do this is a more systematic way:
import java.lang.reflect.*;
public class Main { public static void main(String[] args) throws Throwable { ClassLoader loader=new MyClassLoader(); Class klass=loader.loadClass("MyClass");
MyClass m=(MyClass) rebrand(MyClass.class,klass.newInstance());
// cast to make sure rebrand works System.out.println((Integer) m.getAck()); }
public static Object rebrand(Class brand,Object obj) throws Throwable { ClassLoader loader=ClassLoader.getSystemClassLoader(); Class klass=loader.loadClass(brand.getCanonicalName()); Object real=klass.newInstance();
for(Field f:real.getClass().getDeclaredFields()) { if(f.getType().isPrimitive()) continue;
Field oldField=obj.getClass().getDeclaredField(f.getName()); boolean fVis=f.isAccessible(); boolean oVis=oldField.isAccessible();
try { f.setAccessible(true); oldField.setAccessible(true); } catch(Throwable e) { // if for some reason we can't mod the accessibility skip it continue; }
f.set(real,oldField.get(obj));
f.setAccessible(true); oldField.setAccessible(true); }
return real; } }
The only problem remaining is to call rebrand when ever the class under test returns a field (see the println for an example).
This is where the proxy comes in I basically wrap Proxy.invoke(....) around all method calls and if the return type needs to be rebranded (made by the custom classloader) it does so. This is safe because it is conceptually illegal for a unit test to call any methods in the returned value except to check it's values.
Aryeh M. Friedman - 14 Oct 2007 10:00 GMT > This is where the proxy comes in I basically wrap Proxy.invoke(....) > around all method calls and if the return type needs to be rebranded > (made by the custom classloader) it does so. This is safe because it > is conceptually illegal for a unit test to call any methods in the > returned value except to check it's values. oops mentioned the one problem java.lang.reflect.Proxy requires all acted on "classses" be interfaces not classes. thus the desire to make a empty interface and wrap any rebranded class in it tempurarly so the proxy can be used.
Owen Jacobson - 14 Oct 2007 12:23 GMT On Oct 14, 2:00 am, "Aryeh M. Friedman" <Aryeh.Fried...@gmail.com> wrote:
> > This is where the proxy comes in I basically wrap Proxy.invoke(....) > > around all method calls and if the return type needs to be rebranded [quoted text clipped - 6 lines] > make a empty interface and wrap any rebranded class in it tempurarly > so the proxy can be used. You will discover that creating a proxy does not expose arbitrary random method calls, nor can the proxy be (down)cast to any interface other than the one you specified when it was created. Java's static type checking still applies, preventing the former, and the object the caller invokes methods on (the proxy) doesn't have the complete inheritance hierarchy of the proxied object. For example, the following won't work:
public interface Proxied {}
public class Foo implements Proxied { public void bar () {...} }
... Foo proxyForFoo = (Foo) Proxy.newProxyInstance( Foo.class.getClassLoader(), new Class[] { Proxied.class }, someHandler); ...
because the object returned from newProxyInstance is neither a Foo nor a child of Foo.
In short, you can't get there from here; go back to working with class loaders. I think you probably want to avoid running any user code from the System classloader at all; if possible, you want to run each test case inside a single classloader which can be thrown away at the end of the run and which provides canonical definitions of each class to the tested code.
The other traditional and, more importantly, workable tool for moving objects' states between classloaders is serialization; obviously, this only works if the objects in question are serializable. This is one of the reasons EJB remote interfaces must accept only serializable objects or primitives as arguments and only return said.
Aryeh M. Friedman - 14 Oct 2007 12:42 GMT > On Oct 14, 2:00 am, "Aryeh M. Friedman" <Aryeh.Fried...@gmail.com> > wrote: [quoted text clipped - 34 lines] > because the object returned from newProxyInstance is neither a Foo nor > a child of Foo. I was vaguely aware of that and was expecting something above the proxy to unwrap before handing back the object.
> In short, you can't get there from here; go back to working with class > loaders. I think you probably want to avoid running any user code > from the System classloader at all; if possible, you want to run each > test case inside a single classloader which can be thrown away at the > end of the run and which provides canonical definitions of each class > to the tested code. That was my orginial thinking but some issues came up like how to handle result reporting when the ui code couldn't do this easily:
Result res=test.getResult();
keep in mind the test is from MyClassLoader and the ui (which has main()) is by definition uses SystemClassLoader
> The other traditional and, more importantly, workable tool for moving > objects' states between classloaders is serialization; obviously, this > only works if the objects in question are serializable. This is one > of the reasons EJB remote interfaces must accept only serializable > objects or primitives as arguments and only return said. Thats where I got stuck.... since it is a testing framework it *MUST* work on anything including Object (in theory)
Daniel Pitts - 14 Oct 2007 18:12 GMT >> This is where the proxy comes in I basically wrap Proxy.invoke(....) >> around all method calls and if the return type needs to be rebranded [quoted text clipped - 6 lines] > make a empty interface and wrap any rebranded class in it tempurarly > so the proxy can be used. What you really need to do is NOT cast to an explicit object, but use reflection to call your test hooks:
Class<?> testClass = loadTestClass(); Object testInstance = testClass.newInstance(); for (Method method: testClass.getDeclaredMethods()) { method.invoke(testInstance, new Object[0]); }
 Signature Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>
Aryeh M. Friedman - 14 Oct 2007 18:21 GMT On Oct 14, 5:12 pm, Daniel Pitts <newsgroup.spamfil...@virtualinfinity.net> wrote:
> >> This is where the proxy comes in I basically wrap Proxy.invoke(....) > >> around all method calls and if the return type needs to be rebranded [quoted text clipped - 16 lines] > > } Then how do you handle the return type?!?!?!? Namely I can't do:
Class<?> testClass = loadTestClass(); Object testInstance = testClass.newInstance(); Result res=new Result();
for (Method method: testClass.getDeclaredMethods()) res.merge((Result) method.invoke(testInstance, new Object[0])); // cast exception
reportResults(res)
...
private void reportResults(Result res) { .... }
The reason why it is not possible is any Result object created by a test is <MyClassLoader>.Result and all the results here are <SystemClassLoader>.Result
Steven Simpson - 14 Oct 2007 19:01 GMT > Then how do you handle the return type?!?!?!? Namely I can't do: > [quoted text clipped - 10 lines] > <SystemClassLoader>.Result > Looking back at an earlier post, your custom MyClassLoader goes like this:
> public class MyClassLoader extends ClassLoader > { [quoted text clipped - 4 lines] > return super.loadClass(name); > A custom ClassLoader is expected to override findClass(String) rather than loadClass(String), as the latter (indirectly) accomplishes this behaviour:
<http://java.sun.com/javase/6/docs/api/java/lang/ClassLoader.html>
"When requested to find a class or resource, a ClassLoader instance will delegate the search for the class or resource to its parent class loader before attempting to find the class or resource itself."
MyClassLoader is loading Result instead of delegating to its parent first, which should always find it first.
 Signature ss at comp dot lancs dot ac dot uk |
Aryeh M. Friedman - 14 Oct 2007 20:17 GMT > > Then how do you handle the return type?!?!?!? Namely I can't do: > [quoted text clipped - 32 lines] > MyClassLoader is loading Result instead of delegating to its parent > first, which should always find it first. I just tested:import java.io.*; import java.lang.reflect.*;
public class MyClassLoader extends ClassLoader { public Class findClass(String name) { try { //if(name.startsWith("java.")) // return super.loadClass(name);
FileInputStream fis=new FileInputStream(name +".class"); byte[] b=new byte[fis.available()];
fis.read(b); fis.close();
return defineClass(name,b,0,b.length); } catch(Throwable e) { e.printStackTrace(); }
return null; }
and now the class loader no longer honors recompiled classes:
Script started on Sun Oct 14 15:09:05 2007 jtest@monster:/home/jtest% java Main 1 ^Z Suspended jtest@monster:/home/jtest% cat foo import java.lang.reflect.*;
public class MyClass { public MyClass() { ack=new Integer(2); }
public Integer getAck() { return ack; }
private int foo; private Integer ack; } jtest@monster:/home/jtest% cp foo MyClass.java jtest@monster:/home/jtest% javac MyClass.java jtest@monster:/home/jtest% fg java Main
1 ^C jtest@monster:/home/jtest% exit Script ends on Sun Oct 14 15:09:45 2007
Just for ref here is the new main():
public class Main { public static void main(String[] args) throws Throwable { while(true) { ClassLoader loader=new MyClassLoader(); Class klass=loader.loadClass("MyClass");
MyClass m=(MyClass) klass.newInstance();
System.out.println(m.getAck()); System.in.read(); } } }
Steven Simpson - 14 Oct 2007 22:31 GMT > I just tested:import java.io.*; > import java.lang.reflect.*; [quoted text clipped - 11 lines] > MyClass m=(MyClass) klass.newInstance(); > By this point at least, you'll have surely caused the system classloader to load in MyClass itself (by referring to it in code), so later MyClassLoaders will defer to that.
But this is just a test program right? In your other code, you had:
Class<?> testClass = loadTestClass(); Object testInstance = testClass.newInstance();
...and you don't actually refer to MyClass. So, as long as MyClass is not in the program's classpath, the system classloader won't load it; but your custom classloader will, if it looks in the right directory.
Going back to your test program to verify, you could just keep 'm' as an Object, and invoke reflectively, also keeping MyClass out of the classpath. Does that help?
 Signature ss at comp dot lancs dot ac dot uk |
Aryeh M. Friedman - 19 Oct 2007 16:20 GMT > Class<?> testClass = loadTestClass(); > Object testInstance = testClass.newInstance(); > > ...and you don't actually refer to MyClass. So, as long as MyClass is > not in the program's classpath, the system classloader won't load it; > but your custom classloader will, if it looks in the right directory. Had other things to do so sorry for not trying this sooner but the following test code (using findClass not loadClass as the override method) still doesn't work:
public static void main(String[] args) throws Throwable { while(true) { ClassLoader loader=new MyClassLoader(); Class klass=loader.loadClass("MyClass"); Object o=klass.newInstance();
System.out.println(o.getClass().getDeclaredMethod("getAck").invoke(o)); System.in.read(); } }
Steven Simpson - 19 Oct 2007 17:14 GMT > the > following test code (using findClass not loadClass as the override [quoted text clipped - 5 lines] > while(true) { > ClassLoader loader=new MyClassLoader(); I did get the following to work. It uses a URLClassLoader, as Daniel Pitts suggested. Your MyClassLoader didn't seem to be doing anything extra, so you may as well use the existing class - though I don't see why yours wouldn't work.
You give the directory containing the class to be tested as the first argument, and it is turned into a URL for the class loader. Make sure this directory is not in your classpath.
import java.io.*; import java.net.*;
public class Test { public static void main(String[] args) throws Throwable { File f = new File(args[0]); URL[] path = new URL[] { f.toURI().toURL() };
while (true) { ClassLoader loader = new URLClassLoader(path); Class klass = loader.loadClass("SomeClass"); Object o = klass.newInstance();
System.out.println(o.getClass().getDeclaredMethod("getAck"). invoke(o)); System.in.read(); } } }
 Signature ss at comp dot lancs dot ac dot uk |
Aryeh M. Friedman - 19 Oct 2007 18:09 GMT > > the > > following test code (using findClass not loadClass as the override [quoted text clipped - 14 lines] > argument, and it is turned into a URL for the class loader. Make sure > this directory is not in your classpath. There is the rub... since it is meant for a standalone commercial app I don't feel comfortable making any assumptions about the struct of the user's class path (matter of fact on the surface I think all loaded classes will have to be in the class path one way or an other [cmd line arg passing maybe but not very portable])
Daniel Pitts - 20 Oct 2007 19:01 GMT Sorry if this a double-post. Damn google. On Oct 19, 10:09 am, "Aryeh M. Friedman" <Aryeh.Fried...@gmail.com> wrote:
> > > the > > > following test code (using findClass not loadClass as the override [quoted text clipped - 20 lines] > loaded classes will have to be in the class path one way or an other > [cmd line arg passing maybe but not very portable]) So, use it as an example, and work from there. You can't do what you're trying to do without some assumptions about the class path. Commercial products make assumptions all the time (like, for instance, the validity of the environment their running on). It is the only feasible way to do this. Just document the fact that your program's execution environment should be *different* than that of the tests. Or, figure out some other way of supporting that.
Daniel Pitts - 16 Oct 2007 01:21 GMT On Oct 14, 12:17 pm, "Aryeh M. Friedman" <Aryeh.Fried...@gmail.com> wrote:
> > > Then how do you handle the return type?!?!?!? Namely I can't do: > [quoted text clipped - 114 lines] > > } You should simply use UrlClassLoader and have the system classpath not include your code that needs to be reloaded, and the URLClassLoader have the path that DOES need to be reloaded (creating a new instance of that class loader every time you need to reload the class)
That way, Result will be properly loaded by the System class loader, and your reloadable class wont.
Hoep this helps, Daniel.
Arne Vajhøj - 15 Oct 2007 02:37 GMT >>>>> Is it possible to force a class to implement a (empty) interface via >>>>> reflection only... the problem is I want to create a Proxy but want [quoted text clipped - 16 lines] > source unit testing framework for java and part of the marketing is > all our products are 100% java. I still can not really see any reason why not to use AspectJ.
Yes - you need to distribute the aspectrt.jar, but most Java apps require some jars.
Yes - developers will need the AspectJ tools, but they also need an editor, a Java compiler etc..
Arne
Lew - 15 Oct 2007 03:33 GMT > I still can not really see any reason why not to use AspectJ. > [quoted text clipped - 3 lines] > Yes - developers will need the AspectJ tools, but they also need > an editor, a Java compiler etc.. Be careful which version of AspectJ you use. Apparently some older versions had trouble in multi-threaded, multi-processor environments. I have heard that these troubles were repaired in the latest release(s).
It is also true that AspectJ defines a superset language to Java, which may be the OP's concern with it. They do take care to maintain complete binary compatibility with "plain" Java, though.
Still, I don't think you should minimize the differences from "plain ol' Java" that AspectJ does introduce to the language. Perhaps they are worthwhile, but the OP is correct to investigate the impact of these differences.
Disclaimer: I am not anything but superficially familiar with AspectJ, although I did review their documentation as I wrote this post.
 Signature Lew
Arne Vajhøj - 15 Oct 2007 04:07 GMT > Be careful which version of AspectJ you use. Apparently some older > versions had trouble in multi-threaded, multi-processor environments. I [quoted text clipped - 8 lines] > worthwhile, but the OP is correct to investigate the impact of these > differences. AspectJ takes a valid class file and convert it to another valid class file based on input from a source text file. The resulting code requires a single jar file to be in classpath.
The concept and syntax understanding to be able to write that source text file has a steep learning curve.
But from a runtime perspective it has less impact to use AspectJ than to use another XML parser than the one Java comes with.
Arne
Arne Vajhøj - 15 Oct 2007 04:17 GMT >> Be careful which version of AspectJ you use. Apparently some older >> versions had trouble in multi-threaded, multi-processor environments. [quoted text clipped - 19 lines] > But from a runtime perspective it has less impact to use AspectJ > than to use another XML parser than the one Java comes with. I just refreshed:
... takes a valid java file and convert it to a valid class file based on ...
Arne
Arne Vajhøj - 15 Oct 2007 04:20 GMT >>> Be careful which version of AspectJ you use. Apparently some older >>> versions had trouble in multi-threaded, multi-processor [quoted text clipped - 25 lines] > ... takes a valid java file and convert it to a valid class file > based on ... But to quote what they write themselves in http://www.eclipse.org/aspectj/doc/next/devguide/ajc-ref.html:
<quote> Compatibility
AspectJ is a compatible extension to the Java programming language. The AspectJ compiler adheres to the The Java Language Specfication, Second Edition and to the The Java Virtual Machine Specification, Second Edition and runs on any Java 2 compatible platform. The code it generates runs on any Java 1.1 or later compatible platform. For more information on compatibility with Java and with previous releases of AspectJ, see AspectJ Version Compatibility. </quote>
And the link points to: http://www.eclipse.org/aspectj/doc/next/devguide/compatibility.html which further explains.
Arne
Aryeh M. Friedman - 19 Oct 2007 16:21 GMT > I still can not really see any reason why not to use AspectJ. > [quoted text clipped - 3 lines] > Yes - developers will need the AspectJ tools, but they also need > an editor, a Java compiler etc.. With AspectJ can you freely change the class of an instant? (assuming that the class your moving to implements the same methods)
Lew - 19 Oct 2007 22:54 GMT > With AspectJ can you freely change the class of an instant? (assuming > that the class your moving to implements the same methods) Do you mean "change the class of an instance"?
No. At least not as I understand the question. AspectJ adds cross-cutting concerns to the Java language; it doesn't change the semantics of the part that it shares with the Java language itself. AFAIK.
Undoubtedly there is a different strategy (!) to achieve your real, and as yet unstated, goal (!).
What exactly do you intend to accomplish with such an idiom?
 Signature Lew
Arne Vajhøj - 25 Oct 2007 01:48 GMT >> I still can not really see any reason why not to use AspectJ. >> [quoted text clipped - 6 lines] > With AspectJ can you freely change the class of an instant? (assuming > that the class your moving to implements the same methods) No.
But you can add functionality to an existing class without changing its code.
Arne
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|