7

Usage example:
I want to put on class fields a custom annotation @MyContainer and then add automatically on all such fields relevant Hibernate annotations (depending on field type and properties).
In additional I need to add JAXB XmlType annotation to the class and base the type name on the class name.
I would want additionally to add annotations to fields based on thier types, etc. All added annotations should be available at run time (So hibernate / JAXB can find them).
I'm aware of the following options:

  1. Pre-processing class source (bad option)
  2. Processing during compilation with javax.annotation.processing APIs
  3. Post compilation manipulation with tools such as Java Assist
  4. Manipulation during class loading with java.lang.instrument APIs
  5. Doing it with AspectJ (not powerful enough)

My primary goals are:

  1. Keep sync between class and source for debugging
  2. Support working from both Maven and IDE (Eclipse / Intellij)

I'll appreciate if people who already done such things can recommend the best approach for such a task (and perhaps potential pitfalls).

Avner Levy
  • 6,601
  • 9
  • 53
  • 92
  • What about not generating annotations, but instead generating hibernate mapping XML files (hbm.xml) that can be loaded by hibernate configuration? – Strelok Jul 22 '12 at 14:08
  • Thanks, it's a good idea but I prefer the annotation option in my case since I need to generate JAXB annotation as well (and maybe others in the future). – Avner Levy Jul 22 '12 at 14:30

5 Answers5

1

Here is a code example for defining custom annotation. This @TesterInfo is applied on class level, store the tester details. This shows the different use of return types – enum, array and string.

package com.mkyong.test.core;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) //on class level
public @interface TesterInfo {

    public enum Priority {
       LOW, MEDIUM, HIGH
    }

    Priority priority() default Priority.MEDIUM;

    String[] tags() default "";

    String createdBy() default "Mkyong";

    String lastModified() default "03/01/2014";

}
Dev707
  • 71
  • 4
0

I think pre-processing class sources should be your preferred way. This enables you to have your sources in sync with the compiled classes, which is good for debugging as you mentioned. But it is also good for version control, since you are able to check in those generated annotations. It is also a lot more difficult to track down problems in your tool, if it is run it during compilation. IDE support should also be no problem when running your code generation in the generate-sources phase.

Edit: Quick searching yielded some infos about programmatic java source modification using the eclipse jdt or some thing in netbeans. But this might be worth some more research or a question of its own.

SpaceTrucker
  • 13,377
  • 6
  • 60
  • 99
  • Thanks for your answer. Can you provide more information on the solution, such as which tool can be used to analyze the source code and manipulate it? The down side is that the source code gets filled with low level annotations and is less clear (I thought of adding the annotation just for runtime). – Avner Levy Jul 23 '12 at 01:00
  • If you are afraid that this might lead to unclean code, you should consider using an interface of your current class and hide the implementation with annotations as much as possible. – SpaceTrucker Jul 23 '12 at 07:07
0

I want to suggest another approach on that. As my first answer might involve coding an own tool, you could also try a much simpler solution. As I hope you are unit testing your classes, you could implement a base class for every unit test of such a class. In this base class there is a test method, which checks that every field annotated with @MyContainer also has the required hibernate annotations.

We basically did the same thing, not for annotations but for serializability of fields and ran quite well with that approach.

Community
  • 1
  • 1
SpaceTrucker
  • 13,377
  • 6
  • 60
  • 99
0

To have it work most transparent in IDE, command line build and at run-time, option 1 (using APT) and option 5 (using AspectJ) will give you best fit.

For option 1 you'll have to implement your own annotation processor that will inject additional annotations based on the presence of your own @MyContainer annotation. Here is an example of this approach used for something similar.

For option 5 you can simply use annotation declaration. Something like this:

declare @field : * ((@*..MyContainer *)).*(..) : @OtherAnnotation();

Spring's Roo tool is extensively using option 5 and I certainly can't say it is not powerful enough.

Eugene Kuleshov
  • 31,461
  • 5
  • 66
  • 67
  • Can you base the annotations and annotations parameters on the class / method / member information? For example I need to examine the container data type and select between few possible hibernate annotations. In additional I need to generate the column names for hibernate annotation based on fields names, etc... – Avner Levy Jul 25 '12 at 06:19
0

There are few alternatives as mentioned above and each have its upsides and downsides. That's why I don't think there a real "right" answer for the above question. My purpose was to get inputs from the community and from people who had done such things in the past and have experience. Personally I've chosen to use the Instrument API with Javassist. This way the classes are extended on run time (although the same tool can be used for post compile processing). The nice thing is that the agent can be loaded from inside the JVM which avoids handling all the command lines. It will be great to hear on other alternatives.
Thanks,
Avner

Avner Levy
  • 6,601
  • 9
  • 53
  • 92