Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 2 of 4
The following are the six built-in annotations in J2SE 5:
@Target(ElementType.METHOD)
public @interface Overrides
{
}
This annotation is used to indicate that a method declaration in the current class is intended to override a method declaration in its (direct or indirect) superclass. If a method is decorated with this annotation type but does not override a superclass method, then the compiler will generate an error message. As an example, consider the following code fragment:
class A
{
@Overrides
public String toString(int i)
{
return "";
}
}
Compiling this code fragment produces the following error during compilation:
method does not override a method from its superclass
@Overrides
^
To fix the error, change the method signature from public String toString(int i) to public String toString(). Using this annotation is a good way to catch inadvertent programming errors during compilation, where you think you are
overriding a method, but in reality you are creating a new one.
@Documented
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented
{
}
This annotation indicates that the annotation to which this is applied is to be documented by javadoc and similar tools. Note that this annotation is just a hint, and a tool can ignore the annotation if it desires to do so.
@Documented
@Retention(RetentionPolicy.SOURCE)
public @interface Deprecated
{
}
This annotation provides a hint to the Java compiler to warn users if they use the class, method, or field annotated with this annotation. Typically, programmers are discouraged from using a deprecated method (or class), because either it is dangerous or a better alternative exists.
As an example of when you could use the Deprecated annotation, consider a scenario where you have implemented a sort() method on a class used by many other developers on different projects. Later you discover a shortcoming in the code and not
only have to rewrite the function, but also change its signature. You can't remove the old sort() method because other programs rely on it. What you can do is annotate it as being deprecated so when developers work on and
compile their code, they will be advised to use the new sort() method instead of the old one.
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited
{
}
The best way to understand this annotation is through an example. Let's say you created your own annotation called Serializable. A developer would use your annotation if his class implemented the Serializable interface. If the developer created any new classes that derived from his original class, then marking those classes as Serializable would make sense. To force that design principal, you decorate your Serializable annotation with the Inherited annotation. Stated more generally, if an annotation A is decorated with the Inherited annotation, then all subclasses of a class decorated by the annotation A will automatically inherit the annotation A as well.
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention
{
RetentionPolicy value();
}
The Retention annotation takes a single parameter that determines the decorated annotation's availability. The values of this parameter
are:
RetentionPolicy.SOURCE: The decorated annotation is available at the source code level only
RetentionPolicy.CLASS: The decorated annotation is available in the source code and compiled class file, but is not loaded into the JVM at runtime
RetentionPolicy.RUNTIME: The decorated annotation is available in the source code, the compiled class file, and is also loaded into the JVM at runtime
By default, all annotations are available at the source level only.
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target
{
ElementType[] value();
}
This annotation is used to indicate the type of program element (such as a class, method, or field) to which the declared
annotation is applicable. If the Target annotation is not present on an annotation type declaration, then the declared type may be used on any program element. If
the Target annotation is present, then the compiler will enforce the specified usage restriction. Legal values for the Target annotation are contained in the java.lang.annotation.ElementType enumeration. An annotation can be applied to any combination of a class, a method, a field, a package declaration, a constructor,
a parameter, a local variable, and another annotation.
It's easy to create and then use a custom annotation. As you have already seen above, the declaration of an annotation is straightforward. In this section, I walk you through a complete exercise of creating, applying, and dynamically querying a custom annotation during runtime.
Let's create an annotation that can be applied to classes and methods and enforces role-based security. The declaration is shown below:
@Documented
@Target({METHOD,TYPE})
@Retention(RUNTIME)
public @interface SecurityPermission
{
String[] value();
}
We can glean the following information from the declaration:
SecurityPermission.
The next step is declaring a class that uses our new annotation. An example is shown below:
@SecurityPermission("All")
public class AnnotationsTest
{
public AnnotationsTest()
{
SecurityBlanket.checkPermission();
}
@SecurityPermission("All")
public void unRestrictedMethod(String message)
{
SecurityBlanket.checkPermission();
System.out.println("Message from unRestrictedMethod: " + message);
}
@SecurityPermission("None")
public void fullyRestrictedMethod(String message)
{
SecurityBlanket.checkPermission();
System.out.println("Message from fullyRestrictedMethod: " + message);
}
@SecurityPermission("Manager")
public void partiallyRestrictedMethod(String message)
{
SecurityBlanket.checkPermission();
System.out.println("Message from partiallyRestrictedMethod: " + message);
}
@SecurityPermission({"Manager","HR"})
public void partiallyRestrictedMethod2(String message)
{
SecurityBlanket.checkPermission();
System.out.println("Message from partiallyRestrictedMethod2: " + message);
}
}
As you can see, AnnotationsTest is a class just like any other with special javadoc-like tags (@SecurityPermission) above the class and method declarations. This is how you apply the SecurityPermission annotation. Note that this is also the same way you apply the built-in annotations to Java code, such as, for example, to
the SecurityPermission annotation declaration. Next, look at the various SecurityPermission annotation declarations to see how I specified the value parameter's value.
As you peruse through the AnnotationsTest class above, you may wonder what the call SecurityBlanket.checkPermission() is doing. This is where all the "magic" occurs. Simply declaring a custom annotation and then decorating your code with it
is not going to accomplish much. The Java runtime has no idea what to do with your custom annotations. They just occupy valuable
memory space doing nothing. That's where the SecurityBlanket.checkPermission() method call fits in.
First, this function figures out what the currently executing method is. Next, the function determines if the caller's role
is one of the permitted roles for the method. If the method is a class constructor, then the function uses the roles specified
in the SecurityPermission annotation applied to the class declaration; otherwise it uses the roles specified in the SecurityPermission annotation applied to the method declaration. The caller's roles are specified on a per-thread basis by calling the SecurityBlanket.addPermission method. In a real application, a custom JAAS (Java Authentication and Authorization Specification) module that allows a user
to log into your application may call the SecurityBlanket.addPermission method on your behalf.
The point to realize from all this is that J2SE 5 provides us with a way of declaring our own custom annotations and a way of decorating our own code with them. It also loads them in the JVM and makes them available dynamically and reflectively. But then we have to write the glue logic that actually makes something useful happen.
The entire SecurityBlanket class is shown below:
public class SecurityBlanket
{
private static Logger logger = Logger.getLogger("AnnotationsTest");
private static HashMap<Thread, String> permissions = new HashMap<Thread, String>();
public static void addPermission(String s)
{
permissions.put(Thread.currentThread(),s);
}
public static void removePermission()
{
permissions.remove(Thread.currentThread());
}
public static void checkPermission()
{
StackTraceElement e[]=Thread.currentThread().getStackTrace();
int i = 0;
for (i = 0; i <e.length; i++)
{
if (e[i].getClassName().equals("SecurityBlanket")
&& e[i].getMethodName().equals("checkPermission"))
break;
}
if (i == e.length)
{
throw new RuntimeException("Unexpected Security Error.");
}
logger.info("Checking security access to "
+ e[i+1].getClassName()
+ "::"
+ e[i+1].getMethodName());
try
{
Class c = Class.forName(e[i+1].getClassName());
if (e[i+1].getMethodName().equals("<init>"))
{
SecurityPermission permission =
(SecurityPermission)
c.getAnnotation(SecurityPermission.class);
if (permission != null)
{
String currentRole =
permissions.get(Thread.currentThread());
for (String role:permission.value())
{
if (role.equals("All"))
return;
else if (role.equals("None"))
{
throw new RuntimeException(
"Unauthorized access to class "
+ e[i+1].getClassName());
}
if (role.equals(currentRole))
return;
}
}
return;
}
Method[] methods = c.getMethods();
for (Method m:methods)
{
if (m.getName().equals(e[i+1].getMethodName()))
{
SecurityPermission permission =
m.getAnnotation(SecurityPermission.class);
if (permission != null)
{
String currentRole =
permissions.get(Thread.currentThread());
for (String role:permission.value())
{
if (role.equals("All"))
return;
else if (role.equals("None"))
{
throw new RuntimeException(
"Unauthorized access to "
+ e[i+1].getClassName()
+ "::"
+ e[i+1].getMethodName());
}
if (role.equals(currentRole))
return;
}
}
break;
}
}
throw new RuntimeException("Unauthorized access to "
+ e[i+1].getClassName()
+ "::"
+ e[i+1].getMethodName());
}
catch (ClassNotFoundException ex)
{
logger.severe("Got an error: " + ex.getMessage());
throw new RuntimeException("Unexpected Security Error.");
}
}
}
The bulk of the code is straightforward. However, note the use of the getAnnotation() method on the java.lang.reflect.Method and the java.lang.Class classes. These are just two of the many new methods added to support reflectively retrieving annotations during runtime.
Archived Discussions (Read only)