Fun with Java Fun with Java AnnotationsAnnotations
Brian McGinnisBrian McGinnis
Java AnnotationsJava Annotations
Introduced in “Tiger” release (Java Introduced in “Tiger” release (Java 1.5)1.5)
One of most interesting Java One of most interesting Java extension in a long timeextension in a long time
Ability to declare metadata modifiers Ability to declare metadata modifiers for Java language upon:for Java language upon: Type declarations, Constructors, Type declarations, Constructors,
methods, Members, Parameters, methods, Members, Parameters, VariablesVariables
Standard Java 1.5 Standard Java 1.5 AnnotationsAnnotations
Tiger has some useful built-in annotationsTiger has some useful built-in annotations Java.lang.OverrideJava.lang.Override
Compiler enforces overriding superclass methodsCompiler enforces overriding superclass methods Java.lang.DeprecatedJava.lang.Deprecated
Warns when @deprecated method, class or member is Warns when @deprecated method, class or member is usedused
Must use javac “-deprecation” argument to javac or the Must use javac “-deprecation” argument to javac or the new -Xlint:deprecated flag to see compiler warningsnew -Xlint:deprecated flag to see compiler warnings
Java.lang.SupressWarningsJava.lang.SupressWarnings Useful for Tiger’s many compiler warnings (especially Useful for Tiger’s many compiler warnings (especially
with legacy collection code)with legacy collection code) KeyKey: Look for a string enclosed in “[ ]” in compiler : Look for a string enclosed in “[ ]” in compiler
warnings from javac (may need to use the command warnings from javac (may need to use the command line). Use this string to use for the @SuppressWarning line). Use this string to use for the @SuppressWarning argument.argument.
This may work on your compiler yet!This may work on your compiler yet!
@Override Example@Override Example
@Override Example Code:@Override Example Code:
class Base {class Base { public void yes(int i) { }public void yes(int i) { }}}
class Subclass extends Base {class Subclass extends Base { @Override@Override public void yes(int i) { }public void yes(int i) { } @Override@Override public void no(float x) { } // Compiler error generated here!public void no(float x) { } // Compiler error generated here!}}
@Deprecated Example@Deprecated ExampleExample usage:Example usage:
@Deprecated@Deprecatedpublic class DeprecatedExample {public class DeprecatedExample { private int x;private int x;
@Deprecated@Deprecated public int value;public int value;
@Deprecated@Deprecated public void setValue(int aValue) { value = aValue; }public void setValue(int aValue) { value = aValue; }}}
public class DeprecatedExampleUse { public class DeprecatedExampleUse { public static void main(String argv[])public static void main(String argv[]) {{ DeprecatedExample obj = new DeprecatedExample(); // Compiler WARNINGs DeprecatedExample obj = new DeprecatedExample(); // Compiler WARNINGs
generated heregenerated here obj.value = 10; // Compiler WARNING generated hereobj.value = 10; // Compiler WARNING generated here }}}}
@SuppressWarning @SuppressWarning ExampleExample
public class SuppressExample {public class SuppressExample { List wordList = new ArrayList(); // no typing information on the ListList wordList = new ArrayList(); // no typing information on the List
private voidprivate void generateWarning()generateWarning() {{ wordList.add("foo"); // Warning generated here.wordList.add("foo"); // Warning generated here. }}}}javac -Xlint:unchecked SuppressExample.javajavac -Xlint:unchecked SuppressExample.javaSuppressExample.java:13: warning: [unchecked] unchecked call to add(E) as a member SuppressExample.java:13: warning: [unchecked] unchecked call to add(E) as a member
of the raw type java.util.Listof the raw type java.util.List wordList.add("foo"); // Warning generated here.wordList.add("foo"); // Warning generated here. ^̂1 warning1 warning
Get rid of warning by adding this line above the method Get rid of warning by adding this line above the method delcaration:delcaration:
@SuppressWarning(“unchecked”)@SuppressWarning(“unchecked”)
Setting Annotation Setting Annotation ValuesValues
Setting Annotation ValuesSetting Annotation Values Set arguments as key/value pairsSet arguments as key/value pairs Can optionally be an array of key value pairsCan optionally be an array of key value pairs
““Marker” annotationsMarker” annotations No argumentsNo arguments Example: @DeprecatedExample: @Deprecated
Single value annotationsSingle value annotations no key is required, just a valueno key is required, just a value Example: @SuppressExample(“unchecked”)Example: @SuppressExample(“unchecked”)
Multiple value annotationsMultiple value annotations separate key/value pairs with commasseparate key/value pairs with commas Example: @Review(student=“Jonny Law”, Example: @Review(student=“Jonny Law”,
grade=Grades.Pass)grade=Grades.Pass)
Java AnnotationsJava Annotations
Metadata from annotations is Metadata from annotations is available at compile-time and at available at compile-time and at runtimeruntime
What for?What for? compile-time uses: Tools like a code compile-time uses: Tools like a code
generator or code validator (e.g. generator or code validator (e.g. java.lang.Override)java.lang.Override)
runtime uses: Any framework or runtime uses: Any framework or application code that can make use of application code that can make use of metadata (e.g. J2EE)metadata (e.g. J2EE)
Implementing Implementing and using custom and using custom
annotationsannotations
A Sample Annotation A Sample Annotation ApplicationApplication
A simple example to demo custom annotationsA simple example to demo custom annotations The demo application is: We want to readily The demo application is: We want to readily
identify design patterns used in our production identify design patterns used in our production codecode
Users put in annotation “@Pattern” to identify Users put in annotation “@Pattern” to identify design patterns in their codedesign patterns in their code
Our @Pattern annotation takes 3 arguments:Our @Pattern annotation takes 3 arguments: PatternTypePatternType = an enum for which design pattern (e.g. = an enum for which design pattern (e.g.
singleton, builder, etc.)singleton, builder, etc.) RoleRole = a string to denote the role played in more = a string to denote the role played in more
complex patterns (e.g. for an MVC pattern, complex patterns (e.g. for an MVC pattern, role=“controller”)role=“controller”)
MsgMsg = a string to further explain pattern usage = a string to further explain pattern usage
Sample usage #1Sample usage #1package samples.myapp;package samples.myapp;import custom.annotation.Pattern;import custom.annotation.Pattern;import custom.annotation.PatternType;import custom.annotation.PatternType;
/** An example singleton that uses our @Pattern annotation. *//** An example singleton that uses our @Pattern annotation. */@Pattern(type=PatternType.SINGLETON, msg=“Singleton wrapper @Pattern(type=PatternType.SINGLETON, msg=“Singleton wrapper
class”)class”)public class SampleOne {public class SampleOne { private static SampleOne instance = null;private static SampleOne instance = null; private SampleOne() { }private SampleOne() { } public static SampleOne public static SampleOne get() get() { { if (instance == null)if (instance == null) instance = new SampleOne();instance = new SampleOne(); return(instance); return(instance); }}}}
Sample Usage #2Sample Usage #2package samples.myapp;package samples.myapp;import custom.annotation.Pattern;import custom.annotation.Pattern;import custom.annotation.PatternType;import custom.annotation.PatternType;
/** An example class that uses our @Pattern annotation. *//** An example class that uses our @Pattern annotation. */public class SampleTwo {public class SampleTwo { @Pattern(type=PatternType.FACTORY , role="static factory @Pattern(type=PatternType.FACTORY , role="static factory
method")method") public static SampleTwopublic static SampleTwo create(int aValue)create(int aValue) {{ SampleTwo instance = new SampleTwo(aValue);SampleTwo instance = new SampleTwo(aValue); return(instance);return(instance); }} private int i; private int i; protected SampleTwo(int a) { i = a; }protected SampleTwo(int a) { i = a; } protected SampleTwo() { }protected SampleTwo() { }}}
Implementing Implementing AnnotationsAnnotations
4 standard 4 standard meta-annotationsmeta-annotations you need to you need to know about:know about: @Target – specifies which Java elements can use @Target – specifies which Java elements can use
the annotation (e.g. Type, method, constructor, the annotation (e.g. Type, method, constructor, etc.)etc.)
@Retention – specifies if annotation info is @Retention – specifies if annotation info is available at compile-time or at runtimeavailable at compile-time or at runtime
@Documented – Indicates if annotation should be @Documented – Indicates if annotation should be part of public api. Annotation use shows up in part of public api. Annotation use shows up in javadoc.javadoc.
@Inherited – Specify inheritance handling of the @Inherited – Specify inheritance handling of the annotationannotation
Implementing Implementing annotationsannotations
1.1. Declare meta-annotations for you Declare meta-annotations for you custom annotation in the java filecustom annotation in the java file
Example: @Target, @Documented, Example: @Target, @Documented, etc.etc.
2.2. Declare an @interface for your Declare an @interface for your custom annotation in the java filecustom annotation in the java file
Similar to a regular Java ‘interface’Similar to a regular Java ‘interface’ Define “properties” of the annotation Define “properties” of the annotation
as methodsas methods
Annotation Annotation ImplementationImplementation
package custom.annotation;package custom.annotation;Import ….Import ….
// Specify our meta-annoations here.// Specify our meta-annoations here.@Target({ElementType.TYPE, ElementType.METHOD, @Target({ElementType.TYPE, ElementType.METHOD,
ElementType.FIELD })ElementType.FIELD })@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME) @Documented // Tell javadoc to include usage of our custom annotations@Documented // Tell javadoc to include usage of our custom annotations
public @interface Pattern {public @interface Pattern { // The design pattern enum value for the annotation instance.// The design pattern enum value for the annotation instance. PatternType type(); PatternType type(); // The "role" played in the design pattern. (value is optional)// The "role" played in the design pattern. (value is optional) String role() default "“;String role() default "“;
// Some text to be displayed played in the design pattern. (optional)// Some text to be displayed played in the design pattern. (optional) String msg() default "";String msg() default "";}}
Annotation Annotation ImplementationImplementation
package custom.annotation;package custom.annotation;
/** Declare the design patterns known by our custom @Pattern /** Declare the design patterns known by our custom @Pattern annotation. */ annotation. */
public enum PatternType {public enum PatternType { /** A class which wraps a singleton object instance... blah. *//** A class which wraps a singleton object instance... blah. */ SINGLETON_CLASS,SINGLETON_CLASS,
/** A method used to constructs an object instance... blah. *//** A method used to constructs an object instance... blah. */ FACTORY_METHOD, FACTORY_METHOD,
/** A class which is used to ... blah. *//** A class which is used to ... blah. */ BUILDER;BUILDER; public static String public static String getHtmlLink(PatternType pattern, String basePath) { … }getHtmlLink(PatternType pattern, String basePath) { … }}}
Tools to use our custom Tools to use our custom typetype
At runtime, you can get annotation At runtime, you can get annotation values from java classesvalues from java classes If you defined the annotation using If you defined the annotation using
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME) Use java reflection to pickup annotations Use java reflection to pickup annotations
from methods, members, etc.from methods, members, etc. Demo a sample app which prints out all Demo a sample app which prints out all
design @Pattern we used in our design @Pattern we used in our application codeapplication code
Pattern Tool Source Pattern Tool Source CodeCodepublic class PatternTool { public class PatternTool {
/** Processes the @Pattern annotations for a java class. The meat of our tool. *//** Processes the @Pattern annotations for a java class. The meat of our tool. */ private void processPatternAnnotations(Class javaClass) throws private void processPatternAnnotations(Class javaClass) throws
IOExceptionIOException { { // Get the design @Pattern declared upon class declaration// Get the design @Pattern declared upon class declaration if (javaClass.if (javaClass.isAnnotationPresentisAnnotationPresent(Pattern.class)) {(Pattern.class)) { Pattern classLevelPattern = Pattern classLevelPattern =
(Pattern)(Pattern)javaClass.getAnnotationjavaClass.getAnnotation((Pattern.classPattern.class);); printClassPattern(classLevelPattern, javaClass.getName());printClassPattern(classLevelPattern, javaClass.getName()); }} // Get all design @Pattern declared upon methods via reflection// Get all design @Pattern declared upon methods via reflection Method[] methods = javaClass.getDeclaredMethods();Method[] methods = javaClass.getDeclaredMethods(); for (Method method : methods) {for (Method method : methods) {
method.setAccessible(true); // make private methods accessiblemethod.setAccessible(true); // make private methods accessible Pattern methodPattern = Pattern methodPattern = method.getAnnotationmethod.getAnnotation((Pattern.classPattern.class);); if (methodPattern != null) { if (methodPattern != null) { printMethodPattern(methodPattern, javaClass.getName(), printMethodPattern(methodPattern, javaClass.getName(),
method.getName());method.getName()); }} }} ……code removed for clarity…code removed for clarity…}}
Sample HTML OutputSample HTML Output
Design Pattern Usage Design Pattern Usage
Java Class: Java Class: samples.myapp.SampleOnesamples.myapp.SampleOne
Class level design pattern implemented: Class level design pattern implemented: Singleton Class Design PatternSingleton Class Design Pattern , Msg: , Msg: ‘Singleton Wrapper Class’‘Singleton Wrapper Class’
Java Class: Java Class: samples.myapp.SampleTwosamples.myapp.SampleTwo
Method Method create()create() implements design pattern: implements design pattern: Factory Method PatternFactory Method Pattern, Role: 'static , Role: 'static factory method' factory method'
Some Ideas on Some Ideas on how to apply how to apply annotationsannotationsThings to make you think Things to make you think
about how annotations can be about how annotations can be usedused
Replace older technologyReplace older technology Replace javadoc-oriented precompilersReplace javadoc-oriented precompilers
Provide runtime access to metadata - not just compile-Provide runtime access to metadata - not just compile-time.time.
Metadata syntax checked by java compilerMetadata syntax checked by java compiler Replace XML-oriented java toolsReplace XML-oriented java tools
Put metadata in your java files were it is easy to Put metadata in your java files were it is easy to maintain - not in separate XML filesmaintain - not in separate XML files
Do away with verbose, error-prone XML typingDo away with verbose, error-prone XML typing Typically easier to find and fix errors than fiddling with Typically easier to find and fix errors than fiddling with
the XML parsing + Java errorsthe XML parsing + Java errors Provide built-in type checking for metadata via java Provide built-in type checking for metadata via java
compilercompiler Rapidly replacing XML usage in J2EE in many Rapidly replacing XML usage in J2EE in many
areasareas
Application Idea: Binding a Application Idea: Binding a pojo to user interface formspojo to user interface forms@HtmlForm(posturl=“/congo/servlet/saveperson”, @HtmlForm(posturl=“/congo/servlet/saveperson”,
resetbutton=false)resetbutton=false)Public class PersonInfo {Public class PersonInfo {
@field(name=“pname”, type=FormField.TEXT, size=25, @field(name=“pname”, type=FormField.TEXT, size=25, isReqd=true)isReqd=true)@format(alignment=Align.LEFT, printf=“%s”)@format(alignment=Align.LEFT, printf=“%s”)public Sting name;public Sting name;
@field(name=“pstate”, type=FormField.RADIO, isReqd=true)@field(name=“pstate”, type=FormField.RADIO, isReqd=true)public State state;public State state;
@field(name=“page”, type=FormField.TEXT, isReq=false)@field(name=“page”, type=FormField.TEXT, isReq=false)@format(alignment=Align.RIGHT, printf=“%d”)@format(alignment=Align.RIGHT, printf=“%d”)public Integer age;public Integer age;……
}}
Application ideas: User Application ideas: User Input validationInput validation
@Validator(class=com.fubar.PersValid)@Validator(class=com.fubar.PersValid)public class PersonPojo {public class PersonPojo {
@range(min=1, max=115)@range(min=1, max=115)public int age;public int age;
@range(min=0, max=20)@range(min=0, max=20)@default(value=new Integer(0))@default(value=new Integer(0))@notnull(true)@notnull(true)public Integer numberOfDependents;public Integer numberOfDependents;
……}}
Application: EJB Application: EJB InterceptorsInterceptors
@Stateless@Stateless@Interceptors({com.acme.AccountAudit.class, com.acme.Metrics.class, com.acme.CustomSecurity.class})@Interceptors({com.acme.AccountAudit.class, com.acme.Metrics.class, com.acme.CustomSecurity.class})public class AccountManagementBean implements AccountManagement {public class AccountManagementBean implements AccountManagement {
public void createAccount(int accountNumber, AccountDetails details) { ... }public void createAccount(int accountNumber, AccountDetails details) { ... }......}}
public class Metrics {public class Metrics {@AroundInvoke@AroundInvoke public Object profile(InvocationContext inv) throws Exception { public Object profile(InvocationContext inv) throws Exception {
long time = System.currentTimeMillis();long time = System.currentTimeMillis();try { return inv.proceed(); } finally {try { return inv.proceed(); } finally {
long endTime = time - System.currentTimeMillis();long endTime = time - System.currentTimeMillis();System.out.println(inv.getMethod() + " took " + endTime + “ milliseconds.");System.out.println(inv.getMethod() + " took " + endTime + “ milliseconds.");
}}}}
}}
public class AccountAudit {public class AccountAudit {@AroundInvoke@AroundInvoke public Object auditAccountOperation(InvocationContext inv) throws Exception { public Object auditAccountOperation(InvocationContext inv) throws Exception {
try { try { Object result = inv.proceed();Object result = inv.proceed();Auditor.audit(inv.getMethod().getName(), inv.getParameters[0]);Auditor.audit(inv.getMethod().getName(), inv.getParameters[0]);return result;return result;
} catch (Exception ex) { Auditor.auditFailure(ex); } catch (Exception ex) { Auditor.auditFailure(ex); throw ex; }throw ex; }}}
}}public class CustomSecurity {public class CustomSecurity {
@AroundInvoke@AroundInvoke public Object customSecurity(InvocationContext inv) throws Exception…. public Object customSecurity(InvocationContext inv) throws Exception….
Application: EJB SecurityApplication: EJB Security@RolesAllowed(“sysadmin”)@RolesAllowed(“sysadmin”)public class Maintainence {public class Maintainence {
public void importCompanyData () {...}public void importCompanyData () {...}public void restart () {...}public void restart () {...}......
}}
@Stateless @Stateless public class HRActivity {public class HRActivity {
@RolesAllowed(“HR”)@RolesAllowed(“HR”)public void addToPayroll () {...}public void addToPayroll () {...}
@RolesAllowed({“HR”, “Manager”})@RolesAllowed({“HR”, “Manager”})public void postJobOpening() {...}public void postJobOpening() {...}......
}}
Application Idea: Code Application Idea: Code Maintentance AnnotationsMaintentance Annotations
Public class MyAppClass {Public class MyAppClass {
// Tags to generate documentation of SQL used in your application so it’s easier to// Tags to generate documentation of SQL used in your application so it’s easier to
// figure out implications of dbms schema changes and dbms tuning.// figure out implications of dbms schema changes and dbms tuning.
@SqlTracker(preparedQuery=sql)@SqlTracker(preparedQuery=sql)
protected static final String sql = “select id, version from astro_entity where type protected static final String sql = “select id, version from astro_entity where type = ?”;= ?”;
public ArrayList<SuperNova> getSuperNova(String snType) { ….}public ArrayList<SuperNova> getSuperNova(String snType) { ….}
// Tags to generate documentation on hacks and things todo for future releases.// Tags to generate documentation on hacks and things todo for future releases.
@Hack(problem=“Uses linear sort algorithm”, fix=“Use tree sort”)@Hack(problem=“Uses linear sort algorithm”, fix=“Use tree sort”)
@Refactor(idea=“move sorting to separate class”)@Refactor(idea=“move sorting to separate class”)
public voidpublic void
sortSuperNova(ArrayList<SuperNova> sn) sortSuperNova(ArrayList<SuperNova> sn)
{{
……
}}