On the flight out to CodeMash I started working on some refinements to how Tapestry does runtime class transformation. My long term goal is to move away from Javassist and towards something a bit simpler ... like ASM. Why? Javassist does not have a good, responsive, supportive community, and it has been increasingly flaky since JDK 1.6.
Previously, I've blogged about how invaluable Javassist is, and I stand behind that early pronouncement. Tapestry IoC and Tapestry Core both use Javassist extensively to create new classes at runtime, as well as modify classes as they are loaded into memory. However, I've also been finding new ways to apply meta-programming without exposing all the gory details of Javassist. As usual, simplicity follows complexity: I'm finding ways to simplify work I've done the hard way, previously, to make these techniques easier for others to leverage.
That's the pattern I'm trying for: none of the explicit Java psuedo-code used by Javassist, instead, defining ways to add behavior to methods, or individual fields, in terms of simple callback interfaces. For the moment, the under-the-covers wiring is still Javassist (underneath the Tapestry ComponentClassTransformation interface), but eventually all the parts that are truly tied to Javassist (i.e., those parts of the API where a Javassist pseudo-code string are provided) can be phased out, deprecated, and eliminated.
The advantage of this revised approach is that the amount of runtime-generated code decreases and simplifies. Less behavior is created via Javassist pseudo-code, and fewer fields need to be created or injected into the component class. Further, more runtime code will be in standard objects, compiled by the standard Java compiler, and less code will be compiled by Javassist. Intuitively speaking (always dangerous), it makes sense that standard Java code will be optimized better by Hotspot: Reportedly, some aspects of Hotspot are tied to the exact form of bytecode produced by the Sun Java compiler).
I've heard from some specific Tapestry users who are building and deploying very large, very complicated applications, that live class reloading is problematic for them to use: their pages consist of hundreds (possibly thousands) of deeply nested components, and they are seeing 30+ second delays reloading a page after a change. Whenever a component class changes, Tapestry must discard the old ClassLoader, and create a new one, and lazily re-instrument all the component classes; this isn't a big deal with only dozens of pages and components, but I want Tapestry to be effective even for the largest, most complicated web applications. Simplifying and revising Tapestry's approach to bytecode enhancement is just the latest in a series of internal changes targeting improved performance.
Meanwhile. the CodeMash conference goes on around me ... and shortly, back to the waterpark.
Great to hear there is progress on the scalability of the platform!
ReplyDeleteThis is more about stability; Javassist is not fully stable for JDK 1.6, users see rare errors caused by invalid bytecode generation. Further, some of the intracacies, even with several layers of wrappers, are difficult.
ReplyDeleteMy goal is to make instrumenting a component class as easy as adding advice to a service; all done with ordinary methods and callback objects, with the bytecode part happening automatically beneath the covers.
I am one of those users that see 'rare errors' while using javassist with jdk 1.6 and being a greenhorn in java and javassist, I'm at a loss. Hearing that you are phasing out javassist sounds even more ominous.
ReplyDeleteThe reasons you specify are indeed true. There are just a few active members in the forum, I don't know what Mr.chiba is upto and I don't know why the activity has come down.
In terms of a javassist replacement, why choose ASM over others like BCEL - performance/support/other? I don't know about ASM and the reason why I am using javassist instead of BCEL is that I don't have to muck around with bytecode. Javassist is however more restrictive than BCEL in terms of capability and there are these exceptions when using it with java 6.