Adding code to a Java class w/ Instrumentation: ASM or BCEL?
Asked Answered
M

2

13

I am writing a game engine/library in which I have an event dispatcher class which dispatches events by calling listener methods of "registered" event handler classes. One can register an event handler/listener with the event dispatcher by calling the appropriate dispatcher method.

This obviously leads to some boilerplate code for registering every event handler(and also other aspects of my engine have similar bolierplate code), so I was wondering - how about just using Instrumentation to add in all of the necessary code during loading of the event handler class, so that no explicit registration with the event dispatcher is necessary while coding - the call to the dispatcher's register method is added in automatically when the program is run.

It is my understanding that in order to use Instrumentation one should use some bytecode modifier API. I know of two - ASM and BCEL. Which one should I use? Obviously, this is a somewhat simple task I am trying to do, so I want the one which is easier to learn and better documented.

EDIT: Here is a specific example.

Original event handler class:

@Handler //indicates this this class should be transformed
public class MouseEventHandler implements EventHandler<MouseEvent>
{
    //hidden default constructor
    public void handleEvent(MouseEvent event)
    { ... }
}

After transformation:

@Handler
public class MouseEventHandler implements EventHandler<MouseEvent>
{
    public MouseEventHandler()
    {
        //add this line of code to default constructor
        Game.getEventDispatcher().addEventHandler(this);
    }
    public void handleEvent(MouseEvent event)
    { ... }
}
Maddiemadding answered 18/4, 2012 at 7:53 Comment(0)
M
26

Java bytecode libraries:

  • ASM is fast and actively developed.
  • BCEL is comparatively slow.
  • Javassist is probably easiest to get started with if you're not familiar with Java bytecode.
  • cglib builds on top of ASM, providing some higher level abstractions.
  • Byte Buddy generates classes via a DSL. Actively maintained and seeing increasing usage.

I would however consider other options before jumping into bytecode manipulation.

Microscopy answered 18/4, 2012 at 8:31 Comment(3)
bcel has been included into JDKBellied
@alexander.box: Yes, as a dependency to the XSLTC transformer Xalan, but it is non-standard API and also hidden in the package com.sun.org.apache.bcel.internal! Better provide your own jar.Contortion
Oh, and while I am at it: ASM is a very good choice when classes have to be instrumented. Easy to understand and to use. Also it has a very nice documentation.Contortion
O
6

Adding the logic to a few classes might be boring, but unless you've thoushands of handlers, that's the way I would go. Keep it simple.

That said,

Game.registerHandler( this );

would be more object-oriented.

An alternative to adding the logic in each class is to introduce a factory that is responsible to instantiate the handlers.

HandlerFactory.createMouseHandler();

And method createMouseHandler contains something like

Handler mh = new MousheHandler();
registerHandler(mh);
return mh;

If you don't want either of these options, I would consider either an aspect framework (maybe AspectJ) or a container for Invertion of Control (maybe Spring IoC). Aspects allow you to annotate your source, and "weave" code at the selected places. An IoC container allows you to control the lifecycle of object (e.g. instantiation). Both use bytecode instrumentation behind the scene.

But if you want to do the instrumentation yourself, I can only compare Javassist and ASM that I used personally.

ASM is low-level, and operates really at the level of java bytecode. You must be familiar with it. The framework is very well designed, the manual is excellent, and it is a great library. One one side it can be complicate to replace patterns of bytecode, because it requires a so-called "stateful" transformation. One the other side, you have full control over the bytecode.

Javassist is more high-level. You do not operate at the raw level of bytecode, a slight higher level, e.g. fields read/write, message send, constructors. Also, it allows you to specify changes using regular java syntax, that is then compiled by the framework. The API is a bit confused, because the project grew over the years. There is documentation about the framework, but not so well centralized as with ASM.

Osseous answered 18/4, 2012 at 9:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.