Skip to main content

skip to main content

developerWorks  >  Java technology  >

Classworking toolkit: Annotations vs. configuration files

Annotations may be all the rage, but configuration files still have their uses

developerWorks
Document options

Document options requiring JavaScript are not displayed


New site feature

Check out our new article design and features. Tell us what you think.


Rate this page

Help us improve this content


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.

Ask the expert: Dennis Sosnoski on JVM and bytecode issues
For comments or questions about the material covered in this article series, as well as anything else that pertains to Java bytecode, the Java binary class format, or general JVM issues, visit the JVM and Bytecode discussion forum, moderated by Dennis Sosnoski.

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.



Back to top


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.



Back to top


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

Author photo

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


Please take a moment to complete this form to help us better serve you.



 


 


Not
useful
Extremely
useful
 


Share this....

digg Digg this story del.icio.us del.icio.us Slashdot Slashdot it!



Back to top