Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

empty interfaces via reflection

3 views
Skip to first unread message

Aryeh M. Friedman

unread,
Oct 13, 2007, 4:40:50 PM10/13/07
to
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

unread,
Oct 13, 2007, 5:49:01 PM10/13/07
to
On Sat, 13 Oct 2007 20:40:50 -0000, "Aryeh M. Friedman"
<Aryeh.F...@gmail.com> wrote, quoted or indirectly quoted someone
who said :

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.
--
Roedy Green Canadian Mind Products
The Java Glossary
http://mindprod.com

Patricia Shanahan

unread,
Oct 13, 2007, 6:04:44 PM10/13/07
to

Have you considered aspect oriented programming, and AspectJ in
particular?

Patricia

Aryeh M. Friedman

unread,
Oct 13, 2007, 6:17:48 PM10/13/07
to

For other projects yes but this is one has Java as one of it's
requirements

Patricia Shanahan

unread,
Oct 13, 2007, 6:33:07 PM10/13/07
to

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

unread,
Oct 14, 2007, 4:29:21 AM10/14/07
to

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

unread,
Oct 14, 2007, 4:57:31 AM10/14/07
to
> 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/5cd333290dc92e74/0169c9ea83253940#0169c9ea83253940

http://groups.google.com/group/comp.lang.java.programmer/browse_thread/thread/9f84cb0f0a2ab367/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

unread,
Oct 14, 2007, 5:00:55 AM10/14/07
to

> 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

unread,
Oct 14, 2007, 7:23:33 AM10/14/07
to
On Oct 14, 2:00 am, "Aryeh M. Friedman" <Aryeh.Fried...@gmail.com>
wrote:

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

unread,
Oct 14, 2007, 7:42:07 AM10/14/07
to

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

unread,
Oct 14, 2007, 1:12:10 PM10/14/07
to
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]);
}


--
Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>

Aryeh M. Friedman

unread,
Oct 14, 2007, 1:21:12 PM10/14/07
to
On Oct 14, 5:12 pm, Daniel Pitts

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

unread,
Oct 14, 2007, 2:01:45 PM10/14/07
to
Aryeh M. Friedman wrote:
> 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
>
> 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
>

Looking back at an earlier post, your custom MyClassLoader goes like this:

> public class MyClassLoader extends ClassLoader
> {
> public Class loadClass(String name)
> {
> try {
> if(name.startsWith("java."))
> 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.

--
ss at comp dot lancs dot ac dot uk |

Aryeh M. Friedman

unread,
Oct 14, 2007, 3:17:45 PM10/14/07
to

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

unread,
Oct 14, 2007, 5:31:58 PM10/14/07
to
Aryeh M. Friedman wrote:
> I just tested:import java.io.*;
> import java.lang.reflect.*;
>
> public class MyClassLoader extends ClassLoader
> {
> public Class findClass(String name)
>
> and now the class loader no longer honors recompiled classes:
>
> while(true) {
> ClassLoader loader=new MyClassLoader();
> Class klass=loader.loadClass("MyClass");
>
> 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?

Arne Vajhøj

unread,
Oct 14, 2007, 9:37:26 PM10/14/07
to

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

unread,
Oct 14, 2007, 10:33:06 PM10/14/07
to
Arne Vajhøj wrote:
> 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..

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.

--
Lew

Arne Vajhøj

unread,
Oct 14, 2007, 11:07:06 PM10/14/07
to
Lew wrote:
> 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.

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

unread,
Oct 14, 2007, 11:17:26 PM10/14/07
to

I just refreshed:

... takes a valid java file and convert it to a valid class file
based on ...

Arne

Arne Vajhøj

unread,
Oct 14, 2007, 11:20:02 PM10/14/07
to

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

Daniel Pitts

unread,
Oct 15, 2007, 8:21:00 PM10/15/07
to
On Oct 14, 12:17 pm, "Aryeh M. Friedman" <Aryeh.Fried...@gmail.com>
wrote:

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.

Aryeh M. Friedman

unread,
Oct 19, 2007, 11:20:31 AM10/19/07
to

> 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();
}
}

Aryeh M. Friedman

unread,
Oct 19, 2007, 11:21:39 AM10/19/07
to

> 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..

With AspectJ can you freely change the class of an instant? (assuming
that the class your moving to implements the same methods)

Steven Simpson

unread,
Oct 19, 2007, 12:14:43 PM10/19/07
to
Aryeh M. Friedman wrote:
> 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();

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();

Aryeh M. Friedman

unread,
Oct 19, 2007, 1:09:07 PM10/19/07
to
On Oct 19, 4:14 pm, Steven Simpson <s...@domain.invalid> wrote:
> Aryeh M. Friedman wrote:
> > 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();
>
> 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.

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])

Lew

unread,
Oct 19, 2007, 5:54:02 PM10/19/07
to
Aryeh M. Friedman wrote:
> 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?

--
Lew

Daniel Pitts

unread,
Oct 20, 2007, 2:01:30 PM10/20/07
to
Sorry if this a double-post. Damn google.
On Oct 19, 10:09 am, "Aryeh M. Friedman" <Aryeh.Fried...@gmail.com>
wrote:

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.

Arne Vajhøj

unread,
Oct 24, 2007, 8:48:20 PM10/24/07
to

No.

But you can add functionality to an existing class without
changing its code.

Arne

0 new messages