Accélérer le développement grâce à XDoclet
Ce post a pour but de montrer comment accélérer le développement d’applications J2ee grace a l’utilisation de XDoclet.
XDoclet et un generateur de code et vient avec une myriade de modules (ejb, struts, orion, jboss…) et vous permet d’accélérer de facon significative le cycle de developpement en vous evitant d’ecrire a la main toute une palanquée de fichiers (web.xml, ejb-jar.xml, struts-config.xml, applicationContext.xml pour SPring, et j’en passe)
XDoclet est construit au dessus du moteur de Javadoc et fonctionne avec les annotations ecrites dans le code source de vos programmes.
Dans cet article je vais montrer trois exemples :
- Generation du fichier web.xml pour une WebApp J2EE
- Generation d’un fichier applicationContext.xml dans le cas d’une WebApp developpée avec Spring
- Enfin, pour les EJB, generation d’interfaces et du fichier ejb-jar.xml
1) Prerequis:
a) Telecharger XDoclet : aller le chercher chez Sourceforge (http://xdoclet.sourceforge.net/xdoclet/index.html)
Je precise qu’une version 2 de XDoclet existe : c’est une refonte du moteur et elle est dispo chez Codehaus (http://xdoclet.codehaus.org/)
Pour l’exemple nous prendrons XDoclet 1 …je jouerai mes tests sur XDoclet 2 et preciserai ulterieuremet si cela fonctionne.
b) Telecharger Ant si vous ne l’avez pas encore (http://ant.apache.org/)
c) Se creer un projet dans votre IDE favori (Eclipse et IntelliJ Idea en ce qui me concerne)
d) Preparer le fichier build.xml pour Ant pour inclure les jars de XDoclet :
<?xml version=”1.0″?>
<project name=”XDocletDemo” default=”gen-web” basedir=”.”>
<property name=”root.dir” value=”${basedir}”/>
<property name=”src.dir” value=”src”/>
<property name=”web” value=”${root.dir}/public_html”/>
<property name=”webinf.dir” value=”${web}/WEB-INF”/>
<property name=”lib” value=”${web}/WEB-INF/lib”/>
<property name=”app.name” value=”xdoclet-demo”/>
<property name=”xdoclet.lib.dir” value=”/path/to/xdoclet-1.2.3/lib” />
<property name=”gen-src.dir” value=”${src.dir}/gen-src”/>
<property name=”gen-src.ejb.interfaces.dir” value=”${gen-src.dir}/ejb/interfaces”/>
<property name=”gen-src.ejb.meta-inf.dir” value=”${gen-src.dir}/META-INF”/>
<property name=”gen-src.web-inf.dir” value=”${gen-src.dir}/WEB-INF”/>
<!– Prepare: Creates the output directories –>
<target name=”prepare”>
<!– Create for interface generated source –>
<mkdir dir=”${gen-src.dir}”/>
</target>
<!– ====================================== –>
<path id=”xdoclet.classpath”>
<fileset dir=”${xdoclet.lib.dir}/”>
<include name=”*.jar”/>
</fileset>
<fileset dir=”${jboss.home}/server/default/lib”>
<include name=”jboss-j2ee.jar”/>
</fileset>
</path>
…..
2) Generation de web.xml avec XDoclet:
a) Creer dans votre repertoire source une servlet d’exemple et ajouter les annotations pour XDoclet.
La syntaxe des annotations est la suivante et doivent figurer dans une section de commentaire juste avant la declaration de la classe :
/**
* @namespace.tag-name attribute-name=”attribute value”
*/
Pour la generation de web.xml il faut consulter les tags reauis pour le namespace web : aller à cette URL
La servlet sera donc la suivante :
package com.lbois.web;
import java.io.*;
import javax.servlet.http.*;
import javax.servlet.*;
/**
* Hello world servlet. Most servlets will extend
* javax.servlet.http.HttpServlet as this one does.
* @web.servlet
* name=”HelloWorldServlet”
* display-name=”HelloWorldServlet”
* description=”Hello World Session Bean”
* load-on-startup=”1″
*
* @web.servlet-mapping
* url-pattern=”/servlet”
*/
public class HelloWorldServlet extends HttpServlet {
/**
* Implements the HTTP GET method. The GET method is the standard
* browser method.
*
* @param request the request object, containing data from the browser
* @param repsonse the response object to send data to the browser
*/
public void doGet (HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
// Returns a writer to write to the browser
PrintWriter out = response.getWriter();
// Writes the string to the browser.
out.println(“Hello, world!”);
out.close();
}
}
Nous utilisons les tags @web.servlet et @web.servlet-mapping afin de generer
dans le fichier web.xml les sections :
<servlet>
<servlet-name>HelloWorldServlet</servlet-name>
<display-name>HelloWorldServlet</display-name>
<servlet-class>com.lbois.web.HelloWorldServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>HelloWorldServlet</servlet-name>
<url-pattern>/servlet</url-pattern>
</servlet-mapping>
b) Ajouter dans le fichier build.xml la tache Ant webdoclet (module de XDoclet utilise pour la generation Web) ; plus d’infos sur la tâche a cette adresse
<taskdef name=”webdoclet”
classname=”xdoclet.modules.web.WebDocletTask”
classpathref=”xdoclet.classpath” mce_href=”xdoclet.classpath” />
<target name=”gen-web” depends=”prepare”>
<webdoclet
destdir=”${gen-src.dir}”
excludedtags=”@version,@author”
verbose=”true” >
<fileset dir=”./${src.dir}”>
<include name=”**/*Servlet.java”/>
<include name=”**/*Filter.java”/>
<include name=”**/*Listener.java”/>
</fileset>
<deploymentdescriptor destdir=”${gen-src.web-inf.dir}”/>
</webdoclet>
</target>
Vous remarquerez que pour l’element <fileset…> j’inclue les servlets, les filtres et les listeners. Tout peut etre généré.
Pour l’exemple courant nous n’aurons qu’une servlet.
c) Executer la tâche gen-web
d) Le fichier web.xml est généré dans le répertoire ${gen-src.web-inf.dir}
Pour le reinclure ensuite dans votre projet (sous ${web-inf.dir} ) faire un simple copier/coller du fichier web.xml.
3) Generation de applicationContext.xml pour Spring.
Si vous etes familier avec Spring vous aurez entendu parler d’Ioc ou de dependency Injection.
Spring vous aide a l’instantiation des beans et a leur population en vous permettant de declarer vos beans dans un fichier XML (nomme par defaut applicationContext.xml)
a) Aller a l’adresse pour voir quels sont les tags disponibles pour le
namespace spring.
b) Ecrire un bean avec les annotations suivantes :
package com.lbois.common.dto;
import java.io.Serializable;
/**
*
* @spring.bean id=”employeeSales”
*
* @spring.property
* name=”empId”
* value=”1284″
*
* @spring.property
* name=”yearMonth”
* value=”1998-01″
*
* @spring.property
* name=”sales”
* value=”$8,115.36″
*/
public class EmployeeSales implements Serializable {
/**
*/
String empId;
String yearMonth;
String sales;
public String getEmpId() {
return empId;
}
public void setEmpId(String empId) {
this.empId = empId;
}
public String getSales() {
return sales;
}
public void setSales(String sales) {
this.sales = sales;
}
public String getYearMonth() {
return yearMonth;
}
public void setYearMonth(String yearMonth) {
this.yearMonth = yearMonth;
}
}
c) Le but est de generer le fichier applicationContext.xml suivant :
<beans
default-autowire=”no”
default-lazy-init=”false”
default-dependency-check=”none”
>
<bean
id=”employeeSales”
class=”com.lbois.common.dto.EmployeeSales”
>
<property name=”empId”>
<value>1284</value>
</property>
<property name=”yearMonth”>
<value>1998-01</value>
</property>
<property name=”sales”>
<value>$8,115.36</value>
</property>
</bean>
</beans>
d) Declarer la tache Ant dans votre fichier build.xml; plus d’infos à cette adresse.
<taskdef name=”springdoclet”
classname=”xdoclet.modules.spring.SpringDocletTask”
classpathref=”xdoclet.classpath” mce_href=”xdoclet.classpath” />
<target name=”gen-spring” depends=”prepare”>
<springdoclet
destdir=”${gen-src.web-inf.dir}”
excludedtags=”@version,@author”
verbose=”true” >
<fileset dir=”./${src.dir}”>
<include name=”**/EmployeeSales.java”/>
</fileset>
<springxml destinationFile=”applicationContext.xml” />
</springdoclet>
</target>
e) Le fichier applicationContext.xml est généré dans le répertoire ${gen-src.web-inf.dir}
Pour le reinclure ensuite dans votre projet (sous ${web-inf.dir} ) faire un simple copier/coller du fichier web.xml.
4) Génération de fichiers ejb-jar.xml et des interfaces:
a) Aller a l’adresse http://xdoclet.sourceforge.net/xdoclet/tags/ejb-tags.html pour voir quels sont les tags disponibles pour le namespace ejb.
b) Ecrire un session bean (pour l’exemple, peut etre fait avec des entity beans également) avec les annotations suivantes :
package com.lbois.server.ejb;
/**
* @ejb.home
* remote-pattern=”TradeManagerHome”
* local-pattern=”TradeManagerLocalHome”
* extends=”javax.ejb.EJBHome”
* local-extends=”javax.ejb.EJBLocalHome”
*
* @ejb.transaction type=”NotSupported”
*
* @ejb.interface
* remote-pattern=”TradeManager”
* local-pattern=”TradeManagerLocal”
* extends=”javax.ejb.SessionBean”
* local-extends=”javax.ejb.SessionBean, com.lbois.server.ejb.TradeManager”
*
* @ejb.env-entry
* name=”BeanFactoryPath”
* value=”applicationContext.xml”
*/
import java.math.BigDecimal;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import javax.ejb.CreateException;
public class TradeManager implements SessionBean {
/**
* Implémentation de la méthode de l’interface <code>SessionBean</code>.
* <br>Cette méthode est déclenchée par l’instantiation de l’EJB par le
* conteneur.
*
* @throws CreateException en cas d’erreur lors de la création.
*/
public void ejbCreate() throws CreateException {
}
/**
* Implémentation de la méthode de l’interface <code>SessionBean</code>.
* <br>Cette méthode est appelée lors de l’activation de l’EJB par le
* conteneur.
*/
public void ejbActivate() {
}
/**
* Implémentation de la méthode de l’interface <code>SessionBean</code>.
* <br>Cette méthode est appelée lors de la passivation de l’EJB par le
* conteneur.
*/
public void ejbPassivate() {
}
/**
* Implémentation de la méthode de l’interface <code>SessionBean</code>.
* <br>Cette méthode est appelée lors de la suppression de l’instance de
* l’EJB par le conteneur.
*/
public void ejbRemove() {
}
private SessionContext ctx;
public void setSessionContext(SessionContext ctx) {
this.ctx = ctx;
}
/**
* @ejb.interface-method view-type=”both”
*/
public BigDecimal getPrice(String symbol) {
return tradeManager.getPrice(symbol);
}
/**
* @ejb.interface-method view-type=”both”
*/
public void setPrice(String symbol, BigDecimal price) {
tradeManager.setPrice(symbol, price);
}
}
c) Le but est de generer les interfaces suivantes :
TradeManagerHome.java, TradeManagerLocal.java, TradeManagerLocalHome.java, TradeManagerRemote.java
et le fichier ejb-jar.xml suivant :
<?xml version = ‘1.0′ encoding = ‘UTF-8′?>
<!DOCTYPE ejb-jar PUBLIC “-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN” “http://java.sun.com/dtd/ejb-jar_2_0.dtd”>
<ejb-jar>
<description>No Description.</description>
<display-name>Generated by XDoclet</display-name>
<enterprise-beans>
<session>
<description>TradeManager</description>
<ejb-name>ejb/TradeManager</ejb-name>
<home>com.lbois.common.TradeManagerHome</home>
<remote>com.lbois.common.TradeManagerRemote</remote>
<local-home>com.lbois.common.TradeManagerLocalHome</local-home>
<local>com.lbois.common.TradeManagerLocal</local>
<ejb-class>com.lbois.server.ejb.TradeManager</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
</session>
…
</ejb-jar>
d) Ajouter la tache ejbdoclet au fichier build.xml ; plus d’infos à cette adresse.
<taskdef name=”ejbdoclet”
classname=”xdoclet.modules.ejb.EjbDocletTask”
classpathref=”xdoclet.classpath” mce_href=”xdoclet.classpath” />
<target name=”gen-ejb” depends=”prepare”>
<ejbdoclet
destdir=”${gen-src.dir}”
excludedtags=”@version,@author”
verbose=”true”
ejbspec=”2.0″>
<fileset dir=”./${src.dir}”>
<include name=”**/TradeManager.java”/>
</fileset>
<dataobject/>
<remoteinterface pattern=”{0}Remote”/>
<homeinterface pattern=”{0}Home”/>
<localinterface pattern=”{0}Local”/>
<localhomeinterface pattern=”{0}LocalHome”/>
<deploymentdescriptor destdir=”${gen-src.ejb.meta-inf.dir}”/>
</ejbdoclet>
</target>
e) Executer la tâche gen-ejb
f) Recopier les fichiers generes dans votre projet.
Cet article a donc permis de voir comment on peut se faciliter la vie avec XDoclet sur la génération de fichiers,
souvent sources d’erreurs quand faits à la main.
Ainsi, et c’est le cas des EJBs, on peut se concentrer sur la logique métier plutôt qu’écrire des fichiers
de configuration web.xml, ejb-jar.xml, applicationContext.xml ou des interfaces.
De plus ces fichiers sont écrits de manière répétitive d’un projet, donc avoir un outil de génération s’avère salvateur.

Hello Laurent, tu confirmes qu’avec EJB 3 et les annotations de java 5 on aura plus besoin de xDoclet ?
Je confirme. Les annotations du JDK 1.5 permettent de se passer de XDoclet.