Múltiples struts-config.xml e internacionalización de Jasper Report

0
38134

Múltiples
struts-config.xml e internacionalización de Jasper Report

Creación: 16-03-2007

    1. Introducción

En este tutoral queremos mostraros
dos cosas útiles para utilizar en vuestras aplicaciones:

  1. Dividir el struts-config.xml
    en dos o más ficheros, pero sin usar la capacidad de módulos
    que tiene struts.

  2. Generar informes con la ayuda
    de Jasper Report con un contenido diferente dependiendo del idioma
    del usuario que utiliza la aplicación.

    1. Entorno

El tutorial está escrito
usando el siguiente entorno:

  • Hardware: HP COMPAQ Presario
    V6000 (Centrino Duo 1.66GHz, 2048 MB RAM, 100 GB HD)

  • Sistema Operativo: Windows
    Vista Bussiness

  • Máquina Virtual Java:
    JDK 1.6.0 de Sun Mircosystems

  • Tomcat 5.5.23

  • Struts 1.3.8

  • Jasper Report 1.3.1

    1. Descripción
      del problema y librerías necesarias

Vamos
a crear una aplicación Struts que muestre en una página
web un texto diferente dependiendo del idioma local del usuario que
utiliza la aplicación. Además contendrá un botón
que permitirá crear un informe con JasperReport (un archivo
pdf) cuyo contenido también dependerá del idioma local
del usuario. La configuración de Struts estará dividida
en dos archivos struts-config.

Necesitamos
las librerias de struts y Jasper Report. A continuación pongo
una lista de todas las librerías necesarias:

    1. Configurando
      la aplicación para utilizar varios struts-config

Debemos
configurar el web.xml de nuestra aplicación de la siguiente
manera:

<!–
Standard Action Servlet Configuration –>

<servlet>

<servlet-name>action</servlet-name>

<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>

<init-param>

<param-name>config</param-name>

<param-value>/WEB-INF/struts-config.xml,/WEB-INF/struts-config2.xml</param-value>

</init-param>

<load-on-startup>2</load-on-startup>

</servlet>

En
el param-value, podemos poner todos los xml que deseemos separados
por coma. En nuestro caso, tenemos sólo dos. Aquí los
podeis ver:

struts-config.xml

<?xml
version=«1.0»
encoding=«ISO-8859-1»
?>

<!DOCTYPE
struts-config PUBLIC

«-//Apache
Software Foundation//DTD Struts Configuration 1.3//EN»

«http://struts.apache.org/dtds/struts-config_1_3.dtd»>

<struts-config>

<!–
================================================ Form Bean
Definitions –>

<form-beans>

<form-bean
name=«LocaleReportForm»
type=«locale.LocaleReportForm»/>

</form-beans>

<!–
=========================================== Global Forward
Definitions –>

<global-forwards>

<forward

name=«welcome»

path=«/Welcome.do»/>

</global-forwards>

<!–
======================================== Message Resources
Definitions –>

<message-resources
parameter=«message»
/>

</struts-config>

struts-config2.xml

<?xml
version=«1.0»
encoding=«ISO-8859-1»
?>

<!DOCTYPE
struts-config PUBLIC

«-//Apache
Software Foundation//DTD Struts Configuration 1.3//EN»

«http://struts.apache.org/dtds/struts-config_1_3.dtd»>

<struts-config>

<!–
=========================================== Action Mapping
Definitions –>

<action-mappings>

<action

path=«/Welcome»

forward=«/pages/Welcome.jsp»/>

<action

path
= «/Report»

type
= «locale.LocaleReportAction»

name
= «LocaleReportForm»

scope
= «request»

validate
= «false»

input
= «/Welcome.jsp»>

</action>

</action-mappings>

</struts-config>

Como
podéis ver los action-mappings están definidios en el
segundo xml.

    1. Creando los
      properties para los idiomas

Vamos
a crear dos ficheros properties para los idiomas. Un fichero para el
idioma por defecto de la aplicación que estará en
español y un segundo fichero para el idioma inglés. La
aplicación buscará el properties correspondiente al
idioma local del usuario, y si no encuentra el correspondiente
properties, utilizará el de por defecto que en nuestro caso
está en español.

El
nombre del fichero de mensajes se define en el struts-config:

<!–
======================================== Message Resources
Definitions –>

<message-resources
parameter=«message»
/>

Por
tanto, tenemos nuestro message.properties con el idioma español:

#
— welcome —

welcome.title=Struts
Aplicacion

welcome.heading=Bienvenido!

welcome.message=Hola
mundo desde
Autentia
!!!

welcome.button=Informe

Y
nuestro fichero message_en_US.properties en idioma inglés:

#
— welcome —

welcome.title=Struts
Application

welcome.heading=Welcome!

welcome.message=Hello
World from
Autentia
!!!

welcome.button=Report

Como
hemos comentado antes, la aplicación cargará el
properties adecuado dependiendo del idioma local del usuario. Si el
usuario utiliza el idioma inglés (en_US), primero intentará
cargar el fichero message_en_US.properties (en nuestro caso ya no
seguiría buscando). Si no fuera encontrado, intentaría
cargar el message_en.properties. Si tampoco fuera encontrado, usará
el locale por defecto de la JVM (Máquina Virtual Java) para
intentar cargar el properties. Nótese que si el usuario
utiliza el idioma en_UK, se cargaría el fichero por defecto.
Si quisieramos que todos los usuarios del idioma inglés
utilizaran un mismo properties, definiriamos tener un
message_en.properties y estaría solucionado.

Aquí
podéis ver un par de artículos de Sun muy interesantes
sobre «Java Internationalization»:

  • Java
    Internationalization: An Overview

  • Java
    Internationalization: Localization with ResourceBundles

    • Creando los
      jsp, el action y el form

      Como
      siempre en mis aplicaciones, defino un jsp de inicio que lo único
      que hace es redirigir a la página principal. Este fichero lo
      llamamos index.jsp y contiene únicamente el siguente
      conentido:

      <%@
      taglib
      uri=«http://struts.apache.org/tags-logic»
      prefix=«logic»
      %>

      <logic:redirect
      forward=«welcome»/>

      Definimos
      el forward global en el struts config:

      <global-forwards>

      <forward

      name=«welcome»

      path=«/Welcome.do»/>

      </global-forwards>

      y
      el action correspondiente en el action mappings:

      <action-mappings>

      <action

      path=«/Welcome»

      forward=«/pages/Welcome.jsp»/>

      </action-mappings>

      Ahora
      hay que crear el archivo Welcome.jsp:

      <%@
      page contentType=«text/html;
      charset=UTF-8»
      %>

      <%@
      taglib
      uri=«http://struts.apache.org/tags-bean»
      prefix=«bean»
      %>

      <%@
      taglib
      uri=«http://struts.apache.org/tags-html»
      prefix=«html»
      %>

      <%@
      taglib
      uri=«http://struts.apache.org/tags-logic»
      prefix=«logic»
      %>

      <html:html
      lang=«true»>

      <head>

      <title><bean:message
      key=«welcome.title»/></title>

      <html:base/>

      </head>

      <body
      bgcolor=«white»>

      <logic:notPresent
      name=«org.apache.struts.action.MESSAGE»
      scope=«application»>

      <font
      color=«red»>

      ERROR:
      Application resources not loaded — check servlet container

      logs
      for error messages.

      </font>

      </logic:notPresent>

      <html:form
      action=«/Report.do?action=viewReport»>

      <h3><bean:message
      key=«welcome.heading»/></h3>

      <p><bean:message
      key=«welcome.message»/></p>

      <html:submit
      property=«submit»><bean:message
      key=«welcome.button»/></html:submit>

      </html:form><br>

      </body>

      </html:html>

      Únicamente
      mostramos mensajes del properties y asociamos el action
      correspondiente al formulario para crear el pdf. Definimos el
      action en el struts-config:

      <action-mappings>

      <action

      path
      = «/Report»

      type
      = «locale.LocaleReportAction»

      name
      = «LocaleReportForm»

      scope
      = «request»

      validate
      = «false»

      input
      = «/Welcome.jsp»>

      </action>

      </action-mappings>

      Tenemos
      que definir el form y el action correspondiente:

      LocaleReportAction.java

      package
      locale;

      import
      javax.servlet.http.HttpServletRequest;

      import
      javax.servlet.http.HttpServletResponse;

      import
      org.apache.struts.action.Action;

      import
      org.apache.struts.action.ActionForm;

      import
      org.apache.struts.action.ActionForward;

      import
      org.apache.struts.action.ActionMapping;

      public
      final class
      LocaleReportAction extends
      Action {

      /**

      *
      Process
      the
      specified
      HTTP
      request,
      and
      create
      the
      corresponding
      HTTP

      *
      response
      (or
      forward
      to
      another
      web
      component
      that will
      create
      it).

      *
      Return an
      <code>ActionForward</code>
      instance
      describing
      where and
      how

      *
      control
      should be
      forwarded,
      or
      <code>null</code>
      if the
      response
      has

      *
      already
      been
      completed.

      *

      *
      @param
      mapping
      The
      ActionMapping
      used to
      select
      this
      instance

      *
      @param
      actionForm
      The
      optional
      ActionForm
      bean for
      this
      request
      (if any)

      *
      @param
      request
      The HTTP
      request
      we are
      processing

      *
      @param
      response
      The HTTP
      response
      we are
      creating

      *

      *
      @exception
      Exception
      if
      business
      logic
      throws an
      exception

      */

      public
      ActionForward execute(ActionMapping mapping, ActionForm form,
      HttpServletRequest request, HttpServletResponse response)
      throws
      Exception

      {

      //
      Creando el informe a partir del locale del usuario y escribiendolo
      en el outputstream

      LocaleReport.createReport(request.getLocale(),
      response.getOutputStream());

      //
      Forward control to the specified success URI

      return
      null;

      }

      }

      LocaleReportForm.java

      package
      locale;

      import
      javax.servlet.http.HttpServletRequest;

      import
      org.apache.struts.action.ActionErrors;

      import
      org.apache.struts.action.ActionForm;

      import
      org.apache.struts.action.ActionMapping;

      public
      final
      class
      LocaleReportForm
      extends
      ActionForm {

      /**

      *
      Reset all
      properties
      to their
      default
      values.

      *

      *
      @param
      mapping
      The
      mapping
      used to
      select
      this
      instance

      *
      @param
      request
      The
      servlet
      request
      we are
      processing

      */

      public
      void
      reset(ActionMapping mapping, HttpServletRequest request) {

      //No
      hay nada que hacer

      }

      /**

      *
      Validate
      the
      properties
      posted in
      this
      request.
      If
      validation
      errors
      are

      *
      found,
      return an
      <code>ActionErrors</code>
      object
      containing
      the
      errors.

      *
      If no
      validation
      errors
      occur,
      return
      <code>null</code>
      or an
      empty

      *
      <code>ActionErrors</code>
      object.

      *

      *
      @param
      mapping
      The
      current
      mapping
      (from
      strutsconfig.xml)

      *
      @param
      request
      The
      servlet
      request
      object

      */

      public
      ActionErrors validate(ActionMapping mapping,

      HttpServletRequest
      request) {

      //No
      hay que validar campos

      return
      null;

      }

      }

      Vamos
      a echarle un vistazo a la función que crea el informe
      (LocaleReport.java):

      package
      locale;

      import
      java.io.InputStream;

      import
      java.io.OutputStream;

      import
      java.util.HashMap;

      import
      java.util.Locale;

      import
      java.util.Map;

      import
      java.util.ResourceBundle;

      import
      net.sf.jasperreports.engine.JREmptyDataSource;

      import
      net.sf.jasperreports.engine.JRException;

      import
      net.sf.jasperreports.engine.JRParameter;

      import
      net.sf.jasperreports.engine.JasperCompileManager;

      import
      net.sf.jasperreports.engine.JasperExportManager;

      import
      net.sf.jasperreports.engine.JasperFillManager;

      import
      net.sf.jasperreports.engine.JasperPrint;

      import
      net.sf.jasperreports.engine.JasperReport;

      public
      class
      LocaleReport

      {

      public
      static
      void
      createReport(Locale locale, OutputStream outputStream)

      {

      InputStream
      reportSource =
      null;

      Map<String,
      Object> params =
      null;

      try

      {

      //Buscamos
      el xml del jasperReport

      reportSource
      =
      LocaleReport.
      class.getClassLoader().getResourceAsStream(«informe.jrxml»);

      params
      =
      new
      HashMap<String, Object>();

      //establecemos
      el resource bundle correspondiente al locale actual del usuario al
      JasperReport

      params.put(JRParameter.REPORT_RESOURCE_BUNDLE,
      ResourceBundle.
      getBundle(«message»,
      locale));

      //también
      pasamos el locale al JasperReport

      params.put(JRParameter.REPORT_LOCALE,
      locale);

      //Compilamos

      JasperReport
      jasperReport = JasperCompileManager.compileReport(reportSource);

      //Llenamos
      el JasperReport con los parametros

      JasperPrint
      jasperPrint = JasperFillManager.
      fillReport(jasperReport,
      params,
      new
      JREmptyDataSource());

      //Creamos
      el pdf

      JasperExportManager.exportReportToPdfStream(jasperPrint,
      outputStream);

      }

      catch
      (JRException ex)

      {

      ex.printStackTrace();

      }

      }

      }

      Usando
      la propiedad REPORT_RESOURCE_BUNDLE
      pasamos como parámetro al informe un manejador de recursos
      que carga el archivo properties que corresponde al locale del
      usuario. En el caso que nuestro informe (.jrxml) tuviera ya un
      properties establecido, no sería necesario pasarle esta
      propiedad al informe. Si nuestro informe tiene ya establecido un
      properties, y le pasamos la propiedad REPORT_RESOURCE_BUNDLE,
      el valor de la propiedad que le estamos pasando sobreescribe el
      valor que hubiéramos definido en el informe.

      Por ejemplo, si usamos iReport para crear nuestro informe y queremos establecer un ResourceBundle tenemos que ir al menú Editar ->Propiedades del Informe. Una vez allí si nos vamos a la pestaña de «i18n»
      podemos establecer el «nombre base» de nuestros properties (Resouces
      Bundle Base Name). Cuando hemos hecho esto ya podemos crear nuestros
      ficheros locales. Vamos al menú Editar -> Internacionalización -> Ficheros Locales.
      En la ventana que se nos abre podemos crear un nuevo 
      properties para el locale que queramos. Incluso podemos editar
      ahí mismo el fichero properties e insertar los mensajes que
      deseemos. Este nuevo fichero properties se guardará en el mismo
      path donde reside nuestro informe.

      También
      pasamos el locale para poder mostrarlo en el informe.

      Para
      crear un informe, Jasper Report necesita un xml con la información
      necesaria para crearlo. En nuestro caso lo hemos llamando
      informe.jrxml:

      <?xml
      version=»1.0″?>

      <!DOCTYPE
      jasperReport

      PUBLIC
      «-//JasperReports//DTD Report Design//EN»

      «http://jasperreports.sourceforge.net/dtds/jasperreport.dtd»>

      <jasperReport
      name=»LocaleReport»>

      <import
      value=»net.sf.jasperreports.engine.*» />

      <import
      value=»net.sf.jasperreports.engine.data.*» />

      <title>

      <band
      height=»552″ isSplitAllowed=»true»>

      <textField
      isStretchWithOverflow=»false» evaluationTime=»Now»
      hyperlinkType=»None» hyperlinkTarget=»Self»
      bookmarkLevel=»0″>

      <reportElement
      positionType=»Float» x=»150″ y=»20″
      width=»400″ height=»50″ stretchType=»NoStretch»
      isPrintRepeatedValues=»true»
      isRemoveLineWhenBlank=»false»
      isPrintInFirstWholeBand=»false»
      isPrintWhenDetailOverflows=»false» />

      <textElement>

      <font
      size=»12″ />

      </textElement>

      <textFieldExpression
      class=»java.lang.String»>

      <![CDATA[
      $P{REPORT_LOCALE}.getDisplayName($P{REPORT_LOCALE})]]>

      </textFieldExpression>

      </textField>

      <textField
      isStretchWithOverflow=»false» evaluationTime=»Now»
      hyperlinkType=»None» hyperlinkTarget=»Self»
      bookmarkLevel=»0″>

      <reportElement
      positionType=»Float» x=»150″ y=»40″
      width=»400″ height=»50″ stretchType=»NoStretch»
      isPrintRepeatedValues=»true»

      isRemoveLineWhenBlank=»false»
      isPrintInFirstWholeBand=»false»
      isPrintWhenDetailOverflows=»false» />

      <textElement>

      <font
      size=»12″ />

      </textElement>

      <textFieldExpression
      class=»java.lang.String»>

      <![CDATA[
      $R{welcome.message}]]>

      </textFieldExpression>

      </textField>

      </band>

      </title>

      </jasperReport>

      En
      el informe sacamos el locale del usuario, y el texto del mensaje
      que corresponde al locale.

      Vamos
      a ver el resultado de la ejecución:

      Con
      el idioma español en el navegador (carga el texto del
      properties por defecto):

      El
      informe con el idioma español:

      Con
      el idioma inglés (carga el texto del
      message_en_US.properties):

      El
      informe:

      7.
      Sobre el autor

      José Carlos López Díaz, Ingeniero en
      Informática

      mailto:jclopez@autentia.com

      Autentia Real Business Solutions S.L – “Soporte a Desarrollo”

Dejar respuesta

Please enter your comment!
Please enter your name here