Metadata for Java

Metadata is information that cannot be expressed in Java, but is nevertheless important for your Java application to work properly. Currently, metadata is expressed in separate text, Java properties, and XML files, but that poses a serious problem: code is disconnected from configuration, making development, deployment, and maintenance that much more difficult. JDK 1.5 addresses this disparity, capturing critical information where it belongs: right in your code.

Sun has always been extremely conservative with Java, introducing very few radical changes even between major releases. The Java Development Kit 1.5 (JDK 1.5, code-named “Tiger”) marks a radical shift from that policy. JDK 1.5 has several new features, including generics, boxing, and enhanced enumerators. [See the accompanying feature story, "Java 1.5," on page 18 for more information.] JDK 1.5 also introduces metadata.

There are numerous ways to describe what metadata is, but a simple definition, at least in this context, is “information that cannot be expressed in Java, but is nevertheless important for your Java application to work properly.” For example, Java properties and XML files are two forms of metadata that you probably use every day.

But metadata — as it’s expressed now in separate text files — poses a serious problem: as applications grow and evolve, the dependence and potential for discontinuity between code and configuration grows, making development, deployment, and maintenance that much more difficult. Tiger’s embedded metadata feature aims to address this disparity, capturing critical information where it belongs: in the code.

For Java, the emergence of the J2EE specification has increased the importance of metadata to a point where it’s now a vital part of most of enterprise applications. A J2EE application is typically made of Java classes, resources, and a good deal of metadata specified in XML files. Indeed, metadata is used widely in Java to specify object-relational mappings (how Java fields are mapped to database columns); to describe security policies (who has the right to invoke particular methods); to make parts of a Java class accessible remotely; to expose Java methods as web services, and more.

For example, here is how you specify a transaction attribute value of Required for an EJB method:


But specifying metadata this way suffers from some severe drawbacks:

1. IT ISN’T JAVA. Java programmers are forced to switch to a different language (with different syntax, semantics, and tools) just to specify metadata.

2. IT’S VERY FRAGILE. For example, if you rename findBy FirstName() in your Java code, you must update the XML descriptor as well or your application will fail in mysterious ways.

About three years ago, a new technique appeared that used comments to insert metadata in Java code. For example, one way to specify the example above would be:

* @ejbgen:remote-method
* transaction-attribute = Required
public String findByFirstName(String
firstName) {
// …

This approach solves some of the problems mentioned above. There’s no more redundancy — the signature of the method doesn’t need to be repeated since the annotation sits on top of the method it applies to — and there’s no need to maintain another file. All of the metadata is part of the source.

This technique has dramatically simplified the J2EE programming model, and it’s been generalized to a lot of other domains as well. You can now find tools that parse such annotations and generate all of the J2EE XML descriptors automatically.

In fact, it’s been so successful that Sun decided to validate it by making it part of the Java language. Thus, JSR 175, Java Metadata, was born.

Formally incorporating annotations into Java formalizes the syntax and provides static typing. Annotations become real Java types that can benefit from the richness of the Java type system.

Defining Metadata

Metadata for Java is a two-step process: You define the annotation types and then access the value of those types in your code.

For example, assume that you’re writing a tool that persists Java classes to a database. To do that, you’d ask your users to specify some object-relational mapping information as metadata on their classes and methods:

* Each class must have an annotation named Persistence Info that describes how to connect to the database via JDBC. This is typically done with a driver name, a connection URL, and, optionally, a user name and password.

* All fields that must be persisted to the database must have a getter (such as getFirstName()), and that getter must have an annotation named PersistentField that specifies the table and column name where the field should be stored.

Here’s a definition of the PersistenceInfo annotation:

01 package com.beust.persistence.annotations;
03 import java.lang.annotation.*;
05 @Retention(RetentionPolicy.RUNTIME)
06 @Target(ElementType.TYPE)
07 public @interface PersistenceInfo {
08 public String driverName();
09 public String connectionUrl();
10 public String userName() default “”;
11 public String userPassword() default “”;
12 }

Let’s examine each line in some detail.

* LINE 1. Even though the specification doesn’t mandate it, it’s good practice to put annotation types in a separate package.

* LINE 5. Annotations can be stored three different ways. @Retention is used to select the method, and can be one of SOURCE, CLASS, or RUNTIME. If set to SOURCE, the annotations are only available in the source and are omitted from the compiled file. If CLASS, the annotations are included in the .class file, but aren’t visible to the reflection API (this is the default). If RUNTIME, the annotations are included in the .class file and are accessible from the reflection API (discussed below).

* LINE 6. The @Target annotation details how your annotation should be applied. The most common cases will likely be on the class (TYPE) and method (METHOD), but there are many more possibilities, including field, method, parameter, and others. (Try javap java.lang.annotation.ElementType for a complete list).

* LINE 7. interface is prefixed with @, which distinguishes an annotation type from a regular interface.

* LINE 8-11. The first two elements of the annotations, driverName() and connectionUrl() are required, but the last two are optional, indicated by the default phrases.

Given the PersistenceInfo annotation, you can now define the annotation type for your persistent fields.

package com.beust.persistence.annotations;

import java.lang.annotation.*;

public @interface PersistentField {
public String tableName();
public String columnName();

The only thing worth mentioning here is that ElementType is now METHOD, since you want this annotation to be available on methods only.

Compiling these annotations is straightforward (all examples were built using the public JDK 1.5 beta, also referred to as “build 40″):

$ javac -source 1.5 -d build Persistence \
Info.java PersistentField.java

Using Annotation Types

Now that you have your annotation types defined and built, it’s time to use them. Here’s an annotated class called Person .java:

package com.beust.persistence;

import com.beust.persistence.annotations.*;

driverName = “com.mysql.jdbc.Driver”,
connectionUrl = “jdbc:mysql://localhost
public class Person {
private String m_firstName;
private String m_lastName;

tableName = “PERSON”,
columnName = “FIRST_NAME”
public String getFirstName() {
return m_firstName;

tableName = “PERSON”,
columnName = “LAST_NAME”
public String getLastName() {
return m_lastName;

Notice that annotations are prefixed by @ and that they appear exactly on top of the element that you’re annotating.

The way attributes are assigned is new in Java, but it’s basically a simple series of attribute = value statements separated by commas.

In a sense, the syntax can be seen as the invocation of a constructor except that you need to name the parameters and assign them a value. You can compile this file the same way you compiled the annotations, but don’t forget to add the build directory to your CLASSPATH or javac won’t find the annotations you imported.

$ javac -source 1.5 -d build -classpath \
“build;$CLASSPATH” Person.java

Now let’s see how we can query annotations.

The Annotation API

Two application programming interfaces (APIs) are available to query annotations: source (sometimes referred to as the “arm’s length API”) and class (or reflection).

The source-level API can be used when you don’t want to require users to compile their annotated file before you can access the annotations. The source-level API uses Javadoc 1.5, which has been enhanced to support annotations.

Listing One shows a sample use of the Javadoc API and illustrates some of the new features that were added to Javadoc to enable annotation querying. The example loops through the source files that were passed on the command line, retrieves the annotations, and then enumerates their values.

Listing One: Reading the annotations with the Javadoc API

package com.beust.persistence;

import com.sun.javadoc.*;

public class SourceTest {
public static boolean start(RootDoc root) {
ClassDoc[] cd = root.classes();
System.out.println(“DOCLET STARTING, ” +
cd.length + ” CLASSES”);
for (int i = 0; i < cd.length; i++) {
System.out.println(” CLASS ” + cd[i]);
AnnotationDesc[] annotations =
for (int j = 0; j < annotations.length; j++) {
AnnotationDesc a = annotations[j];
System.out.println(” ANNOTATION:” +
AnnotationDesc.MemberValuePair[] pairs =
for (int k = 0; k < pairs.length; k++) {
System.out.println(” CLASS ATTRIBUTE:
” + pairs[k].toString());

return true;


After compiling this program, launch it via Javadoc with:

$ javadoc -classpath build:$CLASSPATH \
-source 1.5 -doclet \
com.beust.persistence.SourceTest \
-docletpath build Person.java

The output of Listing One should look something like Figure One.

Figure One: The output of the Javadoc code in Listing One

CLASS com.beust.persistence.Person
CLASS ATTRIBUTE: connectionUrl=

Similarly, you could iterate through the methods declared in this class and then retrieve the annotations on each of them.

Another way of retrieving the annotations of a Java class is by using reflection. Using reflection, it’s assumed that the user compiled their class and that your code can simply look up the class object. Several methods have been added to the java.lang.reflect package to allow you to retrieve annotations.

Listing Two shows an example of how to use the reflection API to access annotations. You can compile this test the same way you compiled the previous files, again making sure your CLASSPATH contains the directory where you built the annotations. The output of Listing Two should look like Figure Two.

Listing Two: Reading the annotations with the reflection API

package com.beust.persistence;

import com.beust.persistence.annotations.*;

import java.lang.annotation.*;

public class ClassTest {
public static void main(String[] argv) {
Class c = Person.class;
Annotation[] annotations = c.getAnnotations();
for (int i = 0; i < annotations.length; i++) {
Annotation a = annotations[i];
System.out.println(“ANNOTATION:” + a);

if (a instanceof PersistenceInfo) {
PersistenceInfo pi = (PersistenceInfo) a;
System.out.println(” FOUND
System.out.println(” driver:” +
System.out.println(” connection URL:”
+ pi.connectionUrl());

Figure Two: The output of the reflection API used in Listing Two

userPassword=, driverName=com.mysql.jdbc.Driver,
FOUND PersistenceInfo:
connection URL:jdbc:mysql://localhost/test

There is a lot more to the source-level and reflection APIs, and you can retrieve nearly any value on any annotation in your code. See the JDK 1.5 API for all the details.

If this is the first time you’ve seen the metadata facility, you are most likely wondering why certain features were left out, why others were implemented, and why the syntax is what it is. The JSR 175 specification contains an enlightening FAQ section that should answer all your questions, such as “Why isn’t null allowed as a default value?” and “Why methods in annotation types and not fields?” (For the former, it’s not legal for switch/case, and in the latter, it’s more consistent with the syntax.) If you can’t find the answer to your question or you’re not convinced of a certain answer, feel free to email the JSR 175 expert panel [of which the author is a member] at jsr-175-comments@jcp.org.

Migrating from Javadoc

If you’ve been using Javadoc annotations for a while, the first thing you probably noticed in this new syntax is that you need to import the 1.5 annotations in your source file. No such thing was necessary with Javadoc annotations, since those were in comments and completely hidden from the Java compiler.

Importing the annotations was heavily debated within the JSR 175 Experts Group, which considered, at some point, making this requirement optional so that the compilation can proceed smoothly even if the imported annotations are not found. After all, the idea is that even though the annotations won’t be parsed, the Java code can still be valid. However, the idea was eventually refused on the grounds that relaxing the import constraint would considerably weaken the compilation model. It would also become impossible for the compiler to tell the difference between an annotation that cannot be found and one that has simply been misspelled.

You are therefore guaranteed full static typing for your source files, including your annotations, but the drawback is that you won’t be able to compile your source file if you don’t have the annotations used inside. The corollary of this observation is that annotation types will become an integral part of the library you ship.

Metadata Tricks and Tips

As an annotation type author, one decision you’ll be confronted with is, “Which retention method do I use?” There is, obviously, no general answer and it all depends on what your target audience is.

If you are writing a tool that generates files based on annotations, such as EJBGen or XDoclet, SOURCE retention is probably sufficient.

At the same time, there’s a lot of potential in RUNTIME retention for deployment purposes. Right now, an application server deploying a module typically needs to open the .ear file, read and parse the XML descriptors, and then tune the deployment based on the values read. This is both time (I/O) and memory (XML parsing) consuming, and explains in great part why deploying big J2EE applications can sometimes take so much time. Annotations can become a big time saver in the future, where the deployer can read the information it needs directly from the class file, therefore skipping all this expensive preprocessing.

Also, keep in mind that annotations can use the new features of JDK 1.5, and more notably, enums. If your annotation can only have a set of well-defined values, you are strongly encouraged to use enum to define it.

For example, imagine that you’re creating a JMS listener and you need the user to specify whether the class should listen to a topic or a queue. You might want to define an annotation type as…

public @interface JMSListener {
enum DestinationType { Queue, Topic };
DestinationType destinationType();

… which can then be used as…

destinationType = JMSListener.Queue
public class Bar {
// …

There is a shortcut notation that can come in handy in certain situations. For example, imagine an @Timeout annotation that takes a single integer. In such a case, you can use the single-member shortcut to improve readability. The only requirement is that the member must be called value() and it should be the only one in your annotation type definition:

public @interface Timeout {
Integer value();

You can then use the shorter notation:

public void invokeService() {
// …

If you want to allow a certain annotation to be present multiple times on a Java element, you need to create a container type that holds an array of these annotations.

For example, assume that you have an Author annotation type:

public @interface Author {
public String getLastName();

You’d define the container like this…

public @interface Authors {
public Author[] value();

… which allows someone to do the following:

Author(“Beust”), Author(“Bloch”),
public class Foo {
// …

Finally, sometimes, certain information doesn’t belong in any class in particular, but more to the application. In such a case, it makes little sense to arbitrarily pick a class and define the annotation on that class. So JSR 175 allows you to specify package-level annotations. As the name implies, such annotations can simply be placed on top of the declaration of your package:

package com.beust;
public class Foo {

Making the Most of Metadata

Metadata is a very powerful tool for Java programmers and will no doubt make its way into the Java sources we read and write every day. It, and other new features in JDK 1.5 will take some time to get used to, but will take Java to a new level of productivity.

Cedric Beust is a Senior Software Engineer at BEA Systems and is a member of the Experts Group that defined JSR 175. He can be reached at cbeust@bea.com.

Comments are closed.