AspectJ resurged over on TSS and one comment reminded me of the singly most useful thing I've done with it for production purposes: used it to augment a very large third party library. This, of course, is a taboo thing to do, but if the library you are mucking around with is, er, the AspectJ compiler... maybe it is okay?
At the time, anyway, the AspectJ compiler had a useful incremental compilation mode where it would keep metadata in memory and basically have a server compiler process. This is fine and dandy if you don't mind hitting the space bar to do an incremental compile. It turned out to be far easier to instrument the compiler to have it take input from a tcp socket so that an ant task could kick off the incremental compile from IDEA =)
Probably not what people would consider good but it reduced compile time from minutes to seconds =)
0 writebacks [/src/java/aop] permanent link
Mon, 21 Jun 2004
Leaning on the Crutch Once More: Detecting GC'd Objects
Leo Simons challenged me a while back to do super-clean object pooling via aspects. I couldn't quite get it, but I tried. I fired up Þe old IDEA and got a step closer today. Still haven't gotten around the new
function declaring a type for its return value yet,though. I mean, you call new Foo()
and expect an actual instance of Foo
. How unreasonable is that? But I digress.
The ReleaseManager
class returns a proxy implementing all the interfaces of a class, and allows for the detection and registration of callbacks (which receive the real instance) for when the proxy falls out of scope. Nothing fancy here, most (good) pooled DataSource
implementations do this now for Connection
instances they loan out.
The fun part is applying an aspect to capture and do this for you to provide "transparent pooling". There is still a problem of needing a GC to force return to the pool. Additionally, you cannot capture on public Mammal+.new(..)
and return anything other than a correct instance of the class being newed (maybe you could return a subclass, which opens up the possibility of cglib proxies, but that has heavy-object ramifications).
I'm still thinking about it, Leo, and I am exceptionally stubborn =)
3 writebacks [/src/java/aop] permanent link
Wed, 02 Jun 2004
Exceptional Transactions and Bad AOP Idioms
I needed yet-another-cmt-in-j2se so went about it the "right way" -- using aspects. It drilled into me a serious trap I have fallen into -- using globals (static singletons) for communication with aspects =/ Lets look at what I am doing:
This is a simple implementation of container manager transactions in AspectJ =) It doesn't support all six declarations, but only the four that I actually use (requires, supports, never, mandatory). Supports is the default.
package org.skife.transition;
public abstract aspect Transition
{
abstract pointcut require();
abstract pointcut mandatory();
abstract pointcut never();
pointcut transacted() : require() && !cflowbelow(require());
before() : never() {
Transaction tx = TxProxy.instance().currentTransaction();
if (tx != null && tx.isInProgress()) {
throw new IllegalStateException("'NEVER' tx method invoked inside tx");
}
}
before() : mandatory() {
Transaction tx = TxProxy.instance().currentTransaction();
if (tx == null || !tx.isInProgress()) {
throw new IllegalStateException("'MANDATORY' tx method invoked outside tx");
}
}
/**
* Begin a transaction if one is not in progress
*/
before() : transacted() {
Transaction tx = TxProxy.instance().currentTransaction();
if (tx == null) {
tx = TxProxy.instance().newTransaction();
}
if (!tx.isInProgress()) {
tx.begin();
}
}
/**
* Commit Transaction
*/
after() returning : transacted() {
Transaction tx = TxProxy.instance().currentTransaction();
if (tx != null && tx.isInProgress()) {
tx.commit();
}
}
/**
* Rollback on exception
*/
after() throwing : transacted() {
Transaction tx = TxProxy.instance().currentTransaction();
if (tx != null && tx.isInProgress()) {
tx.rollback();
}
}
}
Once again it uses a singleton helper class to handle interactions with the external system (whatever provides the transaction service -- JDBC, OJB, JTA, Hibernate, Spring, etc). This type of singleton bridge seems to be coming up a lot in my aspects, unfortunately. I think it points out a serious weakness in my knowledge of how to use the tool.
Adrian Colyer has done some work with letting Spring configure aspects in AspectJ, and I need to play with this some more. That may be an answer. He seems to use rather intimate knowledge of how AspectJ compiles aspects in though, which I am not sure I am comfortable depending on if it isn't part of the spec =/
The problem I run into is when I need to have the same aspects talking to different component implementations in the same VM. BANG. It is the wonderful service-lookup issue all over again.
4 writebacks [/src/java/aop] permanent link
Thu, 27 May 2004
ObjectFilter and Collections -- Why Communities are Important
I have been wrestling with handling collections in a robust manner in ObjectFilter for a while. The initial implementation cheated, it returned a different, unmodifiable collection. The next implementation cheated, it returned a different collection which delegated everything important to the real one. The next implementation was going to cheat again, it would return a cglib generated proxy which acted like the second impl, but got rid of nasty class cast exceptions on returns expecting the concrete impl. All of these basically sucked.
So I explain the problem to my friend Shaun and in five minutes he points out that instead of advising the collection it should be advising the calls to the collection. Duh. This makes the collection advice simply a special case of the property advice where it pretends that the instance doesn't exist instead of [nulling|throwing exception|whistling dixie].
More importantly, it points out how screamingly important it is to ask other people when you are stuck =)
0 writebacks [/src/java/aop] permanent link
Fri, 14 May 2004Get thee hence and subscribe to Adrian's blog.
6 writebacks [/src/java/aop] permanent link
Thu, 13 May 2004
Solved: Advice on Started Threads
Moved the subject into a WeakHashMap
keyed to Thread
and
simply populated it based on the started thread =)
package org.skife.objectfilter;
public aspect AssociateSubjectWithSpawnedThreads
{
pointcut startThread(Thread thread): target(thread)
&& call(void java.lang.Thread.start());
before(Thread thread) : startThread(thread) {
Filter.instance().setSubject(thread, Filter.instance().getSubject());
}
}
Next glaring hole: block ability to change subject except when allowed by a given pointcut. Funny how most of the configuration for this tool is pointcut definition.
My goal for ObjectFilter, btw, is to give someone the ability to execute arbitrary code (Groovy, BeanShell, Jython, etc) in a VM, with access to the sources for whatever is in it, and still be able to prevent any information leakage =)
Pushed newest tarball as well. Lots of minor changes.
0 writebacks [/src/java/aop] permanent link
LazyWeb: Advice on Started Threads
Code shows the problem best, so... Given the following sample code:
public void testSubjectFollowsSpawnedThreads()
{
// Never allow access to teacher
rp.addRules(Teacher.class, new Rules()
{
public boolean allow(Object subject, Object object)
{
return false;
}
});
Teacher teacher = new Teacher("Mr. Brown");
final ClassRoom english = new ClassRoom(teacher);
filter.setSubject(new Object());
// Verify subject set on current thread
assertNull(english.getTeacher());
final boolean [] blocked = {false};
final boolean [] finished = {false};
Thread thread = new Thread(new Runnable()
{
public void run()
{
blocked[0] = (english.getTeacher() == null);
finished[0] = true;
}
});
thread.start();
while (!finished[0])
{
Thread.yield();
}
assertTrue(blocked[0]);
}
I am trying to define a pointcut in AspectJ to match the beginning of the execution of the Runnable
associated with thread
. I need to be able to access some static context in the spawning thread and pass it into the advice (the current security subject). I have tried a couple things and failed to get it right =( I am seriously thinking that a static thread-local subject may not be the best way of specifying the subject, but I don't have a better one. Any thoughts on pointcut definition?
1 writebacks [/src/java/aop] permanent link
Wed, 12 May 2004
ObjectFilter and the Canadian Army and DARPA and Bears (oh my!)
So, I have said the problem ObjectFilter tries to solve is a low-hanging fruit problem. After running into a reference to Captain Jason Furlong's thesis proposal I did some more google research (ie, not research, but disturbingly effective).
Apparently DARPA has been handing out grants to develop just this type of system!
0 writebacks [/src/java/aop] permanent link
Fri, 30 Apr 2004
Drools Module for ObjectFilter
Started generalizing ObjectFilter a little bit more by seperating out the rule providers. The style seen previous (manual rule implementation) has been moved into ManualRuleProvider
, and I added a DroolsRuleProvider
which uses Drools rules for access permissions, as follows:
public void testSlightlyMoreComplexRools() throws Exception
{
Filter filter = Filter.instance();
filter.configure(new DroolsRuleProvider(complex));
filter.setActor(new Actor());
Teacher mr_brown = new Teacher("Mr. Brown");
ClassRoom english = new ClassRoom(mr_brown);
assertEquals(mr_brown, english.getTeacher());
ClassRoom maths = new ClassRoom(new Teacher("Mr. White"));
assertNull(maths.getTeacher());
}
Which looks pretty similar so far. The configure
method was added to allow for plugging rule systems in. The complex
argument is a java.net.URL
pointing to the Drools ruleset.
The rule set for this example looks like:
<rule-set name="DroolsTest"
xmlns="http://drools.org/rules"
xmlns:java="http://drools.org/semantics/java">
<rule name="Mr. Brown is Friendly">
<parameter identifier="actor">
<java:class>org.skife.objectfilter.drools.Actor</java:class>
</parameter>
<parameter identifier="response">
<java:class>org.skife.objectfilter.drools.Response</java:class>
</parameter>
<parameter identifier="subject">
<java:class>org.skife.objectfilter.Teacher</java:class>
</parameter>
<java:condition>
subject.getName().equals("Mr. Brown")
</java:condition>
<java:consequence>
response.setAllowed(true);
</java:consequence>
</rule>
...
stuff removed
...
</rule-set>
The actor and instance being tested are bother asserted into the working memory (new working memory for each test, awkward, but I don't have a better solution in mind right now, any ideas?), rules are fired, and the response is tested to see if the access should be granted.
The response (which provides a way for the rules to tell the Filter what to do, and defaults to "false" to block access) can probably be moved to the ApplicationData
bit of Drools, but I haven't done it yet and am getting sleepy =) I am, also, not fully satisfied with the Actor
thing I used for this. In order to distinguish between Actor and Subject in the rule I made the actor a specific class -- this is probably how it will need to be done. Another option I am thinking about is to declare mixin tags for Actor or Subject types, but this seems much more intrusive.
I think for now users just need to be aware that there is no convenient way to distinguish between objects asserted as actors and objects asserted as subjects if their types overlap.
The source tarball for the changes has been posted =) Once again, it doesn't include the AspectJ jars as they are big.
3 writebacks [/src/java/aop] permanent link
Tue, 27 Apr 2004
ObjectFilter Test Object Model
Just to verify there is no magic going on in the objects themselves:
The aspects handle the filtering, not the classes. They are ignorant of the filter. Also, they are unaffected by it:
public void testBlockReference()
{
Filter filter = Filter.instance();
filter.setActor(new Object());
filter.addRules(Teacher.class, new Rules()
{
public boolean allow(Object actor, Object instance)
{
return false;
}
});
Teacher mr_brown = new Teacher("Mr. Brown");
ClassRoom english = new ClassRoom(mr_brown);
assertNull(english.getTeacher());
filter.clearActor();
assertEquals(mr_brown, english.getTeacher();
}
By default, if no actor is set nothing is filtered (this is really important).
1 writebacks [/src/java/aop] permanent link
ObjectFilter: AOP Rule Based Object Access
I took a couple hours this evening to port my object-access security aspects from YAIL to AspectJ this evening -- and like the results so much I am releasing them.
ObjectFilter is designed to raise the bar on access security. I cannot count the different ways I have gone about preventing information leakage by checking primary keys etc. Its awful. AOP makes it much easier. Look carefully at the two test cases here:
public void testBlockReference()
{
Filter filter = Filter.instance();
filter.setActor(new Object());
filter.addRules(Teacher.class, new Rules()
{
public boolean allow(Object actor, Object instance)
{
return false;
}
});
Teacher mr_brown = new Teacher("Mr. Brown");
ClassRoom english = new ClassRoom(mr_brown);
assertNull(english.getTeacher());
}
public void testAllowReference()
{
Filter filter = Filter.instance();
filter.setActor(new Object());
filter.addRules(Teacher.class, new Rules()
{
public boolean allow(Object actor, Object instance)
{
return true;
}
});
Teacher mr_smith = new Teacher("Mr. Smith");
ClassRoom english = new ClassRoom(mr_smith);
assertEquals(mr_smith, english.getTeacher());
}
The one nulls out the reference, the other allows it. ObjectFilter works by applying rules (abstracted out to the Rules
interface) governing access to particular types. The rules reference a thread local actor (ObjectFilter presumes you can say only one user is on a thread) for testing access.
In addition to nulling out properties, it has limited abilities at prsent to filter collections (only allow client code to see what it is allowed to see).
The rules used in the tests above are pretty simplistic. I've done fancier in in the YAIL stuff, and in a proprietary library, but the best will probably grow out of this as, well, it is open source and there are smarter people than me around. I expect writing rules as Groovy closures might work well ;-) Right now rules are set on a type, and the logical AND
of all the Rules
instances that apply to an instance are used (check out this test for an example, near the end of it with StudentTeacher
The source tarball is available under an ASL 2.0 license. Have fun. The AspectJ jars are not included in the tarball, need to save some of my bandwidth. Just grab AspectJ 1.1 from eclipse.org and drop aspectjrt.jar
and aspectjtools.jar
in the lib
Feedback is much appreciated -- particularly in how to more elegantly handle collections and define the pointcuts for the two aspects (look for the aspects in the src/test tree to see what I mean). I am thinking about deriving them via an xdoclet/qdox style system and generating the sub-aspect at compile time. Dunno, need to think about it.
4 writebacks [/src/java/aop] permanent link
Thu, 15 Apr 2004
Using AspectJ to Instrument the AspectJ Compiler
Problem: AspectJ incremental compiler requires manual intervention to do a compile. It acts like a daemon and tracks its state in memory. It supports stdin recompile commands or watching the timestamp on a flag file.
The real problem is that we want to be able to do a fast recompile (incremental) from an IDE so we can run unit tests frequently. This is more or less incompatible with how incremental works in iajc.Solution 1: Use ruby's popen to handle the "press enter to recompile" type interactive compile. Have the script open a socket and listen for connections on some random high port. When a connection is established recompile and flag the client when it is finished.
However, java and ruby don't get along -- java sends an EOF instead of blocking to ruby's gets
when there is no input to read, ruby sees the EOF and closes the IO
instance =(
Solution 2: Modify the aspectj compiler to signal its state somehow. The aspectj compiler code is... interesting (in the art critic sense). You don't see named continues very often in Java for a reason. Maybe they like them at PARC, who knows.
Solution 2.1: Use aspectj to instrument the aspectj compiler to provide event callback hooks on the incremental compiler. Bingo!
This was actually really fun, but it means our build system uses a custom aspectjtools.jar
. That said, recompiles are down to hundreds of milliseconds from a couple minutes. Basically I just added advice to the incremental looper to invoke named (properties file) java.lang.Runnable
impls at the interesting bits in the compiler lifecycle. The basic socket solution from the ruby script can now be done in Groovy via callbacks instead of listening on a popen
and the world is once again at peace.
5 writebacks [/src/java/aop] permanent link
Sun, 11 Apr 2004
IoC Containers and AspectJ -- LazyWeb
Speaking of AOP -- a problem we have not found a good solution for is using an IoC container with AspectJ. You have no control over how aspects are instantiated at runtime, so you cannot exactly obtain them from a container.
The best we have come up with so far is to provide a static service lookup component that can provide the container to the aspect via a lookup. Yuck, completely breaks the point of the container. Static lookup to obtain a container and then use it to lookup components... far from ideal.
In cases where we've used proxy based AOP the container isn't a problem, but I find myself, and others at the company I work for, leaning more and more towards AspectJ from proxy based options.
Anyone have any ideas?
6 writebacks [/src/java/aop] permanent link
Fri, 09 Apr 2004
How I'm Using AOP (in Real Apps) Right Now
I've seen a lot of people lately who think AOP is a joke because all they see is the typical logging and tracing examples. I personally (professionally) use it, so figured I would mention some of the ways. These are not in toy apps, but in real apps. In all cases these are done via DynAOP or AspectJ depending on various other constraints at the moment. Aspectwerks is high on my list to try out, just haven't yet. I think my experiences with JRockit are holding me back there.
Persistence and Transactions:
Declarative transactions outside of EJB containers are an obvious one up there with tracing and logging. In addition to that, however, a number of other sophisticated persistence systems can be arranged with a good O/R mapping tool and aspects. One of the more useful has been that advice can be applied to constructors to makePersistent
new persistent objects created via new
magic are also transparently (and transactionally) stored. Declaring a pointcut to match on the o/r mapping metadata I haven't quite worked out yet -- so right now the pointcut def'n needs to be watched as persistent classes are added/factored. This is annoying, but not insufferable. It could be solved via a tag interface, or namespace/naming requirements but that starts to break transparence. I'm using namespace requirement at the moment, but I don't like it much.
Rule-Based Access Policies
Permissions to do CRUD operations on these persistent objects can be managed by a rules system. We have a really big application with a lot of users with incredibly different permissions profiles. We have talked half seriously about having to go to a certified multilevel secure system as we have all but implemented it as such. The tricksy part is being able to conveniently apply access policies across everything. AOP solves this much more easily than JAAS or any home-rolled system I have seen. The collection-projections we can do are the nicest part, Java doesn't provide the ability to actually remove properties/methods so we have to throw exceptions or just noop out things instead. It hurts to think about going back to per-AOP access policy declaration and enforcement.
Transparent Proxying
Sometimes the best solution is a remote object. Not very often, but sometimes. Forget EJB's as interface to RMI, pita. Axis and AOP is much nicer. Side benefit -- can access the same remote object as easily from Ruby (would say Groovy except that I'm not actually doing that in a real app -- and it is a boring example as I would just use Axis again.) Aside from remote, non-transactional objects can act as proxies for transactional objects (as long as the non-transactional one has enough info to build an identity). I've used this, but not yet deployed anything using it. I suspect I will in the future as it makes a large number of clienty things more convenient -- and a convenient client API is important.
10 writebacks [/src/java/aop] permanent link
Fri, 26 Mar 2004
Here is a fun one to add to your build come April 1... Take AspectJ, define an aspect which applies advice on every method call where an exception is thrown. Catch the exception, and throw a random error =) We all love when we execute a JDBC call and an EJBException
is thrown, or even better a ThreadDeath
in place of an NPE -- and best of all -- make the determination of which one to throw random, so the same code throws a different error every time ;-) To pull it off you need to sneak it into CVS and hide the aspect weaving in the build somewhere.
If I have spare time between now and then I'll toss up a library to do it for everyone ;-) Not much chance of that though =(
1 writebacks [/src/java/aop] permanent link
Tue, 02 Mar 2004James suggested I blog on the core difference between DynAOP and AspectWerkz after I quipped in his blog, and I am a sucker for a request, so here goes.
First some definitions, these won't be precise, but they should convey the ideas as they are in use in java open source AOP at the moment: Advice represents code that should modify the behavior or properties of existing code. An interceptor or mixin would be a common example of advice. Pointcuts specify points in code. A typical pointcut might be "any invocation of a method named 'execute' on a class implementing the Action interface." Weaving is a terrible name for the process of combining Poiuntcuts and Advice. Advice is applied to a Pointcut forming an aspectified thingie =)
Aspectwerks and AspectJ do this by bytecode manipulation. They typically modify the bytecode (or sourcecode) of java classes directly so that advice (or hooks for advice) is woven in directly. They may do this as a compile step, or may do this at classload time, the difference is moot. The aspecty stuff is mixed directly into the bytecode.
DynAOP, Nanning, and Rickard's Mysterious Project (reportedly -- no one has actually seen it yet ;-) use a different approach where all aspectified classes are proxied (via a java.reflect.DynamicProxy
or CGLIB proxy) and the weaving is done at the proxy, not on the base instance -- the bytecode is unchanged. The drawback is that you have to specifically wrap an instance and talk to the proxy rather than being able to work with whatever is returned by the new
magic.
2 writebacks [/src/java/aop] permanent link
Fri, 27 Feb 2004
Real Dynamic Pointcuts in DynAOP
So I sat down to do real dynamic pointcuts in dynaop, as compared to the hacked interceptor control flow stuff before, and... it is dirt easy. Bob's design shines once again =)
All it entailed was extending Aspects
to add support for a Aspects#dynamicInterceptor
call which wraps the test in an interceptor:
import dynaop.*;
/**
* Aspects implementation which supports dynamic interception
*/
public class DynamicAspects extends Aspects {
public void dynamicInterceptor(ClassPointcut cp,
MethodPointcut mp,
final InvocationPointcut ip,
final Interceptor inter)
{
this.interceptor(cp, mp, new Interceptor() {
public Object intercept(Invocation invocation)
throws Throwable {
if (ip.pick(invocation)) {
return inter.intercept(invocation);
}
else {
return invocation.proceed();
}
}
});
}
}
and a InvocationPointcut
interface which tests invocations:
import dynaop.Invocation;
public interface InvocationPointcut
{
public boolean pick(Invocation invoke);
}
This lets you intercept based on invocation time properties. The unit test I wrote basically just skips the interceptor where the first arg is "foo"
DynamicAspects dynamic_aspects = new DynamicAspects();
final boolean[] called = { false };
Interceptor interp = new Interceptor() {
public Object intercept(Invocation invocation)
throws Throwable {
called[0] = true;
return invocation.proceed();
}
};
DynamicPointcut dynamic_pointcut = new InvocationPointcut() {
public boolean pick(Invocation invoke) {
Object[] args = invoke.getArguments();
// Don't select if first argument is "foo"
if ("foo".equals(args[0])) return false;
return true;
}
};
dynamic_aspects.dynamicInterceptor(Pointcuts.ALL_CLASSES,
Pointcuts.ALL_METHODS,
dynamic_pointcut,
interp);
ProxyFactory factory = new ProxyFactory(dynamic_aspects);
Map map = (Map) factory.wrap(new HashMap());
map.put("1", "2");
assertTrue(called[0]);
called[0] = false;
map.put("foo", "bar");
assertFalse(called[0]);
Meanwhile, maniax has been doing some really cool stuff with groovy and dynaop.
1 writebacks [/src/java/aop] permanent link
Sun, 22 Feb 2004Earlier this week I was discussing AOP tools with Guillaume in irc and I said that you couldn't do cflow with dynaop or nanning -- that you needed AspectJ or AspektWerks to do that. This didn't really seem true -- and I was right that i was wrong. It took all of fifteen minutes to do!
Cflow is basically a pointcut based on the state of the stack -- is this point in execution upstack from that point, etc. For the proof of concept to myself I didn't provide cflow as a pointcut (as it probably should be) but rather as a combination of an interceptor and mixin. It works as demonstrated by a simple unit test::
import dynaop.*;
import junit.framework.TestCase;
import java.util.Map;
import java.util.HashMap;
public class CFlowTest extends TestCase {
public void testCFlow() throws Exception {
Aspects aspects = new Aspects();
aspects.interceptor(Pointcuts.instancesOf(Map.class),
Pointcuts.membersOf(Map.class),
new CFlowInterceptor());
aspects.mixin(Pointcuts.ALL_CLASSES, CFlowMixin.class, null);
final boolean[] called = { false };
aspects.interceptor(Pointcuts.instancesOf(Map.class),
Pointcuts.membersOf(Map.class),
new Interceptor() {
public Object intercept(final Invocation invocation)
throws Throwable {
called[0] = true;
assertTrue(invocation.getProxy() instanceof CFlowAware);
assertNotNull(((CFlowAware)invocation.getProxy()).getCFlow());
return invocation.proceed();
}
});
ProxyFactory fact = ProxyFactory.getInstance(aspects);
Map proxy = (Map) fact.wrap(new HashMap());
proxy.put("a", "b");
assertTrue(called[0]);
}
}
The silly part is how easy it was to do. This implementation won't be very fast, but it does work:
public interface CFlowAware
{
void setCFlow(StackTraceElement[] stack);
StackTraceElement[] getCFlow();
}
------------------------------------------------------
public class CFlowMixin implements CFlowAware {
private StackTraceElement[] stack;
public void setCFlow(StackTraceElement[] stack) {
this.stack = stack;
}
public StackTraceElement[] getCFlow() {
return this.stack;
}
}
------------------------------------------------------
import dynaop.Interceptor;
import dynaop.Invocation;
import dynaop.Proxy;
public class CFlowInterceptor implements Interceptor {
public Object intercept(Invocation invocation)
throws Throwable {
Exception e = new Exception();
StackTraceElement[] stack = e.getStackTrace();
e = null;
Proxy p = invocation.getProxy();
((CFlowAware)p).setCFlow(stack);
return invocation.proceed();
}
}
The CFlowInterceptor uses a hack to generate the stack trace -- it generates an exception and never throws it. There has to be a better way to do this, but I don't know it. It then passes the stack trace from the exception to the CFlowMixin which makes it availble downstream on the interception stack via the proxy =)
1 writebacks [/src/java/aop] permanent link
Wed, 18 Feb 2004Bob changed the license on dynaop to ASL 2.0! Wow. That is the death nell for Yail, but in a good way, as Yail is now just a slightly less round wheel =)
It's probably safe to consider Yail deprecated unless I get a lot of pushback -- I am willing to keep working on it, but right now not much is happening. Chris has talked about adding the baking in but I don't know its state. If anyone has built significantly on top of it let me know as I don't want to leave anyone hanging in the wind.
Argh, it's tough to let a project go you've worked hard on (if for only a short while), but its silly to continue something for which you have found a better solution and may just be ego-padding by continuing =)
6 writebacks [/src/java/aop] permanent link
Sat, 14 Feb 2004I have been playing with dynaop by Bob Lee (crazybob) for a bit now and am quite impressed. It pretty much does what my goals with intercepts were, plus a bit more (mixin support), and with a friendlier client API. Bob knows how to write a clean client API, that is for sure, and the BeanShell configuration is brilliant (though I have been using the Java config API instead -- which is also quite clean!).
Its interesting to see how he gets around this
in mixins. DynAOP's solution is to use a type 1 style ioc container setup where the proxy can be provided to the mixin. It certainly works fine (is pragmatic ;-) but this
support allows for more idiomatic Java. The way I did it in YAIL was to intercept all internal calls against anything not explicity declared in the mixin, or classes it extends, and return the call against the proxy from the interceptor, instead of against the mixin. This worked fairly nicely, and allowed for partial-class type things (implement an interface and only implement parts while trusting that the rest of the mixed in stuff will fill out the rest).
The proxy system is great (Bob certainly gets clean design) -- the cglib proxy for classes acts just like the interface proxy version, so the majority of the tool doesn't care if it is an interface proxy of cglib proxy.
It is just a hair slower than unbaked Yail interception in my tests, which is pretty impressive as Yail blows the other AOP's away even without baking in yet. I have figured some ways to speed up the unbaked Yail interception but haven't yet implemented them -- and to be honest, am considering not bothering to now! DynAOP gives me everything I was looking for when I started writing YAIL. It has some drawbacks though, which make me sit on the fence about dropping YAIL. First, it is built against CGLIB 1.0 and doesn't run against CGLIB 2.0 (which is due for release any-day-now). This is a significant drawback, but can probably be overcome with a couple patches. Second is the LGPL license, which can be inconvenient in infrastructure type tools in Java (which this definately is). Finally, there is no mechanism for proxying specific instances of a class instead of interface, but I don't think it would be difficult to add in (particularly if the switch to CGLIB 2 is being made).
All said -- the license thing may knock it out as my preferred tool as I do too much with BSD and Apache licensed projects, and I really want a good interceptor tool against them. Nanning is quite nice, but is pure Java DynamicProxy based with no support for class extension directly. From a technical standpoint, I would probably switch right over as modifying it for cglib2 and adding proxies to existing instances shouldn't be hard if the internals are as clean as the client API's!