Proxies without ASM/CGLIB

33 views
Skip to first unread message

Gili

unread,
Nov 25, 2008, 3:29:36 AM11/25/08
to google-guice
Johan Haleby brought up an interesting approach to proxing classes
without the limitations introduced by ASM/CGLIB such as:

- requiring no-op constructors
- preventing the use of final or private methods
- losing annotations on the original class

I would love to get your opinion on it because frankly if we can get
rid of these limitations I would be *that* much happier :) Take a
look: http://groups.google.com/group/mockito/browse_thread/thread/2a85cace251eb92c

Gili

Anthony MULLER

unread,
Nov 25, 2008, 3:44:38 AM11/25/08
to google...@googlegroups.com
Another limitation is that the proxies are not serializable... I will look at your link, thanks!

Anthony


2008/11/25 Gili <gili.t...@gmail.com>

Stuart McCulloch

unread,
Nov 25, 2008, 4:01:56 AM11/25/08
to google...@googlegroups.com
2008/11/25 Gili <gili.t...@gmail.com>

FWIW, I wouldn't call this a proxy per se - this is bytecode manipulation
which requires access to the original bytes (it uses javassist to do this)
and only works on non-interface classes... but you can easily fall back
to using standard proxies for interfaces - downside is you end up with
two approaches to maintain

however I would be wary of calling this a 'silver' bullet because there are
several limitations wrt. bytecode manipulation (security notwithstanding)
- for example, I don't know if javassist works with custom classloaders as
used in EJB containers (and OSGi, etc.) where the original class bytes
may not be available, or where the original class may already be in-use
elsewhere in the JVM

just warning we may be jumping from known issues into the unknown ;)

Gili




--
Cheers, Stuart

Stuart McCulloch

unread,
Nov 25, 2008, 4:28:48 AM11/25/08
to google...@googlegroups.com
2008/11/25 Stuart McCulloch <mcc...@gmail.com>
2008/11/25 Gili <gili.t...@gmail.com>

Johan Haleby brought up an interesting approach to proxing classes
without the limitations introduced by ASM/CGLIB such as:

- requiring no-op constructors
- preventing the use of final or private methods
- losing annotations on the original class

I would love to get your opinion on it because frankly if we can get
rid of these limitations I would be *that* much happier :) Take a
look: http://groups.google.com/group/mockito/browse_thread/thread/2a85cace251eb92c

FWIW, I wouldn't call this a proxy per se - this is bytecode manipulation
which requires access to the original bytes (it uses javassist to do this)

just want to point out that javassist does provide a "ProxyFactory", but this
uses the same approach as ASM/CGLIB ie. extending the given superclass
and implementing the interfaces - their proxy is serializable (so proxies can
in fact be serializable) ... *but* they warn that serialized proxies should only
be used for short-term storage or RMI, because of migration issues

FYI, their approach to serialization is to write out a special SerializedProxy
instance to the object stream, in place of the proxy instance - this instance
stores the superclass, interfaces, etc. in fields which it uses to recreate the
proxy in readResolve() when deserializing

and only works on non-interface classes... but you can easily fall back
to using standard proxies for interfaces - downside is you end up with
two approaches to maintain

however I would be wary of calling this a 'silver' bullet because there are
several limitations wrt. bytecode manipulation (security notwithstanding)
- for example, I don't know if javassist works with custom classloaders as
used in EJB containers (and OSGi, etc.) where the original class bytes
may not be available, or where the original class may already be in-use
elsewhere in the JVM

just warning we may be jumping from known issues into the unknown ;)

Gili




--
Cheers, Stuart



--
Cheers, Stuart

Esko Luontola

unread,
Nov 25, 2008, 5:27:12 AM11/25/08
to google-guice
On Nov 25, 10:29 am, Gili <gili.tzab...@gmail.com> wrote:
> - requiring no-op constructors

I assume that you are referring to the fact a CGLIB proxy calls the
constructor of its superclass, which means that the class being
proxied (unless only interfaces are proxied) must have an accessible
constructor, possibly even a default constructor.

I need to overcome this limitation of requiring an accessible default
constructor for one project of mine, so I'll be modifying CGLIB so
that it ignores the superclass's constructors. I have posted a proof
of concept at the the issue which is tracking this feature request:
http://sourceforge.net/tracker/index.php?func=detail&aid=2070600&group_id=56933&atid=482371

Would this feature be useful for Guice?

Anthony MULLER

unread,
Nov 25, 2008, 8:30:34 AM11/25/08
to google...@googlegroups.com
Exactly what I need: to serialize/unserialize objects (proxified by Guice/cglib) in a short time....

It should be great if it was possible to do this using Guice !

Anthony


> just want to point out that javassist does provide a "ProxyFactory", but this
> uses the same approach as ASM/CGLIB ie. extending the given superclass
> and implementing the interfaces - their proxy is serializable (so proxies can
> in fact be serializable) ... *but* they warn that serialized proxies should only
> be used for short-term storage or RMI, because of migration issues

> FYI, their approach to serialization is to write out a special SerializedProxy
> instance to the object stream, in place of the proxy instance - this instance
> stores the superclass, interfaces, etc. in fields which it uses to recreate the
> proxy in readResolve() when deserializing


Gili

unread,
Nov 25, 2008, 12:02:34 PM11/25/08
to google-guice
Hi Stuart,

It is my understanding that all of the difficulties come from proxing
concrete classes, not interfaces (the latter can be proxied quite
easily using the core API). I think it's well worth using two
different implementations for interfaces and concrete classes so long
as the differences can be abstracted away from end-users and it really
overcomes all the limitations I and you listed (i.e. EJB containers).

Let's ask Johan, perhaps he's already investigated EJB space.

Gili

On Nov 25, 4:01 am, "Stuart McCulloch" <mccu...@gmail.com> wrote:
> 2008/11/25 Gili <gili.tzab...@gmail.com>
>
>
>
> > Johan Haleby brought up an interesting approach to proxing classes
> > without the limitations introduced by ASM/CGLIB such as:
>
> > - requiring no-op constructors
> > - preventing the use of final or private methods
> > - losing annotations on the original class
>
> > I would love to get your opinion on it because frankly if we can get
> > rid of these limitations I would be *that* much happier :) Take a
> > look:
> >http://groups.google.com/group/mockito/browse_thread/thread/2a85cace2...

Gili

unread,
Nov 25, 2008, 12:05:07 PM11/25/08
to google-guice
The main thing I like about Johan's approach is that it maintains full
backwards compatibility with the original class. There is no magic.
You are invoking the original constructors and everything works as
expected.

Using Sun's hidden API to construct objects without invoking their
constructors is part of the magic I'd like to do away with.

PS: Sun is beta-testing changes to ClassLoader in Java7 so now would
be a really good time to request enhancements in this space if you've
got any.

Gili
> of concept at the the issue which is tracking this feature request:http://sourceforge.net/tracker/index.php?func=detail&aid=2070600&grou...

Stuart McCulloch

unread,
Nov 25, 2008, 12:50:43 PM11/25/08
to google...@googlegroups.com
2008/11/26 Gili <gili.t...@gmail.com>

Hi Stuart,

It is my understanding that all of the difficulties come from proxing
concrete classes, not interfaces (the latter can be proxied quite
easily using the core API).

that's why I always prefer to program to interfaces ;)
 
I think it's well worth using two
different implementations for interfaces and concrete classes so long
as the differences can be abstracted away from end-users and it really
overcomes all the limitations I and you listed (i.e. EJB containers).

for testing it is a really cool idea, but for server-side I would be cautious

my main concern is how you'd handle the situation where someone else is
using class (A) but Guice wants to "proxy" it by rewriting - the new definition
(A') would need to be loaded into another classloader, but that means that
class A and class A' are incompatible, and people who expect A wouldn't be
able to use A'

(unless they cast it to a common superclass, but if you were going to do this
 then you wouldn't mind whether the proxy had subclassed A to begin with!)

I think this is an issue, but I'd be interested to hear what other people think

Let's ask Johan, perhaps he's already investigated EJB space.

certainly, although having discussed this with him before I believe his focus
is still on testing (at least for the near future) as it's much easier to rewrite
classes in the "set-up/tear-down" model where you can throw away loaders
between tests...




--
Cheers, Stuart

Stuart McCulloch

unread,
Nov 25, 2008, 1:11:05 PM11/25/08
to google...@googlegroups.com
2008/11/26 Gili <gili.t...@gmail.com>

The main thing I like about Johan's approach is that it maintains full
backwards compatibility with the original class. There is no magic.
You are invoking the original constructors and everything works as
expected.

except that your redefined class is actually incompatible with the original class
because they exist in different classloader spaces - the only way you could use
instances of the mocked class alongside the original would be if you cast them
to a superclass or interface

perhaps I'm reading it wrongly - but how is the bytecode manipulation not magic?
 
Using Sun's hidden API to construct objects without invoking their
constructors is part of the magic I'd like to do away with.

which hidden API is this? I've written my own basic proxies in the past with ASM,
they just initialize the superclass via the "<init>" method - and afaik there is no
reason why a proxy couldn't call a non-default constructor

in fact... checking javassist, I see they also support non-default constructors:

   http://www.csg.is.titech.ac.jp/~chiba/javassist/html/javassist/util/proxy/ProxyFactory.html#create(java.lang.Class[],%20java.lang.Object[])

so serialization and non-default constructors are technically possible - just as
it would also be possible to copy annotations onto the proxy class (given time!)

PS: Sun is beta-testing changes to ClassLoader in Java7 so now would
be a really good time to request enhancements in this space if you've
got any.

Gili

On Nov 25, 5:27 am, Esko Luontola <esko.luont...@gmail.com> wrote:
> On Nov 25, 10:29 am, Gili <gili.tzab...@gmail.com> wrote:
>
> > - requiring no-op constructors
>
> I assume that you are referring to the fact a CGLIB proxy calls the
> constructor of its superclass, which means that the class being
> proxied (unless only interfaces are proxied) must have an accessible
> constructor, possibly even a default constructor.
>
> I need to overcome this limitation of requiring an accessible default
> constructor for one project of mine, so I'll be modifying CGLIB so
> that it ignores the superclass's constructors. I have posted a proof
> of concept at the the issue which is tracking this feature request:http://sourceforge.net/tracker/index.php?func=detail&aid=2070600&grou...
>
> Would this feature be useful for Guice?




--
Cheers, Stuart

Gili Tzabari

unread,
Nov 25, 2008, 1:35:18 PM11/25/08
to google...@googlegroups.com
Stuart McCulloch wrote:
> my main concern is how you'd handle the situation where someone else is
> using class (A) but Guice wants to "proxy" it by rewriting - the new
> definition
> (A') would need to be loaded into another classloader, but that means that
> class A and class A' are incompatible, and people who expect A wouldn't be
> able to use A'

There are two cases:

1) A is shared between different webapps. I am not particularly
interested in this use-case but I'm sure you can come up with
workarounds for it too.

2) A is used exclusively by one webapp, but someone else might load it
before Guice. We can solve this quite easily by proxying it eagerly
before anyone else without instantiating any instances.

My only concern is what happens if another framework sits on the same
webapp and also has a custom ClassLoader. Is there such a thing as
ClassLoaders cooperating?

Gili

Stuart McCulloch

unread,
Nov 25, 2008, 2:03:21 PM11/25/08
to google...@googlegroups.com
2008/11/26 Gili Tzabari <gili.t...@gmail.com>

Stuart McCulloch wrote:
> my main concern is how you'd handle the situation where someone else is
> using class (A) but Guice wants to "proxy" it by rewriting - the new
> definition
> (A') would need to be loaded into another classloader, but that means that
> class A and class A' are incompatible, and people who expect A wouldn't be
> able to use A'

       There are two cases:

1) A is shared between different webapps. I am not particularly
interested in this use-case but I'm sure you can come up with
workarounds for it too.

2) A is used exclusively by one webapp, but someone else might load it
before Guice. We can solve this quite easily by proxying it eagerly
before anyone else without instantiating any instances.

it's the eager proxying that I'm worried about - could get hairy

for example: how do I tell Guice about the class that should be proxied
without actually loading that class (as Guice works primarily on types)

       My only concern is what happens if another framework sits on the same
webapp and also has a custom ClassLoader. Is there such a thing as
ClassLoaders cooperating?

classloaders can delegate requests between each other - typically this
is done as a tree (child->parent) but OSGi also supports graph based
delegation, which is how it controls visibility and manages updates

however, two classes of the same name defined by two different loaders
are distinct and incompatible (!A.equals(A')) which is the problem I have
with redefining the class at runtime - as Johan says, it has to go into a
separate classloader, unless you use an instrumentation agent

there are also visibility limitations - if I have non-public classes A and B
in the same package "foo" then they can only see each other if they're
loaded by the same classloader (delegation doesn't help here)

Gili

--
Cheers, Stuart

Gili Tzabari

unread,
Nov 25, 2008, 2:22:42 PM11/25/08
to google...@googlegroups.com
Stuart McCulloch wrote:
> it's the eager proxying that I'm worried about - could get hairy
>
> for example: how do I tell Guice about the class that should be proxied
> without actually loading that class (as Guice works primarily on types)

Alternatively you could register the Guice classloader eagerly and
ensure that any types you refer to beyond that point would go through it
(by setting it as the Thread context CL for example). Any class injected
by Guice would automatically use the right CL. The only problem would
come from any classes loaded before the registration of the Guice CL.
Though, I suspect you should be able to do this very early on in most cases.

> however, two classes of the same name defined by two different loaders
> are distinct and incompatible (!A.equals(A')) which is the problem I have
> with redefining the class at runtime - as Johan says, it has to go into a
> separate classloader, unless you use an instrumentation agent

I believe instrumentation agent wouldn't work in the context of a web
container since you don't want to affect classes outside your webapp.

> there are also visibility limitations - if I have non-public classes A and B
> in the same package "foo" then they can only see each other if they're
> loaded by the same classloader (delegation doesn't help here)

Agreed, but I assume that if you're going to inject one class in a
package you're likely going to use injection for the rest of them too
(or load them from classes that *have* been injected).

Gili

Stuart McCulloch

unread,
Nov 25, 2008, 11:37:11 PM11/25/08
to google...@googlegroups.com
2008/11/26 Gili Tzabari <gili.t...@gmail.com>

Stuart McCulloch wrote:
> it's the eager proxying that I'm worried about - could get hairy
>
> for example: how do I tell Guice about the class that should be proxied
> without actually loading that class (as Guice works primarily on types)

       Alternatively you could register the Guice classloader eagerly and
ensure that any types you refer to beyond that point would go through it
(by setting it as the Thread context CL for example). Any class injected
by Guice would automatically use the right CL. The only problem would
come from any classes loaded before the registration of the Guice CL.
Though, I suspect you should be able to do this very early on in most cases.

so essentially with this technique we'd be limiting how you could use Guice?

sorry, but personally it sounds that this introduces problems for 90% of users
while (possibly) fixing an issue that affects 10% at most - as Java developers
we use sub-classing all over the place, why not use it when proxying?

the issues about serialization and default constructors are solvable without
redefining the original class - and I suspect you'd also run into serialization
issues even when using class redefinition, as any method interception could
distribute the state to fields in objects unknown to the original class
 
[FYI, setting the TCCL doesn't work for legacy code that uses Class.forName]

> however, two classes of the same name defined by two different loaders
> are distinct and incompatible (!A.equals(A')) which is the problem I have
> with redefining the class at runtime - as Johan says, it has to go into a
> separate classloader, unless you use an instrumentation agent

       I believe instrumentation agent wouldn't work in the context of a web
container since you don't want to affect classes outside your webapp.

exactly, so you have to use a separate classloader and deal with visibility issues

> there are also visibility limitations - if I have non-public classes A and B
> in the same package "foo" then they can only see each other if they're
> loaded by the same classloader (delegation doesn't help here)

       Agreed, but I assume that if you're going to inject one class in a
package you're likely going to use injection for the rest of them too
(or load them from classes that *have* been injected).

and what happens if I (as a user of Guice) don't want to inject every class from a
package? Even in the general case, how do I pass Guice a module with bindings
for a package without loading that package first?

I guess you'd have to ensure Guice was at the top of the classloader chain, and
have some way of marking classes you wanted redefined early on - but you'd be
limiting how people could use Guice (ie. no use as a bundle, no way to upgrade
without restarting the process) - all to fix some issues which can be fixed using
normal sub-classing (as I think javassist shows)

Gili

--
Cheers, Stuart

Gili Tzabari

unread,
Nov 25, 2008, 11:59:31 PM11/25/08
to google...@googlegroups.com

I guess I agree with you for the most part. Thing is, for all this talk
about how "it's all fixable" no one actually done so... I don't recall
hearing of a single project that tried to improve the situation. I might
sound silly saying this... but it really ticks me off. I hate using APIs
that force no-op constructors and inappropriate method accessibility on
me. CGLIB acts more like a framework than a library in the sense that it
forces its bad design on you :( Anyway, end of rant. I'll be happy when
I can avoid CGLIB altogether in Guice.

Gili

Stuart McCulloch wrote:
> 2008/11/26 Gili Tzabari <gili.t...@gmail.com
> <mailto:gili.t...@gmail.com>>

Stuart McCulloch

unread,
Nov 26, 2008, 12:18:09 AM11/26/08
to google...@googlegroups.com
2008/11/26 Gili Tzabari <gili.t...@gmail.com>
       I guess I agree with you for the most part. Thing is, for all this talk
about how "it's all fixable" no one actually done so...

yep, I think proxying deserves more attention - especially wrt. annotation support
(unfortunately at the moment it's easier to workaround it than deal with it upfront)

I don't recall hearing of a single project that tried to improve the situation.

well I haven't looked that closely, but javassist tried (not sure how successfully)

I might sound silly saying this... but it really ticks me off. I hate using APIs
that force no-op constructors and inappropriate method accessibility on
me. CGLIB acts more like a framework than a library in the sense that it
forces its bad design on you :( Anyway, end of rant. I'll be happy when
I can avoid CGLIB altogether in Guice.

definitely - making this pluggable/optional should make everyone happy
 



--
Cheers, Stuart

Johan Haleby

unread,
Nov 26, 2008, 2:26:35 AM11/26/08
to google-guice
Perhaps I'm mistaken but mabey aspectj would be able to help you here?
AJDT works in the Eclipse environment and thus should work in OSGi as
well and afaik it would be possible to do delegation without proxying
the classes using this approach? I think Spring uses aspectj to inject
beans to instances that are not part of the Spring life-cycle (you use
the @Configurable annotation). I guess it would be possible to start
transactions and all kinds of stuff this way as well?

On 26 Nov, 06:18, "Stuart McCulloch" <mccu...@gmail.com> wrote:
> 2008/11/26 Gili Tzabari <gili.tzab...@gmail.com>
>
> >        I guess I agree with you for the most part. Thing is, for all this
> > talk
> > about how "it's all fixable" no one actually done so...
>
> yep, I think proxying deserves more attention - especially wrt. annotation
> support
> (unfortunately at the moment it's easier to workaround it than deal with it
> upfront)
>
> I don't recall hearing of a single project that tried to improve the
>
> > situation.
>
> well I haven't looked that closely, but javassist tried (not sure how
> successfully)
>
> I might sound silly saying this... but it really ticks me off. I hate using
>
> > APIs
> > that force no-op constructors and inappropriate method accessibility on
> > me. CGLIB acts more like a framework than a library in the sense that it
> > forces its bad design on you :( Anyway, end of rant. I'll be happy when
> > I can avoid CGLIB altogether in Guice.
>
> definitely - making this pluggable/optional should make everyone happy
>
>
>
> > Gili
>
> > Stuart McCulloch wrote:
> > > 2008/11/26 Gili Tzabari <gili.tzab...@gmail.com
> > > <mailto:gili.tzab...@gmail.com>>

Stuart McCulloch

unread,
Nov 26, 2008, 3:22:37 AM11/26/08
to google...@googlegroups.com
2008/11/26 Johan Haleby <johan....@gmail.com>

Perhaps I'm mistaken but mabey aspectj would be able to help you here?
AJDT works in the Eclipse environment and thus should work in OSGi as
well

actually if you're using OSGi, I'd suggest looking at Equinox Aspects:

   http://www.eclipse.org/equinox/incubator/aspects/

they've got AspectJ and OSGi working smoothly together, but I digress...

and afaik it would be possible to do delegation without proxying
the classes using this approach? I think Spring uses aspectj to inject
beans to instances that are not part of the Spring life-cycle (you use
the @Configurable annotation). I guess it would be possible to start
transactions and all kinds of stuff this way as well?

but Spring afaik does not use AspectJ for proxying - according to their
docs on AOP they only use JDK or CGLIB proxies, just like Guice does:

   http://static.springframework.org/spring/docs/2.5.x/reference/aop.html

however, Spring does provide integration support for using injection with
AspectJ - but again, this is different from proxying because you're given
a _pre-existing_ object (already woven) and asked to inject stuff into it:

  http://static.springframework.org/spring/docs/2.5.x/reference/aop.html#aop-using-aspectj

so it would be possible to write a similar Guice extension to support AspectJ,
probably using injectMembers (basically what @Configurable does in Spring)
but this would not replace proxying - it's an alternative approach where you
don't need to proxy in the first place, because you've already woven your
classes in advance

so to sum up:

   adding support for pluggable / optional proxying would be cool

   writing a Guice extension to fit with AspectJ would also be cool

but imho (as a third party) there is no need to use AspectJ inside Guice core

HTH




--
Cheers, Stuart

Johan Haleby

unread,
Nov 26, 2008, 3:58:23 AM11/26/08
to google...@googlegroups.com
I didn't mean that they used aspectj for proxying but rather suggesting that aspectj could be used instead of proxying to achieve the same results. We discussed earlier that e.g. guice-warp couldn't support final or private methods for starting transacation because it used CGLib, but using a delegating approach instead of using a proxy could solve the problem (which you seem to imply as well if I'm not misstaken). But whether this is applicable to Guice is another question.

I think it would be very cool if Guice could support something similar as Springs AOP support for injection. Actually I'd like to create a spike for this if I get some time over, I've been needing this kind of stuff myself.

/Johan

Stuart McCulloch

unread,
Nov 26, 2008, 4:12:37 AM11/26/08
to google...@googlegroups.com
2008/11/26 Johan Haleby <johan....@gmail.com>
I didn't mean that they used aspectj for proxying but rather suggesting that aspectj could be used instead of proxying to achieve the same results.

exactly - I think the wires got crossed somewhere between your replies to the other thread and the start of this one

We discussed earlier that e.g. guice-warp couldn't support final or private methods for starting transacation because it used CGLib, but using a delegating approach instead of using a proxy could solve the problem (which you seem to imply as well if I'm not misstaken). But whether this is applicable to Guice is another question.

the question came up here relating to Guice's use of CGLIB for AOP/proxy support ... Gili was wondering whether
Guice could use class redefinition to implement proxying (ie. still behaving as a proxy) - which I don't think it could,
because of classloading visibility - but users could decide to weave classes externally using aspects and then use
Guice to inject them later, which is basically what Spring does with @Configurable

I think it would be very cool if Guice could support something similar as Springs AOP support for injection. Actually I'd like to create a spike for this if I get some time over, I've been needing this kind of stuff myself.

it's certainly possible and shouldn't require any changes to Guice core - you'd need to create an extension library,
like the small Spring-AspectJ library under Spring-AOP, which can find instances and ask Guice to inject them
 
/Johan
--
Cheers, Stuart
Reply all
Reply to author
Forward
0 new messages