Section 21.4. XDoclet Tutorial


21.4. XDoclet Tutorial

XDoclet is a toolset built on top of the Javadoc annotation mechanism. The XDoclet toolset is an open source project, currently managed on SourceForge. Essentially, XDoclet provides a suite of predefined custom Javadoc tags designed for use in various development scenarios, such as developing web and EJB components, implementing web services, integrating Hibernate persistence management, deploying components to specific application servers, and so on. Annotating your code is done using these Javadoc tags within code comments. XDoclet includes a set of Ant tasks that are used in your buildfiles to process the annotations and generate the appropriate outputs.

21.4.1. Annotating Your Code

XDoclet annotations can be applied to specific entities in your Java code:

  • Classes or interfaces

  • Methods and constructors

  • Member variables

These annotations look a lot like standard Javadoc comments. The general syntax for XDoclet annotations is:

 @tag-group.tag-name param1="value1" param2="value2" 

The tags are organized into groups, indicated by the tag-group label, and each tag has a unique name within its group, indicated by the tag-name. A tag can optionally accept one or more parameters, which are provided in the annotation using the syntax shown here. You can also put parameters on lines following the tag line. The XDoclet processor will consider all subsequent parameter lines to belong to the initial tag you specify. So the previous example could also be put into your code as:

 @tag-group.tag-name     param1="value1"     param2="value2" 

XDoclet annotations always reside within a Javadoc comment, and the location of the comment (class, method, or member variable) determines the level of the annotation. Here is a class-level XDoclet annotation:

 /**  * Class: MyNeatServlet  * @author Jim Farley  *  * @web.servlet  *     name="neat-servlet"  */ public class MyNeatServlet extends HttpServlet {    . . . 

In this case, the annotation tag group is web, and the tag name is servlet. The annotation has a single parameter, name, with a value of neat-servlet. The web.servlet annotation applies at the class level because it sits within a class-level Javadoc comment.

A method annotation would look like this:

 public class MyDataObject {     . . .     /**      * Description property      *      * @hibernate.property      *     column="DESC"      */     public String getDescription(  ) { . . . }     . . . } 

The hibernate.property annotation sits within a Javadoc comment for the getdescription( ) method, so it applies to that method.

A member-level (also called a field-level) annotation is not as common in XDoclet but still useful in some situations. A member annotation sits in a member-level Javadoc comment, as shown in this example:

 public class MyEJB extends EntityBean {     . . .     /**      * @ejb.resource-ref res-name="jdbc/MyDBPool"      */     protected DataSource mDBPool = null;     . . . } 

21.4.2. Processing XDoclet Annotations

The only way to process XDoclet annotations right now is to use the provided XDoclet Ant tasks in an Ant buildfile. Each XDoclet Ant task is intended for a particular development context and processes a targeted subset of the available annotations. The webdoclet Ant task, for example, is aimed at developing various types of web components (servlets, JSPs, custom JSP tags) and processes annotations from the web group and a few related annotation groups. The ejbdoclet task, on the other hand, is for EJB component development and can process annotations in the ejb and related annotation groups.

More complete details on the general topic of writing Ant buildfiles can be found in Chapter 17. Here, we'll just look at the basics of how to import the XDoclet tasks into your buildfile. In the following sections, we'll take a look at a few key scenarios to demonstrate how the XDoclet Ant tasks are used to process some of the commonly used annotations.

Adding the XDoclet tasks to your buildfile is the same as adding any other custom Ant tasks. You use the taskdef task to define a new task that is implemented by classes in the XDoclet libraries. The dependent classes for the task are specified using a nested <classpath> element. In Example 21-1, we "import" the webdoclet task from the XDoclet libraries.

Example 21-1. Importing the webdoclet task into an Ant buildfile
 <property name="xdoclet.home" value="/usr/local/xdoclet-1.2.3"/> <!-- Include the XDoclet webdoclet Ant task --> <taskdef name="webdoclet"          classname="xdoclet.modules.web.WebDocletTask">     <classpath>         <path>             <fileset dir="${xdoclet.home}/lib">                 <include name="*.jar"/>             </fileset>         </path>         <path ref/>     </classpath> </taskdef> 

We're using an xdoclet.home property to point to the root of the XDoclet installation, just as a convenience. In the taskdef task, we specify that we're defining a task named webdoclet and that it is implemented by the class xdoclet.modules.web.WebDocletTask. This class will be located in the <classpath> we define within the taskdef. In our <classpath>, we're including two sets of <path> elements. First, we're adding all of the XDoclet jars using a fileset that includes any file ending in .jar in the XDoclet lib directory. Following this, we add the <classpath> named java.compile.classpath (whose definition is not shown here). The <classpath> that's specified in the taskdef will be used whenever the task is used in the buildfile. In the case of the XDoclet webdoclet task, the <classpath> has to include all of the web component librariesthe servlet and JSP libraries at a minimum, possibly others depending on the webdoclet options you use. The java.compile.classpath path used here must include the jars for the servlet and JSP packages, pulled from whatever application server we're using. Note that you can also set the classpath for the task at the point in the buildfile where you actually use the task. Setting the classpath here in the taskdef can be convenient when the imported tasks will be used several times in the buildfile with the same base classpath.

To add other XDoclet tasks to our buildfile, we would repeat these previous steps above, specifying different task names and implementation classes. The ejbdoclet task definition would be imported as shown in Example 21-2. In this case, since the ejbdoclet task will need to have access to the EJB libraries (javax.ejb, etc.), we'll need to ensure that these classes are included in the java.compile.classpath <path> element.

Example 21-2. Importing the ejbdoclet task into an Ant buildfile
 <!-- Include the XDoclet ejbdoclet Ant task --> <taskdef name="ejbdoclet"          classname="xdoclet.modules.ejb.EjbDocletTask">     <classpath>         <path>             <fileset dir="${xdoclet.home}/lib">                 <include name="*.jar"/>             </fileset>         </path>         <path ref/>     </classpath> </taskdef> 

Once the tasks have been defined and imported into the buildfile, you can use them like other Ant tasks, nested within your build targets. In the following example, we use the webdoclet task inside of a create-war target to generate a web.xml deployment descriptor from the source code before assembling the web archive (war) file:

 <target name="create-war">     <!-- Generate a web.xml file using annotations on the          web components -->     <webdoclet destDir="${web.dir}">         <fileset dir="${src.dir}">             <include="**/web/*Servlet.java"/>         </fileset>         <deploymentdescriptor             servletspec="2.4"             destinationFile="web.xml"/>     </webdoclet>       <!-- Assemble the classes, deployment descriptor, etc.          into a web archive, using the Ant built-in war task -->     <war warfile="myapp.war" webxml="${web.dir}/web.xml">         . . .     </war> </target> 

Aside from the general details of invoking the webdoclet Ant task, you should also note here that the war task that follows the webdoclet task is reading in the web.xml deployment descriptor being generated by the webdoclet task. The deployment descriptor is being generated directly from annotations found in the servlet source code. This ability to extract key code artifacts directly from source code annotations is an important benefit provided by XDoclet and other annotation-driven facilities.

21.4.3. Generating Deployment Descriptors

We just saw an example of generating a web deployment descriptor using XDoclet , but now let's look in more detail at using XDoclet to assist in the generation of deployment descriptors for your J2EE components.

21.4.3.1. "Pure" annotation-driven deployment descriptors

The most straightforward approach is to have the entire deployment descriptor generated entirely from code annotations. In this case, the code annotations include all of the information to be placed in the deployment descriptor. If we wanted to generate a web.xml deployment descriptor for our application's web components, for example, we would use annotations in the web group in XDoclet and place them as needed in the web component code.

As a simple example, suppose we had a PeopleFinderServlet that we wanted to deploy using a single URL mapping. First, we'd need to let XDoclet know that our class is a servlet, using the web.servlet annotation, and we would use the web.servlet-mapping annotation to indicate how to map the deployed web component to a URL. Example 21-3 shows the annotated source code for the PeopleFinderServlet class.

Example 21-3. Annotated servlet code
 /**  * Class: PeopleFinderServlet  *  * @author <a href="mailto:jim@jimfarley.org">Jim Farley</a>  *  * XDoclet tags:  *  * @web.servlet  *     name="PeopleFinderServlet"  *     display-name="PeopleFinder Servlet"  *  * @web.servlet-mapping  *     url-pattern="/pf"  *  */ public class PeopleFinderServlet extends HttpServlet {     // Servlet implementation details omitted     . . . } 

In the web.servlet annotation, we're indicating some minimal information about the web componenta component name and display name to be used in the deployment descriptor. In the web.servlet-mapping annotation, we indicate that we want the component mapped to the /pf URL within the application's overall web context.

We would process this annotated source in our Ant buildfile using the webdoclet task, as shown here:

 <target name="create-war">     <!-- Generate a web.xml file using annotations on the          web components -->     <webdoclet destDir="${web.dir}">         <fileset dir="${src.dir}">             <include="**/PeopleFinderServlet.java"/>         </fileset>         <deploymentdescriptor             servletspec="2.4"             destinationFile="web.xml"/>     </webdoclet>     . . . </target> 

This example is very similar to our previous Ant example, except that we're specifically processing the PeopleFinderServlet source file, rather than all source files whose class names end in Servlet. We are trying to generate only a web.xml deployment descriptor, so we use the <deploymentdescriptor> child element of the webdoclet task. In the <deploymentdescriptor> element, we use the servletspec attribute to indicate the version of the servlet specification that our application server supports. This will influence the DTD/schema that XDoclet references in the generated deployment descriptor and in some cases the structure of the generated XML itself (though not in this simple example). The destinationFile attribute indicates where we want the deployment descriptor file deposited.

If we ran this task on the source code shown in Example 21-3, XDoclet would generate the deployment descriptor shown in Example 21-4. This is a complete and valid deployment descriptor that we could use to deploy the PeopleFinderServlet to a Servlet 2.4 web container.

Example 21-4. XDoclet-generated web deployment descriptor
 <?xml version="1.0" encoding="UTF-8"?>   <web-app xmlns="http://java.sun.com/xml/ns/j2ee"          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"          xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee                             http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"          version="2.4">    <distributable/>      <!-- XDoclet comments omitted... -->      <servlet>       <display-name>PeopleFinder Servlet</display-name>       <servlet-name>PeopleFinderServlet</servlet-name>       <servlet-class>           com.oreilly.jent.people.annotation.PeopleFinderServlet       </servlet-class>    </servlet>    <servlet-mapping>       <servlet-name>PeopleFinderServlet</servlet-name>       <url-pattern>/pf</url-pattern>    </servlet-mapping>   </web-app> 

The generated web.xml file properly references the XML schema for the Servlet 2.4 deployment descriptor, and all of the deployment information that we placed in our code annotations has been placed into its proper locations in the web.xml file. And if we change the source code, we have the annotations right there in the code and we can keep them up-to-date as the code changes. The web.xml file is automatically regenerated each time we run the appropriate task in our Ant buildfile.

Many application servers require a supplementary deployment descriptor or other configuration file in order to specify server-specific deployment details (see Chapter 2 for more information about deploying applications). XDoclet can generate these descriptors as well for a set of supported application servers. If we're using JBoss 4.0 to run our web components, for example, we can ask XDoclet to generate a JBoss web deployment descriptor by adding the <jbosswebxml> element to our webdoclet task:

 <target name="create-war">     <!-- Generate a web.xml file using annotations on the          web components -->     <webdoclet destDir="${web.dir}">         <fileset dir="${src.dir}">             <include="**/PeopleFinderServlet.java"/>         </fileset>         <deploymentdescriptor             servletspec="2.4"             destinationFile="web.xml"/>         <jbosswebxml             version="4.0"             destinationFile="jboss-web.xml"/>     </webdoclet>     . . . </target> 

In the <jbosswebxml> subelement, we're specifying that we want a deployment file for a JBoss 4.0 server and that we want the file saved to the file jboss-web.xml. If we ran this modified version of our create-war target on the source code in Example 21-3, the result would be the following JBoss web deployment file:

 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE jboss-web     PUBLIC "-//JBoss//DTD Web Application 2.4//EN"            "http://www.jboss.org/j2ee/dtd/jboss-web_4_0.dtd"> <jboss-web>     <!-- XDoclet comments omitted --> </jboss-web> 

There's nothing interesting here because there are no details in our code annotations that need to be reflected in a JBoss deployment file. XDoclet has a set of server-specific annotations that can be used to generate these deployment files as well. If, for example, our web component required a resource reference to a DataSource, we could add annotations to our servlet class to indicate the resource reference needed in the web.xml file as well as the entry to be placed in the JBoss web deployment file. The servlet code with the additional annotations is shown here:

 /**  * @web.servlet  *     name="PeopleFinderServlet"  *     display-name="PeopleFinder Servlet"  *  * @web.servlet-mapping  *     url-pattern="/pf"  *  * @web.resource-ref  *     name="jdbc/MyDataSource"  *     type="javax.sql.DataSource"  *     auth="Container"  *  * @jboss.resource-ref  *     res-ref-name="jdbc/MyDataSource"  *     jndi-name="jdbc/DataSourceX"  *  */ public class PeopleFinderServlet extends HttpServlet {     . . . } 

The web.resource-ref annotation is used to specify the resource reference required by the web component. In this case, we're declaring that a DataSource is needed at jdbc/MyDataSource in the JNDI namespace. The jboss.resource-ref annotation indicates how this resource reference should be handled in the JBoss web deployment file. In this case, we're declaring that the jdbc/MyDataSource JNDI reference should be linked to the jdbc/DataSourceX JNDI resource on the JBoss server.

If we run our earlier Ant target again using these updated annotations, our web.xml file will now look like this:

 <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/j2ee"          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"          xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee                             http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"          version="2.4">    <distributable/>    <servlet>       <display-name>PeopleFinder Servlet</display-name>       <servlet-name>PeopleFinderServlet</servlet-name>       <servlet-class           >com.oreilly.jent.annotation.web.PeopleFinderServlet       </servlet-class>    </servlet>      <servlet-mapping>       <servlet-name>PeopleFinderServlet</servlet-name>       <url-pattern>/pf</url-pattern>    </servlet-mapping>      <resource-ref>       <res-ref-name>jdbc/MyDataSource</res-ref-name>       <res-type>javax.sql.DataSource</res-type>       <res-auth>Container</res-auth>    </resource-ref> </web-app> 

The new resource-ref enTRy in web.xml was generated because of the new web.resource-ref annotation we added to the source file. Similarly, the new jboss.resource-ref annotation we added to the source file will be reflected in the new JBoss web deployment file:

 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE jboss-web     PUBLIC "-//JBoss//DTD Web Application 2.4//EN"            "http://www.jboss.org/j2ee/dtd/jboss-web_4_0.dtd"> <jboss-web>    <resource-ref>       <res-ref-name>jdbc/MyDataSource</res-ref-name>       <jndi-name>jdbc/DataSourceX</jndi-name>    </resource-ref> </jboss-web> 

21.4.3.2. Mixed models: Deployment using annotations and external data

The preceding annotation examples work very well when we are deploying a set of components in a specific environment, where the deployment details do not vary. But suppose we handed this system over to another developer working in a different environment. The other environment might require different deployment details: modified URL mappings for the web components, different resource names on the application server, and perhaps even a different application server altogether with its own extended deployment files.

In a situation like this, it would be bad practice to have to change the source code of the application in order to deploy it. Even though you are modifying "only" annotations and those annotations are inside of comments that shouldn't affect compiled code behavior, some basic principles still apply: source code should encode the behavior of the system globally, and anything that needs to be localized to a particular environment should happen in externalized configuration files and build scripts that are distinctly separate from the source code.

Thankfully, XDoclet recognizes the need, in some cases, for having some deployment data reside in external sources (external to the source code, that is). It supports this practice in two ways. Deployment data sitting in external files can be merged with the deployment data generated from annotations, using the mergeDir options on the XDoclet Ant tasks. In addition, XDoclet annotations can use Ant property names to parameterize the values of their properties. This allows you to put deployment parameters into the Ant buildfile (or in separate properties files loaded into the Ant buildfile), and they will be automatically applied when the parameterized annotation is processed.

As an example of using the merged deployment descriptor approach, suppose we have a situation in which the URL mapping of particular web components needs to be set on a per-environment basis. Perhaps other web applications are being deployed in these environments and in some cases the default URL mappings are conflicting. We could alter our servlet example to remove the URL mapping annotation and instead use a separate XML file to provide the URL mappings for the web components. The <deploymentdescriptor> subelement on the webdoclet task supports the use of a mergeDir attribute. When annotated files are being processed for deployment-related annotations, the webdoclet processor will check this directory for specific XML files containing segments of the deployment descriptor to be merged together with the generated deployment data to produce the final web.xml file. In our case, we want to provide servlet mappings in an external file, and webdoclet expects these to be in a file named servlet-mappings.xml. So we would make a servlet-mappings.xmlfile in our mergeDir that looks like this:

 <servlet-mapping>     <servlet-name>PeopleFinderServlet</servlet-name>     <url-mapping>/my-pf-context</url-mapping> </servlet-mapping> 

Assuming that this file sits in a directory called config/xdoclet-merge in our project area, then we would modify our call to the webdoclet task in our Ant file to look like this:

 <webdoclet destDir="${web.dir}">     <fileset dir="${src.dir}">         <include name="**/PeopleFinderServlet.java"/>     </fileset>     <deploymentdescriptor         servletspec="2.4"         mergeDir="config/xdoclet-merge"         destinationFile="web.xml"/> </webdoclet> 

If we ran this task on the annotated code shown in Example 21-3, the resulting web.xml file would look like the following:

 <?xml version="1.0" encoding="UTF-8"?> <web-app     xmlns="http://java.sun.com/xml/ns/j2ee"     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee                         http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"     version="2.4">    <distributable/>    <servlet>       <display-name>PeopleFinder Servlet</display-name>       <servlet-name>PeopleFinderServlet</servlet-name>       <servlet-class           >com.oreilly.jent.annotation.web.PeopleFinderServlet</servlet-class>    </servlet>   <servlet-mapping>     <servlet-name>PeopleFinderServlet</servlet-name>     <url-mapping>/my-pf-context</url-mapping> </servlet-mapping>    <servlet-mapping>       <servlet-name>PeopleFinderServlet</servlet-name>       <url-pattern>/pf</url-pattern>    </servlet-mapping> </web-app> 

The highlighted servlet mapping came from the servlet-mappings.xml file while the rest of the web.xml file came from processing the annotations in the source code. The webdoclet task supports a specific XML merge file for each section of a web deployment descriptor: servlets, filters, listeners, resource references, and so on. You can decide in your context what deployment information is truly universal and belongs in the source code and what needs to be localized to the environment and therefore placed in one of these externalized XML fragments.

XDoclet also allows you to use Ant properties to parameterize your annotations. This applies to any XDoclet annotations, not just the web-related ones we're showing here. In our case, rather than using an external, merged XML file to specify a localized servlet mapping, we could place the URL mapping value in our Ant buildfile, as the value of a property called pf.servlet.context:

 <property name="pf.servlet.context" value="/pf-ant-param"/> <webdoclet destDir="${generated.web.dir}">     <fileset dir="${java.src.dir}">         <include name="**/PeopleFinderServlet.java"/>     </fileset>     <deploymentdescriptor         servletspec="2.4"         destinationFile="web.xml"/> </webdoclet> 

We could then reference this Ant parameter in our code annotation:

 /**  * @web.servlet  *     name="PeopleFinderServlet"  *     display-name="PeopleFinder Servlet"  *  * @web.servlet-mapping  *     url-pattern="${pf.servlet.context}"  *  */ public class PeopleFinderServletParam extends HttpServlet {     . . . } 

Our web.xml file will now be generated with the value of the pf.servlet.context Ant property injected. This value will be read at the time the XDoclet Ant task is run, so in our case the web.xml file will end up looking like this:

 <?xml version="1.0" encoding="UTF-8"?> <web-app     xmlns="http://java.sun.com/xml/ns/j2ee"     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee                         http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"     version="2.4">    <distributable/>    <servlet>       <display-name>PeopleFinder Servlet</display-name>       <servlet-name>PeopleFinderServlet</servlet-name>       <servlet-class>PeopleFinderServlet</servlet-class>    </servlet>      <servlet-mapping>       <servlet-name>PeopleFinderServlet</servlet-name>       <url-pattern>/pf-param</url-pattern>    </servlet-mapping> </web-app> 

21.4.4. Generating Source Code

Generating documentation, deployment files, and general configuration files from code annotations can be a powerful tool, as we've just seen. But some code annotations provided with XDoclet actually generate other Java source code. We'll take a look at two specific examples: generating support interfaces for SOAP web services and generating client and home interfaces for EJB components.

21.4.4.1. Web services

As we discussed in our web services coverage in Chapter 12, a SOAP service can be implemented in JAX-RPC using a simple RMI programming model. We won't review all the details of writing JAX-RPC web services here (refer to Chapter 12 for those details). We'll just stick to the basics here to demonstrate how annotations can help with code generation.

With JAX-RPC, a Remote interface is used to declare the methods that will be mapped into operations on the SOAP service, and a concrete Java class provides the implementations of these methods. There is a direct relationship between the implementation methods and the declared methods on the Remote interface, so it can be useful to have the Remote interface automatically generated from the implementation class. Otherwise, each time you adjust the implementation class (change the signature of a web service method or add a new service method), you need to manually update the corresponding Remote interface to keep the two in sync.

XDoclet supports this with the web group of annotations in combination with the service-endpoint subtask of the webdoclet Ant task. Example 21-5 shows a modified version of one of the JAX-RPC SOAP service implementations we looked at in Chapter 12, PeopleFinderServiceImpl. Notice that we've added a web.servlet annotation at the class level and a web.interface-method annotation on each method that we want exposed in the SOAP service interface.

Example 21-5. Annotated JAX-RPC service implementation
 /**  * PeopleFinderImpl: Implementation of our remote PeopleFinder interface  *  * XDoclet tags:  *  * @web.servlet  *     name="PeopleFinderService"  */ public class PeopleFinderServiceImpl {     public PeopleFinderImpl(  ) throws RemoteException {}       /**      * @web.interface-method      */     public Person[] findPeople(SearchArg[] args)         throws InvalidSearchException, PersistenceException,                 RemoteException {         . . .     }       /**      * @web.interface-method      */     public Person findPerson(String id)        throws InvalidSearchException, PersistenceException,               RemoteException {       . . .     }       /** Utility method for retrieving a database connection */     private Connection getConnection(  ) {         . . .     } } 

To process these annotations, we still use the same webdoclet Ant task we used earlier to generate web deployment descriptors. In this case, though, we want to generate source code related to our web service implementation. So we use a different subtask to the webdoclet task, service-endpoint, as shown in Example 21-6. The service-endpoint subtask is used to generate JAX-RPC service interfaces for any classes annotated as web components with the web.servlet tag that also have one or more methods annotated as web service operations using the web.service-interface tag.

Example 21-6. Using the webdoclet task to generate JAX-RPC interfaces
 <target name="create-ws">     <webdoclet destDir="${ws.src.dir}">         <fileset dir="${src.dir}">             <include name="**/PeopleFinderServiceImpl.java"/>         </fileset>         <service-endpoint/>     </webdoclet> </target> 

If we run this Ant target on the source code in Example 21-5, the service interface shown in Example 21-7 will be generated into the ws.src.dir directory.

Example 21-7. Generated web service interface
 /*  * Generated by XDoclet -- Do not edit!  */   /**  * service_endpoint_interface_for arguments: [Ljava.lang.String;@35b92e  */ public interface PeopleFinderService    extends java.rmi.Remote{    public Person[] findPeople( SearchArg[] args )       throws InvalidSearchException, PersistenceException,              java.rmi.RemoteException;      public Person findPerson( java.lang.String id )       throws InvalidSearchException, PersistenceException,              java.rmi.RemoteException; } 

Notice that the class name for the generated service interface was taken from the name parameter provided on the web.servlet annotation. The two methods on the interface are the two marked with the service-interface tag in the source. Notice as well that the getConnection( ) method on the service implementation was not mapped to the generated interface because it was not annotated.

21.4.4.2. EJBs

Many developers are put off by the complexity of writing EJBs because of the many code artifacts that have to be generated. As described in Chapter 6, an EJB component can include one or more home interfaces, one or more client interfaces, a bean implementation class, and a custom primary key class (for certain entity bean situations) in addition to the deployment descriptor. Since all of these artifacts have specific relationships with one another (defined by the EJB specification), this seems like an ideal context for code annotations to help out. In fact, XDoclet began as a tool to assist in the development of EJBs and has since grown and been generalized to many other contexts, some of which we've already explored.

The ejb tag group in XDoclet contains a set of EJB-oriented annotations. Many of these are meant to generate deployment descriptor information, similar to the web annotations we saw in the previous section. But here we will take a look at using annotations to generate some of the actual source code that makes up an EJB component.

Example 21-8 shows an annotated version of the implementation class for the ProfileManager EJB discussed in Chapter 6. In the class-level comments, we've inserted an ejb.bean annotation indicating that the name of the EJB should be ProfileManager and that it is a stateless session bean. This EJB implementation has a single create method, so we've annotated that method with an ejb.create-method tag. Finally, the bean has a single business method, getProfile( ), and we've annotated that with an ejb.interface-method tag. On this tag, we've set the view-type property to both to indicate that we want this business method to be exposed in both the remote and local client interfaces for this EJB, if either or both are generated.

Example 21-8. Annotated EJB implementation class
 /**  * @ejb.bean  *     name="ProfileManager"  *     type="Stateless"  */   // Imports omitted...   public class ProfileManagerBean implements SessionBean {     // Store session context     private SessionContext mContext = null;       public ProfileManagerBean(  ) {}       /** Activate notification method */     public void ejbActivate(  ) {}       /** Remove notification method */     public void ejbRemove(  ) {}       /** Passivation notification method */     public void ejbPassivate(  ) {}       /** Container session context accessor */     public void setSessionContext(SessionContext context) {         mContext = context;     }       /**      * No-argument create(  ) -- nothing to initialize in this case.      *      * @ejb.create-method      */     public void ejbCreate(  ) {}       /**      * Get a profile for a named person      *      * @ejb.interface-method      *     view-type=both      */     public ProfileBean getProfile(String name)         throws NoSuchPersonException {         ProfileBean profile = new ProfileBean(name);         return profile;     } } 

We use the same general approach to process these annotations as with any other XDoclet annotations: we run the appropriate XDoclet Ant task over the source code. In this case, we're dealing with EJBs and tags from the ejb group, so we use the ejbdoclet task rather than the webdoclet task we've been using so far. A sample target that makes use of the ejbdoclet task to process our ProfileManagerBean source code is shown in Example 21-9.

Example 21-9. Ant target for processing EJB annotations
 <target name="gen-ejb-code">     <!-- Generate EJB home and client interfaces from an          annotated bean implementation class -->     <ejbdoclet destDir="${generated.ejb.dir}"                ejbSpec="2.1">         <fileset dir="${java.src.dir}">             <include name="**/ProfileManagerBean.java"/>         </fileset>         <remoteinterface/>         <localinterface/>         <homeinterface/>         <localhomeinterface/>     </ejbdoclet> </target> 

Since our goal here is to generate support source code, we've used only subtasks of the ejbdoclet task related to generating code. The <remoteinterface>, <localinterface>, <homeinterface>, and <localhomeinterface> elements are included to indicate that we want these respective pieces of code generated for us from the annotations found in the source files included in the <fileset>.

If we run this target against the annotated source code in Example 21-8, XDoclet will generate remote and local client interfaces for our EJB (named ProfileManager and ProfileManagerLocal) and remote and local home interfaces (named ProfileManagerHome and ProfileManagerLocalHome). The names of these interfaces are generated automatically, based on the name parameter provided on the ejb.bean annotation. We could have specified the names of these interfaces using additional annotations in the source, but, for this example, the automatically generated interface names are fine.

The generated remote client interface, ProfileManager, is shown in Example 21-10, and the remote home interface, ProfileManagerHome is shown in Example 21-11. The local versions are nearly identical, so we won't show them here. Notice that the ProfileManager client interface contains a declaration for the getProfile( ) method that was annotated with an ejb.interface-method tag in the source for the implementation class. Also notice that the ProfileManagerHome home interface contains a declaration for a single no-argument create( ) method because we annotated the corresponding ejbCreate( ) method in the bean class with an ejb.create-method tag.

Example 21-10. Generated remote client interface
 /*  * Generated by XDoclet -- Do not edit!  */   /**  * Remote interface for ProfileManager.  */ public interface ProfileManager    extends javax.ejb.EJBObject{    public ProfileBean getProfile( java.lang.String name )       throws NoSuchPersonException, java.rmi.RemoteException; } 

Example 21-11. Generated remote home interface
 /*  * Generated by XDoclet -- Do not edit!  */   /**  * Home interface for ProfileManager  */ public interface ProfileManagerHome    extends javax.ejb.EJBHome{    public static final String COMP_NAME="java:comp/env/ejb/ProfileManager";    public static final String JNDI_NAME="ProfileManager";      public ProfileManager create(  )       throws javax.ejb.CreateException,java.rmi.RemoteException; } 

You'll also notice a few static String members defined in the remote home interface. These variables were generated from the name parameter on the ejb.bean annotation and represent default JNDI mappings that XDoclet will generate for the EJB in any application server configuration files we ask it to generate. So these static variables are a convenience for any client code that makes use of this EJB component.

Keep in mind that we are showing you only a small part of the options available in these XDoclet annotations and Ant tasks. You can, for example, generate a deployment descriptor for the EJB component as well by just adding a deploymentdescriptor subtask to the ejbdoclet task:

 <target name="gen-ejb-code">     <ejbdoclet destDir="${ejb.dir}"                ejbSpec="2.1">         <fileset dir="${src.dir}">             <include name="**/ProfileManagerBean.java"/>         </fileset>         <homeinterface/>         <localhomeinterface/>         <remoteinterface/>         <localinterface/>         <deploymentdescriptor/>     </ejbdoclet> </target> 

Running this version of the gen-ejb-code target will generate the same client and home interfaces as before and also the EJB deployment descriptor shown in Example 21-12.

Example 21-12. Generated EJB deployment descriptor
 <?xml version="1.0" encoding="UTF-8"?>   <ejb-jar xmlns="http://java.sun.com/xml/ns/j2ee"          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"          xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee                             http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd"          version="2.1">    <description><![CDATA[No Description.]]></description>    <display-name>Generated by XDoclet</display-name>      <enterprise-beans>         <!-- Session beans -->       <session >          <description><![CDATA[]]></description>            <ejb-name>ProfileManager</ejb-name>            <home>ProfileManagerHome</home>          <remote>ProfileManager</remote>          <local-home>ProfileManagerLocalHome</local-home>          <local>ProfileManagerLocal</local>          <ejb-class>ProfileManagerBean</ejb-class>          <session-type>Stateless</session-type>          <transaction-type>Container</transaction-type>         </session>       <!-- Various XDoclet comments omitted... -->    </enterprise-beans>    <!-- Various XDoclet comments omitted... --> </ejb-jar> 

21.4.5. Other Annotations

XDoclet has many other uses that we haven't seen in our examples. Just in the web and ejb tag groups are many other annotations and parameters on these annotations that you can use to generate all the other parts of their respective deployment descriptors as well as configuration files for other application servers and other code artifacts.

But there are a number of other tag groups you may find useful, depending on what kinds of enterprise development you are doing. We mention a few key ones here:


@wsee

This tag group contains annotations that can be used to generate JAX-RPC namespace mapping files for web services. This can be as useful as generating deployment descriptors for J2EE components, allowing you to keep your mapping file in sync with the source code as it evolves. There is a wseedoclet Ant task that can be used to process these annotations.


@hibernate

This set of annotations can be used to mark up data objects, allowing you to generate the Hibernate mapping files and configuration files for a set of JavaBeans that you want to persist using Hibernate. The hibernatedoclet Ant task is used to process these annotations.


@jsf

These annotations can be used to mark up JavaServer Faces code, such as managed JavaBeans, validators, and converters, and use these annotations to generate JSF configuration files. These annotations are processed using the webdoclet task by including the facesconfigxml subtask.

We'd encourage you to explore the XDoclet documentation to learn about some of the other capabilities that might be useful to you.

21.4.6. Custom XDoclet Annotations

It is possible to write your own XDoclet annotations, if you need to do that. Some reasons you may feel the need to define your own annotations might include:

  • Custom configuration files that you've defined in your application

  • Design patterns that you've adopted that require support code that can be generated from base classes plus some annotations

  • Third-party libraries that you are using to implement key service interfaces in your system, where these implementation classes can be autogenerated from annotations placed in the interfaces

The details of writing custom XDoclet tags and processors is beyond the scope of this chapter, but it essentially boils down to writing a template file to specify the structure of the output you want generated from your code annotations, or writing a custom tag handler class that will be invoked at runtime when one of your tags is encountered. It's also possible to define your own custom Ant task for processing your tags, although in many cases, it's possible to just use the generic doclet Ant task provided with XDoclet to process a set of code using a template file you provide. For more details, consult the XDoclet developer's documentation.

21.4.7. A Word of Caution

As we mentioned earlier in the chapter, you need to temper your use of code annotations with some practical considerations. Deployment descriptors, configuration files, and other code artifacts are often separate from the source code for a good reason. Some configuration parameters need to be localized to the target deployment environment, for example, and if you have several environments to deal with (development, test, and production; developers A, B, and C; or clients X, Y, and Z), you don't want to be forced to edit the source code to deploy your system across these environments. Make use of Ant properties to parameterize your annotations, or make use of externalized configuration file fragments, as we demonstrated in our web deployment descriptor examples. These practices, in conjunction with the general Ant localization tactics discussed in Chapter 17, should give you all the power you need to both take advantage of code annotations yet maintain the ability to easily deploy your applications in various environments.



Java Enterprise in a Nutshell
Java Enterprise in a Nutshell (In a Nutshell (OReilly))
ISBN: 0596101422
EAN: 2147483647
Year: 2004
Pages: 269

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net