Using java.lang.reflect.Proxy for interposition

D

David C. Partridge

I want to intercept method calls on Java classes. The classes in question
are delivered by a third party in a jar file and I do not have access to the
sources for that. The client code that uses those classes is also not
available to me. If I were writing C code I'd just create a new shared
library that exported the API calls I wanted to intercept, and dynamically
load the real API calls (after adjusting the appropriate environment
variables so that my library was picked up first.

I was hoping to use java.lang.reflect.Proxy to do this, and in Tom Harpin's
article "Using java.lang.reflect.Proxy to Interpose on Java Class Methods",
he said:

===QUOTE===
At class load time, the interposed class must be found before the original
one. This can be readily accomplished through manipulation of the CLASSPATH
environment variable or a -classpath switch on the commandline. The
interposed class must have the same full name as the original. For example,
a class Foo may exist in package pub.foo and create an instance of class Bar
from package pub.bar. Creating a file Bar.java in another directory, such as
fake/pub/bar and prepending /fake to the classpath, ensures that the JVM
will load our definition of class pub.bar.Bar rather than the original. In
this way, we can interpose our definitions of Bar's methods over the
originals.
There is a problem, however, if we now wish to dispatch to the original
method. Since each classloader maintains a cache of unique class names, the
interposed and original classes pub.bar.Bar cannot coexist in the same
classloader's namespace. Therfore, the original class must be loaded by a
separate classloader (using a different classpath) and a technique must be
devised to "trampoline" from the interposed method call to the original.
===QUOTE===

and also said:

===QUOTE===
The proxy class is relatively flexible and straightforward to use, but there
are some distinct limitations on its use for method interposing. One
limitation is that the method must be called through an instance of the
proxy class. So nested methods calls, for instance, would not be
intercepted. Another limitation is that the method must have been defined in
an Interface that is implemented by the object being proxied. It can not be
called through an instance of a class that does not implement an interface.
The next article in this series will illustrate some techniques for
overcoming these limitations.
===QUOTE===

As far as I can determine, no such article was ever published. Does anyone
know of techniques that I can use to achieve the required result so that I
can intercept those method calls (preferably by just modifying CLASSPATH so
my jar file is picked up ahead of the other one).

Dave
 
C

Chris Smith

David said:
I want to intercept method calls on Java classes. The classes in question
are delivered by a third party in a jar file and I do not have access to the
sources for that. The client code that uses those classes is also not
available to me. If I were writing C code I'd just create a new shared
library that exported the API calls I wanted to intercept, and dynamically
load the real API calls (after adjusting the appropriate environment
variables so that my library was picked up first.

I was hoping to use java.lang.reflect.Proxy to do this, and in Tom Harpin's
article "Using java.lang.reflect.Proxy to Interpose on Java Class Methods",
he said:

Use of java.lang.reflect.Proxy looks rather suspect, unless you know
that a few conditions are true of the code you are trying to intercept.
Specifically, the methods you are intercepting would need to be
specified by interfaces, and the client code would need to call this
code only through those given interfaces. I had not read Tom's article
until now, but he does mention these limitations as well.

If that's not the case, then the Proxy class does not meet your needs,
and there's really no way to make it work.
As far as I can determine, no such article was ever published. Does anyone
know of techniques that I can use to achieve the required result so that I
can intercept those method calls (preferably by just modifying CLASSPATH so
my jar file is picked up ahead of the other one).

You won't be using Proxy, but it is possible to intercept method calls
by interposing your own code if you care to write your code statically
(i.e., you will have to write the intercepting class instead of having
it generated for you) and you know the APIs to implement.

The situation with classloaders is a bit difficult. You do *not* want
your intercepting code or the library code to be loaded by the system
classloader, because the names defined would then be effectively
globally accessible and would always conflict with any attempt to define
another such entry point with the same name. Furthermore, your
intercepting code cannot by loaded by the same loader as the library
code, because the entry point names are the same. You therefore need
three classloaders, chained in this specific way:

1. System classloader
1a) Intercepting classloader
1b) Library classloader

Now, you want the application to see your intercepting classes rather
than the library classes. The easiest way to do this is to load the
application into the intercepting classloader. That means that you'll
need a custom launcher class loaded by the system classloader, to
interject instead of the original class, which will set up the remaining
two classloaders. It would look like this (with tedious code omitted;
let me know if you don't know how to write the tedious code, and I'll
post it):

public class InterjectingLauncher
{
public static ClassLoader appLoader;
public static ClassLoader libLoader;

public static void main(String[] args)
{
if (args.length < 1) usage();
String className = args[0];
String[] newArgs = new String[args.length - 1];
System.arraycopy(/* from args to newargs */);

appLoader = new URLClassLoader(/* URL to app */);
libLoader = new URLClassLoader(/* URL to lib */);

/* Load className from appLaoder and invoke its main method */
}
}

Then you'd write your interceptors to explicitly switch over to the
other loader if you want to pass the call along. For example:

package original.package.name;

public class OriginalClassName
{
public void foo(Object param)
{
/* do your thing */

ClassLoader ldr = InterjectingLauncher.libLoader;
/* load "OriginalClassName" from ldr and dispatch */
}
}

That said, it's worth deciding if it's even worth your effort here. If
this isn't something that's going to happen at deployment, then can you
solve your problem with JVMPI or JVMDI instead. If it is happening
post-deployment, it's possible that there are legal problems with
creating a look-alike API in someone else's proprietary package (but I'm
not a lawyer, so check with yours for further advice there).

--
www.designacourse.com
The Easiest Way to Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,733
Messages
2,569,439
Members
44,829
Latest member
PIXThurman

Latest Threads

Top