Desarrollo Struts con XDoclet

1
40845

Como manejar Struts con Xdoclet

1. Introducción

Struts es un proyecto de apache que podemos encontrar en http://jakarta.apache.org/struts/index.html

Es un framework de desarrollo de aplicaciones Web. Este framework se basa en el patron MVC (Modelo Vista Controlador), con lo que nos permite separar perfectamente al lógica de negocio de la lógica de presentación, de la lógica de control.

Se configura a partir del fichero struts-config.xml. En este fichero es donde describimos, entre otras cosas, los formularios de nuestra aplicación, así como las acciones que pueden invocarse.

Los formularios corresponden con una clase. Típicamente un POJO, VO, o Bean, es decir una clase que sólo tiene atributos y métodos get y set. Las acciones corresponden con clases que extienden de org.apache.struts.action.Action. Sólo con esto ya podemos ver que el trabajo de mantenimiento va a ser doble: por un lado tendremos que mantener las clases, y por otro lado tendremos que mantener el fichero de configuración struts-config.xml.

Además del problema del mantenimiento tenemos otro problema añadido: el fichero struts-config.xml suele ser único (podemos dividirlo en módulos, pero el problema sólo se atenúa) con lo que su mantenimiento va a resultar engorroso incluso con un sistema de control de versiones (como CVS) ya que todos los desarrolladores tendrán que tocar el mismo fichero.

Para solucionar estos problemas podemos usar Xdoclet. Xdoclet es una herramienta que nos permite poner una serie de tags en los comentarios de nuestras clases y métodos, de forma que al correr Xdoclet se parsean estos tags y se genera algún tipo de salida. En nuestro caso, pondremos comentarios en las clases de acción y las clases de formulario y obtendremos de forma automática el fichero struts-config.xml y el fichero validation.xml (es donde definimos las validaciones automáticas de los formularios).

Esto es sólo un ejemplo de las múltiples cosas que se pueden hacer con Xdoclet. os recomiendo que os paséis por su página http://xdoclet.sourceforge.net/xdoclet/index.html
para echar un vistazo a todo su potencial.

2. Preparando Xdoclet para ejecutarlo con ant

Ant (http://ant.apache.org/) es una herramienta al estilo del make de C. Es decir nos va a permitir automatizar procesos de compilación, despliegue, copiado de ficheros, … La forma ideal de usar Xdoclet es integrándolo dentro de nuestros scripts ant para automatizar todo el proceso.

Dentro de nuestro fichero build.xml (por defecto es el fichero que ejecuta ant) tendremos que crear un nuevo target:

    <target name="webdoclet" depends="init">
        <taskdef name="webdoclet" classname="xdoclet.modules.web.WebDocletTask">
            <classpath>
                <path refid="xdoclet.classpath" />
            </classpath>
        </taskdef>

        <!-- Run the Ant build file with a parameter "-Dxdoclet.force=true" to force regeneration of files. -->
        <webdoclet destdir="${build.web.dir}/WEB-INF"
                   force="${xdoclet.force}"
                   mergedir="metadata">
            <fileset dir="src" />

            <strutsconfigxml version="1.1"
                xmlencoding="ISO-8859-1"
                validateXML="true"
                templateFile="metadata/struts/struts_config_xml.xdt"
                mergeDir="metadata/struts" />

            <strutsvalidationxml />
        </webdoclet>
    </target>

Como se ve en el ejemplo, primero se define la tarea “webdoclet” con taskdef. En este punto es importante destacar el uso de “xdoclet.classpath”, esta es una referencia al path donde están todos los .jar necesarios para que Xdoclet funcione.

Después ejecutamos la tarea que acabamos de crear (webdoclet). Con “destdir” indicamos el directorio donde se deben dejar los resultados. Con “fileset dir=’src’” indicamos donde se deben buscar los ficheros en los que se buscaran los tags de Xdoclet.

Con “strutsconfigxml” estamos indicando a Xdoclet que queremos generar el fichero struts-config.xml. Le indicamos la versión de struts para la que queremos generar el fichero.

Con “templateFile” estamos indicando la plantilla que Xdoclet va a utilizar para generar el fichero struts-config.xml. Esto no debería ser necesario ya que Xdoclet trae una plantilla para hacer esto. Yo lo he puesto porque con la plantilla por defecto tuve algunos problemillas con las clases de formulario que no extendían de org.apache.struts.action.ActionForm, así que cambié la plantilla por defecto.

Con “mergedir” indicamos el directorio donde se encuentran los ficheros de “merge”. Estos ficheros los utiliza Xdoclet para insertarlos dentro del fichero struts-config.xml que está generando. Esto sirve para definir ciertas partes del fichero struts-config.xml que son fijas (como la definición de los datasources).

Con “strutsvalidationxml” estamos indicado a Xdoclet que también queremos que genere el fichero validation.xml.

3. Ficheros de merge

En el punto anterior hemos hemos hablado del atributo “mergeDir”, y decíamos que en este directorio tendríamos algunos ficheros que Xdoclet iba a incluir en el fichero struts-config.xml.

A continuación vamos a enumerar estos ficheros, y a ver un ejemplo de su contenido:

  • struts-data-sources.xml:
    Definimos los datasources de struts. Nótese que en este fichero tenemos que poner los tags <data-sources> y </data-sources>.
    <data-sources>
        <!-- Ejemplo de definición de datasource
        <data-source type="org.apache.commons.dbcp.BasicDataSource">
            <set-property
              property="driverClassName"
              value="org.postgresql.Driver" />
            <set-property
              property="url"
              value="jdbc:postgresql://localhost/mydatabase" />
            <set-property
              property="username"
              value="me" />
            <set-property
              property="password"
              value="test" />
            <set-property
              property="maxActive"
              value="10" />
            <set-property
              property="maxWait"
              value="5000" />
            <set-property
              property="defaultAutoCommit"
              value="false" />
            <set-property
              property="defaultReadOnly"
              value="false" />
            <set-property
              property="validationQuery"
              value="SELECT COUNT(*) FROM market" />
        </data-source>
        -->
    </data-sources>

 

  • struts-forms.xml: Nos permite definir formularios que no se van a mantener con Xdcolet (tags en los comentarios de javadoc).
    <!-- sample form bean descriptor for a DynaActionForm
        <form-bean name="logonForm"
            type="org.apache.struts.action.DynaActionForm">
            <form-property
                name="username"
                type="java.lang.String"/>
            <form-property
                name="password"
                type="java.lang.String"/>
       </form-bean>
    end sample -->

 

  • global-exceptions.xml: Para definir excepciones globales. Téngase en cuenta que tenemos que especificar los tags <global-exceptions> y </global-exceptions>
    <global-exceptions>
        <!-- sample exception handler
        <exception
            key="expired.password"
            type="app.ExpiredPasswordException"
            path="/changePassword.jsp"/>
        end sample -->
    </global-exceptions>

 

  • global-forwards.xml: Para definir redirecciones globales (se pueden usar desde cualquier acción). Nótese que es necesario especificar los tags <global-forwards> y </global-forwards>
    <global-forwards>
        <!-- Default forward to "Welcome" action
        <forward
            name="welcome"
            path="/Welcome.do"/>
        -->
    </global-forwards>

 

  • struts-actions.xml: Nos permite definir acciones que no se van a mantener con Xdcolet (tags en los comentarios de javadoc).
    <!-- Ejemplo de acción para saltar a index.jsp
    <action 
        path="/index"
        forward="/index.jsp"
    />

 

  • struts-controller.xml: Para definir el controller.
    <controller processorClass="org.apache.struts.tiles.TilesRequestProcessor"/>

 

  • struts-message-resources.xml: Para definir los ficheros de
    recursos.
    <message-resources parameter="MessageResources" />

 

  • struts-plugins.xml: Para definir los plugins.
    <!-- ======================================================= Tiles plugin -->
    <plug-in className="org.apache.struts.tiles.TilesPlugin" >
        <!-- Path to XML definition file -->
        <set-property property="definitions-config" value="/WEB-INF/tiles-defs.xml" />

        <!-- Set Module-awareness to true -->
        <set-property property="moduleAware" value="true" />
    </plug-in>

    <!-- =================================================== Validator plugin -->
    <plug-in className="org.apache.struts.validator.ValidatorPlugIn">
        <set-property property="pathnames"
                      value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/>
    </plug-in>

4. Definiendo un Formulario

Supongamos que tenemos una clase del estilo:

    /**
     * @author autentia.alex
     */
    public class Contact {
        private String name = null;
        private String familyName = null;
        private String email = null;

        public Contact() {
        }

        public String getName() {
            return this.name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getFamilyName() {
            return this.familyName;
        }

        public void setFamilyName(String familyName) {
            this.familyName = familyName;
        }

        public String getEmail() {
            return this.email;
        }

        public void setEmail(String email) {
            this.email = email;
        }
    }

Para definir un formulario de Struts con esta clase basta con añadir lo siguiente (lo que está en negrita):

    /**
     * @author autentia.alex
     *
     * @struts.form name="contactForm"
     */
    public class Contact extends ValidatorForm {
        private String name = null;
        private String familyName = null;
        private String email = null;

        public Contact() {
        }

        public String getName() {
            return this.name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getFamilyName() {
            return this.familyName;
        }

        public void setFamilyName(String familyName) {
            this.familyName = familyName;
        }

        public String getEmail() {
            return this.email;
        }

        public void setEmail(String email) {
            this.email = email;
        }
    }

Esto nos generará la siguiente entrada en el struts-config.xml:

    ...
    <form-beans>
        <form-bean
          name="contactForm"
          type="com.autentia.contacto.vo.Contact"
        />
    </from-beans>
    ...

5. Definiendo las validaciones

Si queremos que sobre los campos del formulario se hagan validaciones (un campo obligatorio, que cumpla un determinado formato, …) bastará con poner algunos tags (ver los cambios en negrita):

    /**
     * @author autentia.alex
     *
     * @struts.form name="contactForm"
     */
    public class Contact extends ValidatorForm {
        private String name = null;
        private String familyName = null;
        private String email = null;

        public Contact() {
        }

        public String getName() {
            return this.name;
        }

        /**
         * @struts.validator type="required"
         */
        public void setName(String name) {
            this.name = name;
        }

        public String getFamilyName() {
            return this.familyName;
        }

        public void setFamilyName(String familyName) {
            this.familyName = familyName;
        }

        public String getEmail() {
            return this.email;
        }

        /**
         * @struts.validator type="email"
         */
        public void setEmail(String email) {
            this.email = email;
        }
    }

Con esto estamos indicando que el campo “name” es obligatorio, y que el campo email debe tener formato de correo electrónico. Nótese que los comentarios sobre validaciones se ponen en los métodos “set”.

Esto generaría la siguiente entrada en el fichero de validaciones validation.xml:

    ...
    <form name="contactForm">
        <field property="name" depends="required">
            <arg0 key="contactForm.name"/>
        </field>
        <field property="email" depends="email">
            <arg0 key="contactForm.email"/>
        </field>
    </form>        
    ...

Fíjese como aparecen las dos claves “contactForm.name” u contactForm.email”. Estas se han generado automáticamente a partir del nombre del formulario y el nombre del campo. Tendremos que añadir estas claves a nuestro fichero de recursos para darles valor según el idioma.

6. Definiendo una acción

Ya sólo nos queda ver como se definen las acciones. Para seguir con el mismo ejemplo podríamos tener la siguiente acción:

    /**
     * @author autentia.alex
     */
    public class InsertContact extends Action {
        public ActionForward execute(ActionMapping mapping, ActionForm form,
                HttpServletRequest request, HttpServletResponse response) throws Exception {
            Contact contact = (Contact) form;
            ContactManager cm = ContactManager.getInstance();
            cm.insert(contact);
            return mapping.findForward("ok");
        }
    }

Para dar de alta esta acción en el fichero struts-config.xml basta con añadir las siguientes líneas (aparecen en negrita):

    /**
     * @author autentia.alex
     * 
     * @struts.action path="/insertContact"
     *                name="contactForm"
     *                input="/formContact.jsp?action=insert"
     * @struts.action-forward name="ok" path="/index.do"
     */
    public class InsertContact extends Action {
        public ActionForward execute(ActionMapping mapping, ActionForm form,
                HttpServletRequest request, HttpServletResponse response) throws Exception {
            Contact contact = (Contact) form;
            ContactManager cm = ContactManager.getInstance();
            cm.insert(contact);
            return mapping.findForward("ok");
        }
    }

Esto generará la siguiente entrada en el fichero struts-config.xml:

    <action
      path="/insertContact"
      type="com.autentia.contacto.struts.action.InsertContact"
      name="contactForm"
      scope="request"
      input="/formContact.jsp?action=insert"
      unknown="false"
      validate="true"
    >
      <forward
        name="ok"
        path="/index.do"
        redirect="false"
      />
    </action>

7. Conclusiones

En todos nuestros proyectos siempre tenemos que intentar seguir la regla del beso seco. Esto en español queda un poco mal, peor en ingles sería DRY KISS, donde DRY = Don’t repeat yourself (no te repitas) y KISS = Keep it simple, stupid (hazlo lo más sencillo que puedas).

En definitiva, es intentar siempre que el código sea lo más sencillo posible de mantener. Con el uso que hemos visto de Xdoclet en este tutorial hacemos un buen acercamiento a la regla DRY ya que evitamos el tener que mantener dos ficheros por separado (código fuente java, y fichero de configuración xml), y encima escribimos menos código.

Además también estamos consiguiendo que la interacción entre distintos desarrolladores sea más sencilla, lo cual también es una gran ganancia.

Espero que os sirva de ayuda, y sobre todo recordad siempre la regla del beso seco 😉

8. Sobre el autor

Alejandro Pérez García

Dir. Implantación y Rendimiento

mailto:alejandropg@autentia.com

Autentia Real Business Solutions S.L.

http://www.autentia.com

 

Alejandro es socio fundador de Autentia y nuestro experto en Java EE, Linux y optimización de aplicaciones empresariales. Ingeniero en Informática y Certified ScrumMaster. Seguir @alejandropgarci Si te gusta lo que ves, puedes contratarle para darte ayuda con soporte experto, impartir cursos presenciales en tu empresa o para que realicemos tus proyectos como factoría (Madrid). Puedes encontrarme en Autentia: Ofrecemos servicios de soporte a desarrollo, factoría y formación.

1 COMENTARIO

  1. Buenas,
    El tutorial está muy bien, pero he visto que también se puede incluir la definicion del formulario en el action, mediante dynaForms. De esta manera no tenemos que generar la clase Contact, creo. ¿Me comentais si hay ventajas-desventajas en este planteamiento que propongo?
    También me gustaría saber si mediante anotaciones sabeis si se puede generar el struts-config al estilo de xdoclet.
    Un saludo

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad