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
@ContinuableMethodannotation 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.CLASSor 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