Múltiples
struts-config.xml e internacionalización de Jasper Report
Creación: 16-03-2007
-
Introducción
En este tutoral queremos mostraros
dos cosas útiles para utilizar en vuestras aplicaciones:
-
Dividir el struts-config.xml
en dos o más ficheros, pero sin usar la capacidad de módulos
que tiene struts. -
Generar informes con la ayuda
de Jasper Report con un contenido diferente dependiendo del idioma
del usuario que utiliza la aplicación.
-
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
-
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:
-
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.
-
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 formComo
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 containerlogs
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 outputstreamLocaleReport.createReport(request.getLocale(),
response.getOutputStream());//
Forward control to the specified success URIreturn
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
struts–config.xml)*
@param
request
The
servlet
request
object*/
public
ActionErrors validate(ActionMapping mapping,HttpServletRequest
request) {//No
hay que validar camposreturn
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 jasperReportreportSource
=
LocaleReport.class.getClassLoader().getResourceAsStream(«informe.jrxml»);params
= new
HashMap<String, Object>();//establecemos
el resource bundle correspondiente al locale actual del usuario al
JasperReportparams.put(JRParameter.REPORT_RESOURCE_BUNDLE,
ResourceBundle.getBundle(«message»,
locale));//también
pasamos el locale al JasperReportparams.put(JRParameter.REPORT_LOCALE,
locale);//Compilamos
JasperReport
jasperReport = JasperCompileManager.compileReport(reportSource);//Llenamos
el JasperReport con los parametrosJasperPrint
jasperPrint = JasperFillManager.fillReport(jasperReport,
params, new
JREmptyDataSource());//Creamos
el pdfJasperExportManager.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
jasperReportPUBLIC
«-//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 autorJosé Carlos López Díaz, Ingeniero en
InformáticaAutentia Real Business Solutions S.L – “Soporte a Desarrollo”
-