There is a well-defined naming convention in Java to start annotation names with an uppercase letter – same as for class/interface names. Though this naming convention is widely accepted, I think it’s a bit dogmatic. If you take a look at the list of Java-specific annotations in Scala you may see that both lowercase (like @cloneable
or @throws
) and uppercase (like @SerialVersionUID
or @BeanProperty
) forms are used. Personally, I tend to agree with decisions made by architects of the Scala’s standard library: some annotations are meta-data, for example javax.persistence.@Entity
/ javax.persistence.@Column
or javax.xml.bind.annotation.@XmlElement
/ javax.xml.bind.annotation.@XmlAttribute
; other ones look like a directive to processing tool or like a syntax extension. In addition, directive-like annotations typically have no extra parameters, so they are very similar to @-prefixed keywords. Hence, I think it would be more natural to write @override
rather than @Override
in Java — it’s a directive to compiler to check whether or not the method overrides/implements a method defined in a superclass/interface. Moreover, in some languages “override” is indeed a keyword, so it’s even more tempting to use all-lower-case variant.
The above should explain why lower-case annotation names where chosen for two annotations in Tascalate Javaflow: @continuable
and @ccs
. Both are directives to bytecode instrumentation tools, both have no extra parameters, and both looks like library-defined keywords:
public @continuable void execute() { ... final @ccs Runnable contRunnable = new MyContinuableRunnable(someArg); ... }
What if you don’t agree with arguments above and would prefer to follow standard Java naming conventions for annotations? Not a problem at all with Tascalate Javaflow! There is a third annotation defined in the library – org.apache.commons.javaflow.api.ContinuableAnnotation – that allows you to define your own annotations instead of @continuable
and @ccs
. In fact, ContinuableAnnotation is a meta annotation and it may/should be applied only to other annotations. Here is how to define @ContinuableMethod
annotation that you may use instead of @continuable
in your code to strictly adhere to Java naming conventions:
package mycompany.myapp.annotations; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.apache.commons.javaflow.api.ContinuableAnnotation; @Documented // optional @Retention(RetentionPolicy.CLASS) // mandatory, may be RetentionPolicy.RUNTIME as well @Target({ElementType.METHOD}) // mandatory, only methods are examined @ContinuableAnnotation // mandatory public @interface ContinuableMethod { }
The rules are:
- Your custom
@ContinuableMethod
annotation must be annotated with@ContinuableAnnotation
(obviously) - It must be an annotation applicable to methods – any other targets have no effect and will only confuse a user of your annotation class. However, sometimes you need to have other targets and it’s ok to define more – typical example is CDI interceptor binding annotations.
- It must have retention policy defined either as
RetentionPolicy.CLASS
or asRetentionPolicy.RUNTIME
, SOURCE-level annotations are not saved into class bytes
Similar, you can re-define @ccs
as @ContinuableTarget
:
package mycompany.myapp.annotations; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.apache.commons.javaflow.api.ContinuableAnnotation; // optional @Documented // mandatory, may be RetentionPolicy.RUNTIME as well @Retention(RetentionPolicy.CLASS) // mandatory, use exact syntax @Target({ElementType.LOCAL_VARIABLE, ElementType.PARAMETER, ElementType.TYPE_USE}) // mandatory @ContinuableAnnotation public @interface ContinuableTarget { }
The rules are similar to the method-level annotation except for @Target
– please use exact syntax as above. In addition to regular variable/parameter targets you must declare ElementType.TYPE_USE
(Java 8 feature) – otherwise annotation is not saved in a class bytecode.
No other customization is necessary: you may use your own annotations right away thanks to meta-annotation. By the way, there is another important use-case when ContinuableAnnotation
may be useful in your code — stereotype annotations. Imaging, that your application has a method-level annotation @WorflowTask
with it’s own duties. Moreover, all @WorkflowTask
methods must be @continuable
. To mark some business methods as workflow tasks you should use both annotations:
package mycompany.myapp.services; import mycompany.myapp.annotations.WorkflowTask; import org.apache.commons.javaflow.api.ContinuableAnnotation; public class MyService { ... @WorkflowTask(timeout="3d",name="SomeTask") @continuable public int myBusinessMethod() { ... } ... }
However, if you mark your @WorkflowTask annotation as @ContinuableAnnotation then you may use just one annotation in your code:
package mycompany.myapp.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.apache.commons.javaflow.api.ContinuableAnnotation; @Retention(RetentionPolicy.CLASS) @Target({ElementType.METHOD}) @ContinuableAnnotation public @interface WorkflowTask{ public String name(); public String timeout(); ... } //===== package mycompany.myapp.services; import mycompany.myapp.annotations.WorkflowTask; public class MyService { ... @WorkflowTask(timeout="3d",name="SomeTask") public int myBusinessMethod() { ... } ... }
Now @WorkflowTask annotation is a stereotype that:
- clearly defines specific business method role
- captures this role at a conceptual level
- encapsulates implementation details