Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [aspectj-users] WeavingAdapter - how to use it?

Hi,

> What I managed to do so far is load Foo.class from disk into a byte[] and manually weave it with ApplicationHackerAspect. The advice is triggered, so far so good. I can use > before or after, but when I use around I can only do that when I skip proceed(). As soon as I call proceed() I am running into

> java.lang.reflect.InvocationTargetException
> (...)
> Caused by: java.lang.NoSuchMethodError: de.scrum_master.aop.ltw_dynamic.Foo.getText_aroundBody0()Ljava/lang/String;
>        at de.scrum_master.aop.ltw_dynamic.Foo$AjcClosure1.run(Foo.java:1)
>       at com.vendor3.ApplicationHackerAspect.ajc$around$com_vendor3_ApplicationHackerAspect$1$30d0d5e4proceed(ApplicationHackerAspect.aj:8)
>        at com.vendor3.ApplicationHackerAspect.ajc$around$com_vendor3_ApplicationHackerAspect$1$30d0d5e4(ApplicationHackerAspect.aj:9)
>        at de.scrum_master.aop.ltw_dynamic.Foo.getText(Foo.java:4)

These can be tricky to diagnose. I feel like I've seen this before but
can't recall precisely why. Can you confirm the Foo that you define
actually has that method in? If you dump out the bytes you received
from the weaver to disk you can run javap on it to check the contents.
 Or post define you could query the class object that you get back to
see if it is there.

> Somehow there seems to be a class-loading issue. Probably this is because I don't know sh** about class-loading and LTW, weaving adapters and the JDK's instrumentation
> interface. Furthermore, I have no idea how to manually redefine/replace the original class Foo because mi woven version exists in a separate classloader, i.e. also somehow
> in isolation.

Replacement via classloader:
I don't think this is what you really want to do, but: If your Foo is
in a separate classloader, you can create another new classloader and
load a new Foo into it, then use that version instead of the one you
are using, but you need to break all ties with the original
classloader and that original loaded Foo - so that doesn't just mean
instances, you'll need to break your link to Foo.class. (if you don't
orphan it you'll leak permgen). Then you will be creating new
instances of the new Foo and plugging them in.  Having an interface
can help you here where the interface is loaded by some parent loader
and the implementation is loaded by a child classloader.  If your code
works through the interface it can be easier to plugin new
implementations behind the scenes.  This can be fiddly to get right.
OSGi offers this kind of facility.

Using the 'debugging' hotswap scheme:
If the JVM is running in the right mode you can replace a class on the
fly but with some limitations.  You can change method bodies but not
the shape of a class (cant add/remove fields/methods/etc).  This is OK
for before/after advice but not for around advice as you get those new
helper methods sometimes. Take a look on google for JVMTI class
redefinition and replacement.

Using a reloading agent:
This is an agent (different to the weaving agent) that allows for
class replacement like jrebel or springloaded.  With these you can
easily plug new versions of classes in adding/removing fields/methods.
 There are restrictions but they are less serious.  There is usually a
trade off cost here for the flexibility at runtime - typically higher
memory usage and somewhat slower code execution.


> I would be glad to get any new hints about what my class-loading issue with proceed() could be (and how to solve it) and what I need to do to really replace my wonderfully
> woven Foo class (later maybe even the Application class) in the main application context instead of creating a clone.

Getting past the proceed problem should be easily doable.  The
replacement is trickier.  What I've found with AspectJ is that is can
be easier just to always weave in your changes but include a suitable
guard on a boolean in the pointcut that is very cheap to test.  e.g.

pointcut(): whatever() && if(Globals.isEnabled) {}

private static boolean isEnabled;

just switch the global to switch the behaviour on/off.  This is common
practice for logging aspects.  Some tools like Spring Insight do the
configuration in aop.xml - so at loadtime the decision is made what to
weave in and then it is fixed until the next restart.

cheers,
Andy



On 4 September 2012 12:13, Alexander Kriegisch <alexander@xxxxxxxxxxxxxx> wrote:
> Hi Andy!
>
> Usually when I try to help people who do not clearly describe their problem, I tell them to share more information or just ask smarter. Thank you for not lecturing me on that (even though probably you should have) but trying instead to make educated guesses about my assumed problems and goals. Maybe I can do better this time. :-)
>
>>>> If you add "-Xbootclasspath/p:<path_to_aspects.jar>" JVM option, then you can instrument inside the JDKs and also instrument already loaded classes.
>>
>> Not sure that statement is quite correct.  You cannot just do that to
>> instrument already loaded classes
>
> Actually I do not use the command line option, I just quoted the full sentence. I am only interested in instrumenting already loaded classes. So here is my situation:
>
> I have written a little playground application:
>
> Eclipse project "main" (AspectJ):
>   - class Application
>   - interface Plugin
>  - aspect ApplicationAccessorAspect
>  - aspect PluginMonitorAspect
>
> Eclipse project "plugin 1"  (Java):
>   - class MyPlugin1 implements Plugin
> Eclipse project "plugin 2"  (Java):
>   - class MyPlugin2 implements Plugin
> Eclipse project "plugin 2b"  (Java):
>   - class MyPlugin2 implements Plugin
>
> ApplicationAccessorAspect monitors calls (not executions!) to getters/setters in Application.
>
> PluginMonitorAspect monitors executions (yes, this time) of two Plugin+ methods.
>
> I successfully tested the whole setup with a WeavingURLClassLoader which weaves all plugins upon class-loading. Log output shows that all my intercepted calls and executions in both the main application and the plugins are triggered perfectly. I even verified that MyPlugin2 (same class & package name in two projects "2" and "2b", just slightly different log output so I can differentiate them) are loaded separately because I am creating a new WeavingURLClassLoader instance each time I load a plugin. Thus, the plugins run in isolation from one another. I have basically created mini containers.
>
> Wonderful! Piece of cake! :-) But...
>
> But now I was getting ambitious with my next project:
>
> Eclipse project "plugin 3"  (AspectJ):
>   - class HackyPlugin implements Plugin
>   - class AspectJLTW (basically the class I quoted in my previous mail with slight changes)
>   - aspect ApplicationHackerAspect
>
> Okay, you see where I am going. I am trying to write a plugin which manipulates (advises, in AOP terms) its container's classes. For that purpose I need to be able to redefine an already loaded class. Because a plugin cannot just start a Java agent, I tried to do that manually utilising my version of AspectJLTW. Actually I want to redefine class Application, but because the loaded plugin is being advised by both PluginMonitorAspect and ApplicationAccessorAspect, plus the Application itself is also being advised by ApplicationAccessorAspect, I thought that for the first try I should rather advise another sibling class of the main project. So I added class Foo to the main project. It just has one static method returning a String, and I want to intercept its execution and manipulate the return value. (Are you still following?)
>
> What I managed to do so far is load Foo.class from disk into a byte[] and manually weave it with ApplicationHackerAspect. The advice is triggered, so far so good. I can use before or after, but when I use around I can only do that when I skip proceed(). As soon as I call proceed() I am running into
>
> java.lang.reflect.InvocationTargetException
> (...)
> Caused by: java.lang.NoSuchMethodError: de.scrum_master.aop.ltw_dynamic.Foo.getText_aroundBody0()Ljava/lang/String;
>         at de.scrum_master.aop.ltw_dynamic.Foo$AjcClosure1.run(Foo.java:1)
>         at com.vendor3.ApplicationHackerAspect.ajc$around$com_vendor3_ApplicationHackerAspect$1$30d0d5e4proceed(ApplicationHackerAspect.aj:8)
>         at com.vendor3.ApplicationHackerAspect.ajc$around$com_vendor3_ApplicationHackerAspect$1$30d0d5e4(ApplicationHackerAspect.aj:9)
>         at de.scrum_master.aop.ltw_dynamic.Foo.getText(Foo.java:4)
>
> Somehow there seems to be a class-loading issue. Probably this is because I don't know sh** about class-loading and LTW, weaving adapters and the JDK's instrumentation interface. Furthermore, I have no idea how to manually redefine/replace the original class Foo because mi woven version exists in a separate classloader, i.e. also somehow in isolation.
>
> Maybe I should have zipped up my Eclipse projects and attached them here, but I thought I should give it a try and explain in prose what I have accomplished and what remains to be achieved. My whole purpose is not to hack a server or anything, but to see how far I can push the limits of LTW and - foremost - to learn.
>
> I would be glad to get any new hints about what my class-loading issue with proceed() could be (and how to solve it) and what I need to do to really replace my wonderfully woven Foo class (later maybe even the Application class) in the main application context instead of creating a clone. In Star Trek TNG speak: I want to improve Data's programming, not replace him by his evil twin Lore. ;-)
>
> Cheers
> --
> Alexander Kriegisch
> _______________________________________________
> aspectj-users mailing list
> aspectj-users@xxxxxxxxxxx
> https://dev.eclipse.org/mailman/listinfo/aspectj-users


Back to the top