15

I wrote a custom annotation containing metadata for a property and an AnnotationProcessor:

@SupportedAnnotationTypes({"<package>.Property"})
public class PropertyProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations,
            RoundEnvironment roundEnv) {
        // Get messager object
        Messager messager = processingEnv.getMessager();
        // Iterate through the annotations
        for(TypeElement typeElement : annotations) {
            // Iterate through the annotated elements
            for(Element element : roundEnv.getElementsAnnotatedWith(typeElement)) {
                // Get Property annotation
                Property property = element.getAnnotation(Property.class);

            }
        }
        return false;
    }

}

Here is the question, I have used Javassist before but it was depending on the class loader and I think it's not appropriate for OSGi applications. I want to change the generated bytecode when a class with Property annotation is compiled.

palacsint
  • 28,416
  • 10
  • 82
  • 109
Deniz Acay
  • 1,609
  • 1
  • 13
  • 24
  • a question - why is this needed? Can't it be achieved in another way? – Bozho Sep 18 '10 at 21:18
  • without using APT or this API, i will need to annotate both setter and getter methods for each property, but if this works i will have complete control over how the code will be generated. this is not a pure business question, i want to know whether this is possible or not – Deniz Acay Sep 19 '10 at 14:50

3 Answers3

6

Have you tried Google Guice?

Google Guice lets you do a bit of Aspect Oriented Programming by intercepting methods. If that's all you need to do, you can implement a MethodInterceptor that'll let you override methods at runtime. It's really neat for isolating cross-cutting concerns.

For example, lets say you want to prevent a certain methods from being executed on weekends, you can annotate them as so:

@Property
public class SomeClass {
    public Receipt doSometing() {
        // Do something
    }
}

Define a MethodInterceptor:

public class PropertyInterceptor implements MethodInterceptor {
  public Object invoke(MethodInvocation invocation) throws Throwable {
    // For example prevent the classes annotated with @Property
    // from being called on weekends
    Calendar today = new GregorianCalendar();
    if (today.getDisplayName(DAY_OF_WEEK, LONG, ENGLISH).startsWith("S")) {
      throw new IllegalStateException(
          invocation.getMethod().getName() + " not allowed on weekends!");
    }
    return invocation.proceed();
  }
}

And then bind the interceptor to the annotation:

public class PropertyModule extends AbstractModule {
  protected void configure() {
        PropertyInterceptor propertyInterceptor = new PropertyInterceptor();        
        bindInterceptor(Matchers.annotatedWith(Property.class), 
        Matchers.any(), propertyInterceptor);
  }
}
Brad Cupit
  • 6,530
  • 8
  • 55
  • 60
Tristan St-Cyr
  • 143
  • 4
  • 7
5

The short answer is: you're not supposed to change source code during annotation processing.

I have had a situation recently where that answer was not satisfactory (see this question). My solutions was to programmatically add the code I needed using the internal javac api. See my answer to my own question for details.

I took the inspiration to this from Project Lombok, starting out with their source code and throwing away everything I didn't need. I don't think you'll find a much better starting point.

BTW, Javassist probably won't help, because you are dealing with a source tree, not with byte code. If you want to use a byte code manipulation library you can do that either statically after compiling or dynamically when loading the classes, but not during annotation processing, because that's a pre-compile step.

Community
  • 1
  • 1
Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
1

Annotation processing is not meant to alter existing classes - it is just for generating additional code/resources (on a class-by-class basis, otherwise you'll run into trouble when re-compiling the modified sources only).

Some time ago I tried Spoon for a similar problem: I liked the idea of a program processor very much (and the IDE integration even more), but it was not really stable at the time...

Depending on your use case, an AOP tool (eg: AspectJ) could suite you better than Spoon, and - of course - you could always use a source code generator or implement a full-blown DSL (take a look at the fantastic Xtext).

Depending on the size, turnover rate and "intellectual inertia" of your team mates - you could be better off bearing the pains of plain java instead of those of introducing a new tool/technology, forming co-workers and integrating the new tool in your CI system. Weigh costs/benefits carefully.

yegor256
  • 102,010
  • 123
  • 446
  • 597
giorgiga
  • 1,758
  • 12
  • 29