 | Level: Intermediate Dennis Sosnoski (dms@sosnoski.com), Consultant, Sosnoski Software Solutions, Inc.
02 Aug 2005 Annotations let you specify metadata as part of your source code. With this feature, you can embed tool instructions in your code rather than creating separate configuration files that you then need to maintain in parallel to the source code. But, as Java™ consultant Dennis Sosnoski explains, configuration files still have their uses, especially for aspect-like functions that cut across the source code structure of an application.
Annotations are one of the major new features of J2SE 5.0, designed
to allow metadata about code to be defined as part of the code. Having the
metadata inlined with the actual code it
references provides locality of reference, so that everything is in one place.
This approach makes it easier to maintain the metadata as the corresponding code
changes. However, annotations also have some weaknesses.
You can define metadata in other ways besides keeping it inline with the
code. I'll lump these alternatives under the general heading of "configuration
files" for the purposes of this column, with the understanding that a
configuration file approach is anything that uses a separate non-Java file for
some form of metadata. This type of approach creates a potential maintenance
problem, because the metadata has to refer to code while being physically
separate. If the code changes, the metadata may need to be modified, and with a
configuration file approach to metadata, you have to remember to go to
the separate configuration file and modify it.
In this column, I'm going to discuss the trade-offs between these two approaches
as I see them, and look at some of the implications for developers as the Java
standards process and many open source projects convert to an ever-more
annotation-based approach to metadata.
Historical background
Configuration files were in use long before the development of the Java
platform but were generally used just to provide run-time parameters for
controlling the execution of an application. The development of the J2EE
platform and related component standards brought a different type of
configuration file into widespread use, a type that contained information used
by a framework in working with application code. One of the earliest examples is
the web.xml file used for Java servlet deployment configuration.
The Java platform's support for dynamic classloading and run-time access to
class metadata through reflection offers many different ways to use configuration
files to control the loading and execution of programs at run time. Developers
were quick to take advantage of the flexibility this approach provided. But as framework
configuration files proliferated, developers have found these files complex to
generate and maintain. Changes to Java source code may require modifications to
the configuration files, but that requirement is not shown by anything in the
source code -- instead, the developers must be aware of the connection between
the source code and the configuration file and remember to keep them in sync.
Enter XDoclet
The popular XDoclet tool was developed to help manage this issue of
linkages between files. Rather than maintaining configuration information as
separate files, XDoclet allows the configuration information to be embedded in
the Java source code by using special Javadoc tags. XDoclet then builds the
configuration files as needed from these tags. Using XDoclet, your configuration
information can be kept with your source code so that everything you need to
track is in one place.
 | |
XDoclet actually goes well beyond just generating configuration files. It was
originally designed for use with EJBs, which require several boilerplate files
for each bean in addition to the actual configuration files. XDoclet
automatically generates these boilerplate files for an EJB from the basic bean
source code, as controlled by the special Javadoc tags. As XDoclet has been
extended to deal with many other usages beyond just EJBs, this code generation
facility has also been extended. It's an extremely useful tool in situations
where boilerplate code is required, helping to keep your source tree free of
clutter that contributes nothing to the operation of the application.
XDoclet does suffer from a few limitations. Because it uses simple
transformations to convert Javadoc-style comments into configuration files, many
aspects of the configuration are not checked for accuracy. In the past, I've had
unfortunate experiences with XDoclet because of typos such as misspelling and
mismatched quotes around values. Errors of this type get passed through into the
generated code or configuration files, which can make it difficult to backtrack
to the original source of the problem.
Annotations to the rescue?
Partly inspired by the success of XDoclet, JSR-175 was formed in 2002 to
provide a standard means for associating metadata in the form of arbitrary
attribute information, with particular Java classes, interfaces, methods, and
fields. Although a custom Javadoc tag similar to XDoclet was listed as one
possible implementation
in the JSR proposal, the final form of JSR-175 metadata support took a very
different approach. This support uses extensions to the Java language defining
annotations, JavaBean-like groups of name-value pairs that can be added as
modifiers on any Java component declaration.
Annotations get around many of the limitations of the XDoclet approach. By
using typed values, and allowing particular annotations to be restricted to being used
with only some types of Java components, annotations allow a much higher degree
of validation than provided by XDoclet. Because annotations are integrated into
the Java language definition, they are also more easily processed (as long as
you're using JDK 5.0, anyway -- for some pointers on using annotations with
earlier JVMs see last month's column). They're
also more flexible in terms of how you use them, with options for whether the
annotation information is to be included in class files output by the compiler
and made available to the application at run time.
Ever since JSR-175 got started, there's been great interest in defining
metadata as part of other JSRs. Once the decision was made to include annotations
in J2SE 5.0, the use of annotations became part of the planning for many
new JSRs scheduled to reach completion after the release of J2SE 5.0. We're
starting to see the release of these JSRs to public comment now, and I'll
use an example from one of these in the next section.
Trade-offs
Annotations represent a powerful refinement of the XDoclet principle of
embedding configuration information in Java source code. The main advantage of
this approach is that everything is in one place and the configuration
information is associated directly with the Java component. Many types of
refactorings of the source code are transparent to annotations for this reason
-- the annotations will always apply to the component they're attached to, even
when that component is moved or renamed. For other refactorings that require new
or modified annotations, everything is in the same location, assuring that the
annotations are visible to the developer and increasing the likelihood that
they'll remember to make the necessary changes.
Despite the benefits of annotations, I see two main drawbacks to their (over)
use. The first is that source code can become cluttered with all sorts of
annotations that are irrelevant to the actual program logic, interfering with
the readability of the code. The second drawback is that while annotations are
ideal for metadata that relates to a particular component, they're not well
suited to metadata with cross-component application.
Configuration files, on the other hand, can provide an organized view of a
web of relationships that span different components of an application. Because
they're separate from the actual source code, they don't interfere with the
readability of the Java source code. The main drawback of configuration files is
as mentioned earlier -- they're separate artifacts that need to be maintained in
parallel with the source code of an application, with no obvious connection
between the two.
Annotation (mis)use
As an example of what I consider annotation misuse, Listing 1 gives an
excerpt from the annotations example supplied with the early access version of
JAX-RPC 2.0 (reproduced here under the publication provision of the Java
Research License used for the early access distribution). Disclaimer:
Although I'm a member of the JAX-RPC 2.0 expert group, the opinions expressed
here are strictly my own and are not intended to reflect those of the expert
group as a whole.
Listing 1. JAX-RPC 2.0 annotation example
/*
* Copyright (c) 2005 Sun Microsystems, Inc.
* All rights reserved.
*/
package annotations.server;
import java.rmi.Remote;
import java.rmi.RemoteException;
import javax.jws.WebService;
import javax.jws.WebMethod;
import javax.jws.soap.SOAPBinding;
import javax.jws.WebResult;
import javax.jws.WebParam;
@WebService(targetNamespace = "http://duke.org", name="AddNumbers")
@SOAPBinding(style=SOAPBinding.Style.RPC, use=SOAPBinding.Use.LITERAL)
public interface AddNumbersIF extends Remote {
@WebMethod(operationName="add", soapAction="urn:addNumbers")
@WebResult(name="return")
public int addNumbers(
@WebParam(name="num1")int number1,
@WebParam(name="num2")int number2) throws RemoteException, AddNumbersException;
}
/*
* Copyright (c) 2005 Sun Microsystems, Inc.
* All rights reserved.
*/
package annotations.server;
import javax.jws.WebService;
@WebService(endpointInterface="annotations.server.AddNumbersIF")
public class AddNumbersImpl {
/**
* @param number1
* @param number2
* @return The sum
* @throws AddNumbersException
* if any of the numbers to be added is negative.
*/
public int addNumbers(int number1, int number2) throws AddNumbersException {
if (number1 < 0 || number2 < 0) {
throw new AddNumbersException("Negative number can't be added!",
"Numbers: " + number1 + ", " + number2);
}
return number1 + number2;
}
}
|
The Listing 1 code consists of a Web service interface definition and the
corresponding implementation class. Although the interface definition is
expressed as Java code (of a sort -- when I showed this example as part of a
recent presentation, an audience member remarked that he wouldn't have
recognized this as Java code if I hadn't identified it as such), it's really
just a configuration file coded as annotations.
Does the use of annotations in Listing 1 actually provide any benefits over
a separate configuration file? Listing 2 gives a configuration file providing
the same information as the Listing 1 annotations (using Apache Axis's
internal configuration format). To me, the configuration file form looks cleaner
and therefore easier to maintain than the mass of annotations shown in the
example. If you really want to tie the implementation class to its use as a Web
service (the apparent point of the @WebService
annotation in the Listing 1 implementation class), an annotation could still be
used just for this purpose (referencing the configuration file, rather than the
interface). Otherwise, there's no need to have any special information in the
implementation class at all.
Listing 2. Configuration file equivalent to Listing 1 annotations
<deployment xmlns="http://xml.apache.org/axis/wsdd/"
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<service name="AddNumbers" provider="java:RPC" style="rpc" use="literal">
<namespace>http://duke.org</namespace>
<parameter name="className" value="annotations.server.AddNumbersImpl"/>
<parameter name="allowedMethods" value="add"/>
<operation name="add" qname="tns:addNumbers" returnQName="return">
<parameter name="num1"/>
<parameter name="num2"/>
</operation>
</service>
</deployment>
|
Where annotations break down
In the last section, I gave an example of what I see as inappropriate use of
annotations, where a separate configuration file would work just as well and be
easier to understand. For an example of a case where a configuration file works
better than annotations, I'll turn to the JiBX data binding framework -- my own pet
classworking project.
JiBX uses bytecode enhancement to add methods to compiled classes that
convert instances of the classes to and from XML. The details of the
relationship between Java classes and XML is determined by a binding definition,
a form of XML configuration file. This configuration is processed by a binding
compiler component of the JiBX framework, which implements the actual bytecode
enhancement of the compiled classes.
Where JiBX differs from other data binding frameworks (aside from speed --
it's generally several times faster than the alternatives) is in the flexibility
of the relationship between Java classes and XML. Most data binding frameworks
assume that the structure of Java classes matches the structure of the XML, in
that elements with attributes or child elements correspond to JavaBean-like
classes, while individual attributes or simple child elements correspond to
simple properties of JavaBean-like classes. JiBX drops this assumption and uses
binding definitions that allow for structural differences between the Java
classes and the XML representation. JiBX even supports multiple bindings to
the same classes, with the actual binding to be used for a particular XML
document selected at run time.
To illustrate, I'll give a simple example of JiBX flexibility. Listing 3
shows a pair of data classes, with Listings 4 and 5 each giving a different
binding definition for these classes along with a corresponding XML document.
Both sample XML documents present the same data, and most developers would find
it easy to relate these documents to the Listing 3 Java classes despite the very
obvious differences in structure of the two XML documents. Most developers also
find it easy to understand how the corresponding binding definitions relate to
the structure of each XML document.
Listing 3. Data classes for XML binding
package simple;
public class Customer
{
private Name name;
private String street1;
private String street2;
private String city;
private String state;
private String zip;
private String phone;
...
}
package simple;
public class Name
{
private String firstName;
private String lastName;
...
}
|
Listing 4. First binding and XML document
<binding name="binding1">
<mapping name="customer" class="simple.Customer">
<structure field="name">
<value name="first-name" field="firstName"/>
<value name="last-name" field="lastName"/>
</structure>
<value name="street" field="street1"/>
<value name="city" field="city"/>
<value name="state" field="state"/>
<value name="zip" field="zip"/>
<value name="phone" field="phone"/>
</mapping>
</binding>
<customer>
<first-name>John</first-name>
<last-name>Smith</last-name>
<street>12345 Happy Lane</street>
<city>Plunk</city>
<state>WA</state>
<zip>98059</zip>
<phone>888.555.1234</phone>
</customer>
|
Listing 5. Second binding and XML document
<binding name="binding2">
<mapping name="customer" class="simple.Customer">
<structure name="name" field="name">
<value name="first-name" field="firstName"/>
<value name="last-name" field="lastName"/>
</structure>
<structure name="address">
<value name="street" field="street1"/>
<value name="city" field="city"/>
<value style="attribute" name="state" field="state"/>
<value style="attribute" name="zip" field="zip"/>
</structure>
<value style="attribute" name="phone" field="phone"/>
</mapping>
</binding>
<customer phone="888.555.1234">
<name>
<first-name>John</first-name>
<last-name>Smith</last-name>
<name>
<address state="WA" zip="98059">
<street>12345 Happy Lane</street>
<city>Plunk</city>
</address>
</customer>
|
Now consider what would be involved in using annotations to define the
Listings 4 and 5 bindings. Listing 6 gives one version of such annotations. Here,
I'm assuming several annotations to represent the different options that can be
used in a binding definition, most of them with variations for a single binding
(using a simple value) vs. multiple bindings (using arrays of values). Even for
this simple example, the result is very messy, and at least to me it seems much
more difficult to either read the Java code as such (compared to the unannotated
version) or to make sense of the binding definitions (compared to the
definitions in Listings 4 and 5).
Listing 6. Annotated Java classes for bindings
package simple;
// Need the JiBXAddedWrappers annotation here because there's no other place to
// put it. The ugly format of the annotation is necessary because you can't
// repeat an annotation, and can only have array values of base types.
@JiBXMapping(names={"binding1", "binding2"})
@JiBXAddedWrappers(bindings={"binding2"}, names={"address"}, field-sets={"street1,city.state,zip"})
public class Customer
{
@JiBXStructure(bindings={"binding1", "binding2"}, names={"", "name"}) private Name name;
@JiBXValue(name="street") private String street1;
private String street2;
@JiBXValue private String city;
@JiBXValue(bindings={"binding1", "binding2"}, styles={element, attribute})
private String state;
@JiBXValue(bindings={"binding1", "binding2"}, styles={element, attribute})
private String zip;
@JiBXValue(bindings={"binding1", "binding2"}, styles={element, attribute})
private String phone;
...
}
package simple;
public class Name
{
@JiBXValue(name="first-name") private String firstName;
@JiBXValue(name="last-name") private String lastName;
...
}
|
When I started developing JiBX, I originally planned to provide XDoclet
support as a convenient alternative for users who didn't want to maintain a
separate binding file. Trying to express the full flexibility of JiBX bindings
rapidly turned into a mess, though. XDoclet tags suffer from the same
limitations as annotations, so the XDoclet version of bindings looked very
similar to the Listing 6 annotations version. I decided it was better to just
force the use of a binding definition file than to encourage users to start down
the pitfall-laden path of defining binding metadata in tags.
Best practices
Hopefully, I've convinced you by this point that annotations are not always
better than configuration files. That leaves the issue of deciding which
approach works better in a particular use case. To me, it seems that the answer
revolves around the type of configuration information you need to represent.
Configuration information that is always going to be linked to a specific
Java component (class, method, or field) is a good candidate to be represented
by annotations. Annotations work especially well in this case when the
configuration is core to the purpose of the code. Because of the limitations on
annotations, it's also best when each component can only ever have one
configuration. If you need to deal with multiple configurations, especially ones
that are conditional on anything outside the Java class containing an
annotation, annotations may create more problems than they solve. Finally,
annotations cannot be modified without recompiling the Java source code, so
anything that needs to be reconfigurable at run time can't use annotations.
In an earlier column, I gave an example
of using annotations for automatic construction of toString() methods. Although that was a somewhat trivial
example, I think it does show the proper usage of annotations: The annotations
were for a purpose that was directly involved with the code (the toString() method), didn't require multiple configurations
(because there can only ever be one instance of the method per class), worked only
within a class unit, and did not need to be changed at run time.
Configuration files, on the other hand,
work best when different Java components are being used
in a coordinated way. The JiBX binding definitions from the last section are
a great example of this fact, where embedding the configuration in the code with
annotations makes both the code and the configuration difficult to understand. Even
beyond this confusion issue, I see the data binding function as an aspect (in
the AOP sense) that gets applied to the application. From the AOP standpoint,
it's clearly best to keep the aspect information (in this case, binding
definitions) outside the code.
Conclusions
In this column, I've set out what I see as the weaknesses of an
annotation-based approach to configuration information. Despite these weaknesses,
I think annotations are a very useful addition to the Java developer's toolkit.
On the other hand, I also think they can be overused and that some of the Java
standards now under development may be headed in that direction.
For my own JiBX project, I intend to stay with configuration files, because I
think these are better suited to represent JiBX bindings. Even so, I'm interested
in adding support for JAXB 2.0 annotations in JiBX 2.0 (hopefully both using the
annotations directly, and converting them to JiBX binding definition files). That
way users who really want to work with annotations will be able to use a standard
representation, while users who want the full flexibility of JiBX bindings will
be able to use binding definition files instead.
Next month I'm going to delve a little deeper into JiBX. As I write this, I'm
wrapping up the JiBX 1.0 production release and plan to start work soon on the
changes planned for 2.0. Now's a great time to summarize some of the
classworking do's and don'ts I've learned in the course of the JiBX 1.0
development, so that's just what I'll do in next month's column. Come back then
for a look behind the curtains at the bytecode generation core of JiBX.
Resources
About the author  | 
|  | Dennis Sosnoski is the founder and lead consultant of Java technology consulting
company Sosnoski Software Solutions, Inc., specialists in
XML and Web services training and consulting. His professional software development experience spans over 30 years, with the last several years focused on server-side XML and Java technologies.
Dennis is the lead developer of the open source JiBX XML Data Binding framework built around Java classworking technology and the associated JibxSoap Web services
framework, as well as a committer on the Apache Axis2 Web services framework. He was also one of the expert group members for the JAX-WS 2.0 specification. |
Rate this page
|  |