4.4 Tutorial de JSP 2.0, JSTL y Apache Struts



Documentos relacionados
A continuación en la figura D.1 se verá el código de una implementación del archivo struts-config.xml:

DESARROLLO DE APLICACIONES WEB Introducción a Struts Framework

Lección 1 Introducción a Struts. uacosta@globalmentoring.com.mx

Especialista Universitario Java Enterprise. Struts. Sesión 4: Introducción a Struts Depto. Ciencia de la Computación e IA

Tema 4: Tecnologías Web Java

Sistemas de Información 12/13 Servlets y JSPs (Java Server Pages)

Requisitos. Universidad ORT Arquitectura de Software

FUNCIONAMIENTO: FUNCIONALIDAD

ISJu: Técnicas de Programación Cartilla Teórica-Práctica Instalación del "Eclipse IDE for Java EE Developers" y el servidor "Apache Tomcat"

Curso Desarrollo Java Web con JSP, Servlets y el MVC

La vista: ActionForms y taglibs propias

Java Struts Framework. Juan Fco. Rodríguez Hervella

DIPLOMATURA DESARROLLO DE APLICACIONES JAVA

Introducción al desarrollo web (idesweb)

Curso de JavaServer Faces

Sistemas de Información 12/13 Ejercicios Tecnologías Web

Configuración servidor Tomcat

Agosto. Un primer JSP. Guía rápida. [ h t t p : / / w w w. o p e n b o x e r m b. c o m / j a v a. p h p ]

EXTENSIÓN DE UML PARA APLICACIONES WEB

Librería Estándar de Etiquetas JSP (JSTL JSP Standard Tag Library)

La plantilla propone aprovechar esta estructura en común y sólo modificar el contenido del área del documento que sea diferente.

Introducción (1) En una aplicación web, especialmente en Internet, los cambios a la interfaz gráfica son muy frecuentes

Struts. Índice. Copyright Dept. Ciencia de la Computación e IA All rights reserved.

Java Servlets. Luis Fernando Llana Díaz. 17 de abril de Departamento de Sistemas Informáticos y ProgramaciónUniversidad Complutense de Madrid

4. DESARROLLO WEB CON JAVA JSP & SERVLETS

Desarrollo de Aplicaciones Web con JAVA: J2EE y Struts

Práctica 1: Instalación de un servidor de aplicaciones web y diseño de la vista de una aplicación

Ejercicios de AJAX y REST

FRAMEWORK SPRING EN UNA APLICACIÓN WEB

Manual de uso del Taglib de Template Saga

Laboratorio de Aplicaciones Telemáticas

Unidad II. - Las técnicas en las que se basó, las categorías de análisis o ejes centrales que permiten guiar el proceso de investigación.

Figura 7-1 Enlace para instalar el servidor web Apache Jakarta Tomcat

Capitulo III. Diseño del Sistema.

Contiene código HTML normal junto elementos especiales de JSP. Internamente, el servidor de aplicaciones las compilará a un servlet

Kaldeera Advanced Forms 2009 Guía del usuario

Seguridad de la aplicación para servlets y JSP (página activas java)

RESUMEN DE CONCEPTOS BASICOS DE PROGRAMACION JAVA

ATLAS MANUAL DE INTEGRACIÓN Cliente del Servicio de SMS

USANDO SERVLETS EN UN SERVIDOR WEB RESIN

Integración Capa Web de pojo-miniportal (1)

Dossier de prácticas

Manual del Protocolo XML-RPC de Mensajería Negocios

REDES DE ÁREA LOCAL. APLICACIONES Y SERVICIOS EN WINDOWS

Capítulo 1 Documentos HTML5

ATLAS MANUAL DE USUARIO SERVICIO DE TRAZAS

Struts. Sesión 1. Introducción a Struts: el controlador y las acciones. Especialista Universitario Java Enterprise

Curso PHP Módulo 1 R-Luis

NIVEL 16: ESTRUCTURAS N-ARIAS RECURSIVAS Aplicaciones Web, Html y Servlets. ISIS1206 Estructuras de Datos

Conexión de Mysql con NetBeans

Introducción a Java LSUB. 15 de enero de 2015 GSYC

Un servlet es una clase java que implementa la Servlet interface. Un servlet corre dentro de un contexto denominado Servlet engine.

Redes de área local: Aplicaciones y servicios WINDOWS

Desarrollo de Servicios Web con JBuilder

RUEDA TORRES DULCE CAROLINA 3CM2 JAX-WS WEB SERVICES WEB APPLICATION DEVELOPMENT

ATLAS MANUAL DE USUARIO COMPONENTE CODIGO DE BARRAS

Curso Java Web (JSP's/Servlets)

JSP Básico. Índice. 2 Traducción de los JSP a servlets Acciones Introducción a JSP... 2

Laboratorio de Aplicaciones Telemáticas (Curso 2009/2010)

Programación páginas web. Servidor (PHP)

5.3.2 Java Server Faces (JSF)

API de java. ( Guía de alumno Laboratorio 9. Recursos disponibles en moodle para este día.

ATLAS MANUAL DE USUARIO COMPONENTES JSF BUSINESS OBJECTS XI

Uso de excepciones en Java

Instalar y configurar W3 Total Cache

TUTORIAL DESARROLLO DE APLICACIONES PARA EVOLUTION CON MS ACCESS

Struts. Sesión 2: La vista en Struts. ActionForms y taglibs propias. Especialista Universitario Java Enterprise

Tema 4: Diseño e Implementación de la Capa Web

A continuación se confeccionara un formulario para el ingreso de un nombre y un botón para el envío del dato ingresado al servidor:

Diplomado Java Web Programming with Servlets, JSP, JSF & Ajax

Práctica de introducción a

Mi propuesta consiste en crear un portal Web que contemple las siguientes funcionalidades:

Universidad Tecnológica del Valle del Mezquital. Desarrollo de Aplicaciones Web. Manual JSP

Manual de Integrador.NET

CAPÍTULO 14. DESARROLLO

Solución al Examen de Prácticas de Programación (Ingeniería Informática)

Capítulo 3 Diseño del Sistema de Administración de Información de Bajo Costo para un Negocio Franquiciable

Manual de Uso XML-Whois de Neubox. Manual del Sistema de XML Whois de Neubox Versión

9. Objetos y clases Clases

EXAMEN FINAL Metodología y Programación Orientada a Objetos. Curso Cuatrimestre de otoño. 17 de Enero de 2011

Introducción al lenguaje Java

Curso de PHP con MySQL Gratis

Programación Orientada a Objetos con Java

VAST: Manual de usuario. Autores: Francisco J. Almeida-Martínez Jaime Urquiza-Fuentes

Curso de Java POO: Programación orientada a objetos

Validación. Internacionalización

Práctica 2: Instalación de un gestor de bases de datos relacionales y desarrollo de una aplicación Web con persistencia de datos

Java Avanzado Facultad de Ingeniería. Escuela de computación.

Identidad Corporativa de ICM 1 TABLA DE CONTENIDO INTRODUCCIÓN COMPONENTES NECESARIOS... 3

DESARROLLO DE APLICACIONES WEB Introducción a Java Server Faces

Bases de Datos. Marta Elena Zorrilla Pantaleón Rafael Duque Medina DPTO. DE MATEMÁTICAS, ESTADÍSTICA Y COMPUTACIÓN

Transcripción:

4.4 Tutorial de JSP 2.0, JSTL y Apache Struts

JSP 2.0 (1) Qué añade JSP 2.0 frente a JSP 1.x? Lenguaje de expresiones Anteriormente sólo estaba disponible en JSTL Documentos JSP Páginas JSP en sintaxis XML JSP 1.2 también permitía escribir documentos JSP, pero de una manera más incómoda Implementar tags a medida utilizando la propia tecnología JSP Se implementan en páginas JSP Más sencillo que el API de extensión de tags (javax.servlet.jsp.tagext), pero menos potente Útil para tags orientados a presentación, para tags que hagan uso de librerías existentes y para desarrolladores de páginas JSP que no dispongan de conocimientos de Java

JSP 2.0 (y 2) Compatibilidad con JSP 1.x Un contenedor de JSP 2.0 tiene que poder ejecutar aplicaciones con sintaxis JSP 1.x Es posible migrar una aplicación JSP 1.x a sintaxis JSP 2.0 página a página En este apartado, y en los dos siguientes, se ilustran la sintaxis de los documentos JSP y el lenguaje de expresiones

Lenguaje de expresiones (1) En JSP 1.x si se desea dar valor a un atributo de un tag, es preciso usar una expresión <%=... %> Ejemplo <jsp:usebean id="shoppingcart" scope="session" class="org.acme.shoppingcart"/> <xxx:if test="<%= shoppingcart.getnumberofproducts() > 0 %>">... </xxx:if> JSP 2.0 proporciona un lenguaje de expresiones para facilitar su construcción Ejemplo <xxx:if test="${sessionscope.shoppingcart.numberofproducts > 0}">... </xxx:if>

Lenguaje de expresiones (2) Expresiones y literales Las expresiones tienen que ir rodeadas por ${ y }. Cualquier valor que no empiece por ${, se considera un literal Los literales que incluyen el símbolo ${, han de escaparlo rodeándolo de ${' y '} Ejemplo: <xxx:atag att="this literal includes ${'${'} character"/> Acceso a atributos de objetos Java en expresiones Se puede acceder a las propiedades de un JavaBean, y a objetos de un Map, List o vector

Lenguaje de expresiones (3) Acceso a atributos de objetos Java en expresiones (cont) Ejemplos ${user.firstname} = user.getfirstname() ${user.address.city} = user.getaddress().getcity() ${user.preferencesmap["shipping"]} = user.getpreferencesmap().get("shipping") ${user.preferenceslist[0]} = user.getpreferenceslist().get(0) Unifica el tratamiento de los operadores. y [] ${user.firstname} es equivalente a ${user["firstname"]} ${user.preferencesmap["shipping"]} es equivalente a ${user.preferencesmap.shipping} Para determinados casos, es preciso usar el operador [] ${user.preferencesmap["book.fiction"]} es equivalente a user.getpreferencesmap().get("book.fiction") ${user.preferencesmap[product.category]} es equivalente a user.getpreferencesmap().get(product.getcategory())

Objetos implícitos Entre otros Lenguaje de expresiones (4) pagescope (Map) requestscope (Map) sessionscope (Map) applicationscope (Map) param (Map que mapea nombres de parámetros univaluados a String) paramvalues (Map que mapea nombres de parámetros multivaluados a String[]) Cuando se usa un objeto sin especificar su ámbito (el objeto implícito en el que está contenido), se busca en los ámbitos page, request, session y application (en este orden) Ejemplo <xxx:if test="${shoppingcart.numberofproducts > 0}">... </xxx:if>

Lenguaje de expresiones (y 5) Literales Boolean (true y false) Numéricos Cadenas de caracteres (entre comillas simples o dobles) null Operadores Aritméticos: +,-, *, /, div, %, mod Lógicos: &&, and,, or,!, not Relacionales: ==, eq,!=, ne, <, lt, >, gt, <=, le, >=, ge empty: permite comprobar si un valor es null Ejemplos <xxx:if test="${!empty requestscope.previous}">... </xxx:if> <xxx:if test="${sessionscope.shoppingcart.numberofproducts > 0}">... </xxx:if> Se pueden usar paréntesis

JSTL (1) En el pasado existían numerosas librerías de tags JSP que permitían Iterar sobre colecciones Imprimir valores de propiedades de JavaBeans de forma segura Internacionalización de mensajes, números, fechas, etc. Generación de URLs aplicando URL rewriting Acceso a documentos XML Etc Por ello, se decidió estandarizar una librería general de tags, llamada JSTL (JSP Standard Tag Library)

JSTL (2) Tags en JSTL Core Control de flujo Soporte para URLs I18n (internacionalización) Soporte para internacionalización: establecimiento del Locale, generación de mensajes, formateo de números, cantidades monetarias, fechas, etc. XML Parsing de un documento XML Flujo de control para recorrer un documento XML (alternativa a XSL para casos sencillos) Tags para lanzar transformaciones XSL

JSTL (y 3) Tags en JSTL (cont) Acceso a BDs Funciones Permiten lanzar sentencias SQL a BDs relacionales Sólo deberían usarse para prototipado rápido o aplicaciones muy simples JSP 2.0 define un mecanismo para añadir funciones al lenguaje de expresiones JSTL 1.1 define un conjunto de funciones estándar length (aplicable a Collection y String), tolowercase, touppercase, substring, contains, etc En este apartado y en los dos siguientes se ilustran parte de los tags de los grupos Core e I18n

Qué es Struts? Framework OpenSource para implementar aplicaciones web con servlets y JSP según el patrón arquitectónico Model-View-Controller Proyecto de Apache Autor original: Craig R. McClanahan Funciona sobre cualquier servidor de aplicaciones web que implemente las APIs de servlets y JSP Ha ganado gran relevancia en el mundo de las aplicaciones web Java Versión 1.0 estable en Julio 2001 Posteriormente, surgieron otros framework MVC

Qué proporciona Struts? Un framework que da soporte para implementar las capas controlador y vista de una aplicación web Servlet Front Controller y clases relacionadas Sistema de plantillas Validación de parámetros Una librería de tags JSP muy completa

El patrón Front Controller en Struts (1) javax.servlet.http.httpservlet org.apache.struts.action.actionservlet # doget # dopost 0..n org.apache.struts.action.action + execute <<use>> <<instantiate>> org.apache.struts.action.actionform + reset + validate Action1... ActionN ActionForm1 ActionFormN... <<use>>

El patrón Front Controller en Struts (2) ActionServlet Servlet Front Controller En web.xml se especifica que todas las URLs que impliquen procesamiento (por GET o POST) vayan a este servlet Ej.: las URLs que termine en.do Clases ActionForm Si el programador lo desea, puede acceder a los parámetros de la request a través de un JavaBean que extiende ActionForm Especialmente útil en formularios Clase Action => método execute Accede a los parámetros de la request, directamente o vía el ActionForm correspondiente Realiza la operación invocando un método de un Session Facade del modelo o una fachada del controlador Deja el resultado devuelto por el método en la request o en la sesión Devuelve un objeto ActionForward, que representa la URL que hay que visualizar a continuación (sendredirect o forward)

El patrón Front Controller en Struts (3) Fichero de configuración Clases ActionForm que usa nuestra aplicación Nombre lógico (ej.: loginform) Nombre completo de la clase (ej.: es.udc.fbellas.j2ee.strutstutorial.portal3.http.view.actionforms.loginform) URLs que implican procesamiento URL de tipo path relativo a contexto (ej.: /Login) No llevan el.do final Nombre completo de la clase Action (ej.: es.udc.fbellas.j2ee.strutstutorial.portal3.http.controller.actions.loginaction) Nombre lógico de la clase ActionForm asociada

El patrón Front Controller en Struts (y 4) Fichero de configuración (cont) Definiciones de nombres lógicos de URLs Nombre que usan las acciones cuando devuelven un ActionForward (ej.: ShowMainPage) sendredirect o forward URL a invocar (ej.: /MainPage.jsp) Cuando el servlet ActionServlet arranca (init), lee el fichero de configuración Crea una única instancia de cada clase Action No se crea una instancia de una clase Action por cada petición que se recibe Tienen que ser thread-safe Misma situación que cuando se trabaja con servlets

La librería de tags de Struts (1) Bean Imprimir el valor de las propiedades de JavaBeans de manera segura Soporte para internacionalización de mensajes No los usaremos, dado que JSTL ofrece una alternativa estándar Logic Control de flujo No los usaremos, dado que JSTL ofrece una alternativa estándar HTML Generación de HTML básico Campos de entrada en formularios Enlaces (con URL rewriting)

La librería de tags de Struts (y 2) Tiles Caso particular del patrón Composite View Sistema de plantillas para páginas JSP Reemplaza a Template El sistema de plantillas que se usaba con Struts 1.0

Arquitectura MVC con Struts Modelo Clases independientes de la vista y el controlador Controlador Conjunto de clases Action Interactúan con el modelo y seleccionan la siguiente vista (dejándole los datos en uno de los cuatro posibles ámbitos, normalmente request o session) Vista Conjunto de clases ActionForm Conjunto de páginas JSP No contienen código Java Sólo visualizan datos Usan acciones JSP para recuperar los valores a mostrar y formatearlos

Demo Portal-3 (1) Lanzar el navegador Acceder a Portal-3 main page

Demo Portal-3 (2) Clic en Login Clic en el botón Login

Demo Portal-3 (3) Terminar y lanzar el navegador dos días más tarde Clic en Logout Acceder a Portal-3 main page Portal-3 main page (Welcome to Portal-3)

Demo Portal-3 (4) Este ejemplo, al igual que los siguientes, usa XHTML 1.0 Estricto y CSS 2.0 XHTML CSS Versión XML de HTML (ej.: todos los tags tienen que cerrarse, los valores de los atributos tienen que entrecomillarse, tags en minúsculas, etc) El XHTML generado sólo contiene contenido estructurado El formato (fuentes, colores, posicionamiento, etc) se especifica en una hoja (fichero) de estilos CSS El aspecto gráfico de la aplicación puede cambiarse modificando la hoja CSS

Demo Portal-3 (y 5) CSS (cont) También puede ser interesante tener un conjunto de hojas CSS con distintos formatos para una misma aplicación web Visualización en ordenador de sobremesa Visualización en PDA (ej.: no muestra o resume cabecera, sidebar y pié de página) Printer-friendly pages Etc Se procura huir del uso de tablas, excepto para la presentación de datos que siempre han de visualizarse de manera tabular (ej.: las cuentas de un usuario en una aplicación bancaria) CSS tiene sus propios mecanismos de posicionamiento

Estructura de paquetes es.udc.fbellas.j2ee.util.struts.action es.udc.fbellas.j2ee.strutstutorial.portal3 http controller actions view actionforms messages model userfacade delegate exceptions

jar tvf StrutsTutorial.war (1) Index.jspx InternalError.jspx Login.jspx MainPage.jspx css/styles.css WEB-INF/Struts/struts-config.xml WEB-INF/lib/jstl.jar WEB-INF/lib/standard.jar WEB-INF/lib/antlr.jar WEB-INF/lib/commons-*.jar WEB-INF/lib/jakarta-oro.jar WEB-INF/lib/struts.jar WEB-INF/lib/StandardUtil.jar WEB-INF/lib/WebUtil.jar

jar tvf StrutsTutorial.war (y 2) WEB-INF/classes/es/udc/fbellas/j2ee/strutstutorial/portal3/http/ controller/actions/loginaction.class WEB-INF/classes/es/udc/fbellas/j2ee/strutstutorial/portal3/http/ controller/actions/loginmanager.class WEB-INF/classes/es/udc/fbellas/j2ee/strutstutorial/portal3/http/ controller/actions/logoutaction.class WEB-INF/classes/es/udc/fbellas/j2ee/strutstutorial/portal3/http/ controller/actions/mainpageaction.class WEB-INF/classes/es/udc/fbellas/j2ee/strutstutorial/portal3/http/view/ actionforms/loginform.class WEB-INF/classes/es/udc/fbellas/j2ee/strutstutorial/portal3/model/ userfacade/delegate/userfacadedelegate.class WEB-INF/classes/es/udc/fbellas/j2ee/strutstutorial/portal3/model/ userfacade/exceptions/incorrectpasswordexception.class WEB-INF/classes/es/udc/fbellas/j2ee/strutstutorial/portal3/http/view/ messages/messages.properties WEB-INF/web.xml

Comentarios (1) Documentos JSP Existen varios métodos para especificar que una página JSP es un documento JSP Quizás la manera más natural consiste en usar un descriptor de la aplicación web conforme a Servlet 2.4 (como ya hicimos en anteriores apartados) y usar jspx como extensión de las páginas JSP que sean documentos JSP WEB-INF/Struts struts-config.xml: configuración de Struts para la aplicación del tutorial

Comentarios (y 2) WEB-INF/lib struts.jar, commons-*.jar: Struts standard.jar, jstl.jar, jakarta-oro.jar, antlr.jar: Jakarta Standard TagLibs (implementación OpenSource de JSTL) StandardUtil.jar y WebUtil.jar: subsistema Util de J2EE-Examples WEB-INF/classes/es/.../Messages.properties Internacionalización de mensajes

WEB-INF/web.xml (1) <?xml version="1.0" encoding="iso-8859-1"?> <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/> <!-- =============== Standard TagLibs configuration ============= --> <context-param> <param-name>javax.servlet.jsp.jstl.fmt.localizationcontext </param-name> <param-value>es.udc.fbellas.j2ee.strutstutorial.portal3.http. view.messages.messages</param-value> </context-param>

WEB-INF/web.xml (2) <!-- ================= Front controller 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/struts-config.xml</param-value> </init-param> <init-param> <param-name>debug</param-name> <param-value>2</param-value> </init-param> <init-param> <param-name>detail</param-name> <param-value>2</param-value> </init-param> <load-on-startup>2</load-on-startup> </servlet>

WEB-INF/web.xml (y 3) <!-- ================ Servlet mapping =========================== --> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <!-- ======================== Session =========================== --> <session-config> <session-timeout>30</session-timeout> </session-config> <!-- ==================== Welcome page ========================== --> <welcome-file-list> <welcome-file>index.jspx</welcome-file> </welcome-file-list> </web-app>

Comentarios (1) context-param Permite definir un parámetro de configuración global a toda la aplicación web Accesible vía Servlet.getServletConfig().getServletContext ().getinitparameter() En el ejemplo se utiliza para dar valor al parámetro de configuración javax.servlet.jsp.jstl.fmt.localizationcontext Lo usan los tags de internacionalización de mensajes de JSTL Nombre del fichero de mensajes (sin sufijo.properties) Debe estar debajo de WEB-INF/classes y usar un nombre consistente con su ubicación (como si de una clase se tratase)

Comentarios (2) Servlet org.apache.struts.actions.actionservlet Aparecen dos tags que no hemos usado hasta ahora init-param Permite definir un parámetro de configuración específico al servlet y su valor Accesible vía Servlet.getServletConfig().getInitParameter() load-on-startup Indica que el servlet se debería cargar cuando el servidor arranque la aplicación web El valor (opcional) indica el orden relativo de carga con respecto a otros servlets (cuanto menor sea el valor, antes se carga)

Comentarios (y 3) Servlet org.apache.struts.actions.actionservlet Parámetros de inicialización config Path de tipo relativo a contexto del fichero de configuración de Struts detail Nivel de detalle en los mensajes de depuración durante el parsing de los ficheros de configuración debug Nivel de detalle en los mensajes de depuración de ActionServlet

WEB-INF/Struts/struts-config.xml (1) <?xml version="1.0" encoding="iso-8859-1"?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd"> <struts-config> <!-- ============ Form Bean Definitions =========================== --> <form-beans> <form-bean name="loginform" type="es.udc.fbellas.j2ee.strutstutorial.portal3.http.view. actionforms.loginform"/> </form-beans>

WEB-INF/Struts/struts-config.xml (2) <!-- ============ Global Forward Definitions ====================== --> <global-forwards> <forward name="mainpage" path="/mainpage.do" redirect="true"/> <forward name="internalerror" path="/internalerror.jspx" redirect="true"/> </global-forwards> <!-- ============ Action Mapping Definitions ====================== --> <action-mappings> <action path="/mainpage" type="es.udc.fbellas.j2ee.strutstutorial.portal3.http. controller.actions.mainpageaction"> <forward name="showmainpage" path="/mainpage.jspx"/> </action> <action path="/login" type="es.udc.fbellas.j2ee.strutstutorial.portal3.http. controller.actions.loginaction" name="loginform" scope="request" input="/login.jspx" validate="true"/>

WEB-INF/Struts/struts-config.xml (y 3) <action path="/logout" type="es.udc.fbellas.j2ee.strutstutorial.portal3.http. controller.actions.logoutaction"/> <!-- ============================================================== The standard administrative actions available with Struts. These must be either omitted or protected by security in a real application deployment. ================================================================ --> <action path="/admin/addformbean" type="org.apache.struts.actions.addformbeanaction"/>... </action-mappings> <!-- ============ Message Resources Definitions =================== --> <message-resources parameter="es.udc.fbellas.j2ee.strutstutorial. portal3.http.view.messages.messages"/> </struts-config>

Comentarios (1) En el fichero struts-config.xml se usa <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd"> El especificador PUBLIC permite especificar un identificador y una URI para la ubicación del DTD El procesador del documento XML (en este caso, Struts) puede usar el identificador para recuperar un DTD localmente almacenado, y en consecuencia, no usar la URI especificada struts.jar incluye el DTD

Comentarios (2) Definiciones de nombres lógicos de URLs Se usan en la implementación de las acciones para devolver un ActionForward y en algunas acciones JSP Se especifican con forward Atributos documentados en JavaDoc de org.apache.struts.action.actionforward name: nombre lógico path: path relativo a contexto de la URL a la que se invocará redirect: true (sendredirect) o false (forward) false por defecto Pueden ser globales (global-forwards) o particulares a una acción (action)

Comentarios (3) action Atributos documentados en JavaDoc de org.apache.struts.config.actionconfig type: nombre completo de la clase Action path: URL (path relativo a contexto) que provocará la invocación de la acción Se especifican sin el sufijo.do! name: nombre del ActionForm (definido por form-bean) que captura los parámetros de la invocación scope: ámbito (request o session) del ActionForm En general, request input: path relativo a contexto del formulario de entrada validate: true si el Front Controller tiene que llamar al método validate del ActionForm En general, true

Comentarios (y 4) message-resources Especifica la ubicación del fichero de mensajes Actualmente Struts no está integrado con JSTL

WEB-INF/classes/es/.../Messages.properties (1) Buttons.login=Login ErrorMessages.loginName.notFound=Login name not found ErrorMessages.mandatoryField=Mandatory field ErrorMessages.password.incorrect=Incorrect password ErrorMessages.retry=Please, check if the operation has been performed, \ and retry if necessary errors.footer=</span> errors.header=<span class="errormessage"> InternalError.title=Internal error

WEB-INF/classes/es/.../Messages.properties (y 2) Login.loginName=Login name Login.password=Password Login.rememberMyPassword=Remember my password (cookies must be enabled) Login.title=Portal-3 login form MainPage.hello=Hello MainPage.login=Login MainPage.logout=Logout MainPage.title=Portal-3 main page MainPage.welcome=Welcome to Portal-3

Comentarios (1) Asocia pares <identificadormensaje, mensaje> Convenios de nombrado para los identificadores de mensajes Ordenados alfabéticamente errors.footer y errors.header son dos identificadores especiales que entiende la acción html:errors de Struts Messages.properties Mensajes en el lenguaje por defecto del servidor Messages_xx.properties Mensajes en el lenguaje cuyo código ISO es xx Códigos ISO en http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt en: Inglés es: Español gl: Gallego Etc

Comentarios (y 2) Messages.properties sólo resuelve un aspecto particular de la internacionalización de aplicaciones: impresión de mensajes en distintos idiomas En una aplicación más compleja puede ser necesario tener trozos de páginas en distintos idiomas (con gran cantidad de texto estático) y seleccionarlos o incluirlos dinámicamente en función del idioma Otros aspectos en internacionalización Formatear y tratar fechas, horas, números, cantidades monetarias JSTL proporciona tags para ello También paquetes java.text y java.util Puede requerir tablas para almacenar contenido en distintos idiomas Ej.: un servicio de noticias

es.udc.fbellas.j2ee.strutstutorial.portal3.model.userfacade.delegate UserFac adedelegate + UserFacadeDelegate() + login(loginname : String, password : String, pas swordisencrypted : boolean) : void Simula la fachada del modelo que proporciona las operaciones relativas a la interacción del usuario con el portal

es.udc.fbellas.j2ee.util.struts.action Action (from action) DefaultAction + execute(actionmapping, actionform, request, response) : ActionForward # doexecute(actionmapping, actionform, request, response) : ActionForward # dooninternalerror(actionmapping, actionform, request, response, internalerrorexception) : ActionForw... P ropertyvalidator

Comentarios DefaultAction Problema En general, las clases Action invocarán una operación sobre un Business Delegate del modelo o una fachada del controlador, que puede lanzar InternalErrorException Es necesario (1) capturarla, (2) imprimirla en un log (para depuración) e (3) ir a una página que indique error interno Las clases Action derivarán de DefaultAction Implementa execute (Template Method) en términos de doexecute y dooninternalerror Las clases hijas implementan doexecute, que tiene la misma signatura que execute, pero puede lanzar adicionalmente InternalErrorException PropertyValidator Clase utilidad para validar campos de entrada comunes (double, long, String, etc.)

es.udc.fbellas.j2ee.util.struts.action.defaultaction (1) package es.udc.fbellas.j2ee.util.struts.action; import java.io.ioexception; import javax.servlet.servletcontext; import javax.servlet.http.httpservletrequest; import javax.servlet.http.httpservletresponse; import javax.servlet.servletexception; import org.apache.struts.action.action; import org.apache.struts.action.actionmapping; import org.apache.struts.action.actionform; import org.apache.struts.action.actionforward; import es.udc.fbellas.j2ee.util.exceptions.internalerrorexception;

es.udc.fbellas.j2ee.util.struts.action.defaultaction (2) public abstract class DefaultAction extends Action { public ActionForward execute(actionmapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { } try { return doexecute(mapping, form, request, response); } catch (Exception e) {// Any exception thrown by "doexecute", // including instances of // "RuntimeException". return doonexception(mapping, form, request, response, e); } protected abstract ActionForward doexecute(actionmapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException, InternalErrorException;

es.udc.fbellas.j2ee.util.struts.action.defaultaction (y 3) protected ActionForward doonexception( ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, Exception exception) throws IOException, ServletException { /* * Log error, even with debug level <= 0, because it is a * severe error. */ ServletContext servletcontext = servlet.getservletconfig().getservletcontext(); servletcontext.log(exception.getmessage(), exception); /* Redirect to input page. */ return mapping.findforward("internalerror"); } }

es.udc.fbellas.j2ee.util.struts.action.propertyvalidator (1) public final class PropertyValidator { private final static String INCORRECT_VALUE = "ErrorMessages.incorrectValue"; private final static String MANDATORY_FIELD = "ErrorMessages.mandatoryField"; private PropertyValidator() {} public final static long validatelong(actionerrors errors, String propertyname, String propertyvalue, boolean mandatory, long lowervalidlimit, long uppervalidlimit) { long propertyvalueaslong = 0; if (validatemandatory(errors, propertyname, propertyvalue, mandatory)) { boolean propertyvalueiscorrect = true;

es.udc.fbellas.j2ee.util.struts.action.propertyvalidator (2) try { propertyvalueaslong = new Long(propertyValue).longValue(); if ( (propertyvalueaslong < lowervalidlimit) (propertyvalueaslong > uppervalidlimit) ) { propertyvalueiscorrect = false; } } catch (NumberFormatException e) { propertyvalueiscorrect = false; } if (!propertyvalueiscorrect) { errors.add(propertyname, new ActionMessage(INCORRECT_VALUE)); } } return propertyvalueaslong; }

es.udc.fbellas.j2ee.util.struts.action.propertyvalidator (y 3) public final static boolean validatemandatory(actionerrors errors, String propertyname, String propertyvalue) { } if ((propertyvalue == null) (propertyvalue.length() == 0)) { errors.add(propertyname, new ActionMessage(MANDATORY_FIELD)); return false; } else { return true; } private final static boolean validatemandatory(actionerrors errors, String propertyname, String propertyvalue, boolean mandatory) { } if (mandatory) { return validatemandatory(errors, propertyname, propertyvalue); } else { return true; } } // Resto de métodos validatexxx => Análogos...

Comentarios org.apache.struts.action.actionmapping Permite acceder a los valores configurados en strutsconfig.xml para la acción en ejecución (inclusive a los forwards globales) org.apache.struts.action.actionerrors Juega el papel del mapa de errores que hemos empleado en apartados anteriores El mensaje de error es un org.apache.struts.action.actionmessage, que dispone de un constructor que permite especificar el identificador del mensaje de error (en Messages.properties) ActionMessage se introdujo en Struts 1.2, y reemplaza a ActionError org.apache.struts.action.actionforward Representa la siguiente URL a la que hay que ir El Front Controller lo invocará después de llamar al método execute sobre la acción

es.udc.fbellas.j2ee.strutstutorial.portal3.http.view.actionforms.loginform (1) public class LoginForm extends ActionForm { private String loginname; private String password; private boolean remembermypassword; public LoginForm() { reset(); } public String getloginname() { return loginname; } public void setloginname(string loginname) { this.loginname = loginname.trim(); }

es.udc.fbellas.j2ee.strutstutorial.portal3.http.view.actionforms.loginform (2) public String getpassword() { return password; } public void setpassword(string password) { this.password = password; } public boolean getremembermypassword() { return remembermypassword; } public void setremembermypassword(boolean remembermypassword) { this.remembermypassword = remembermypassword; }

es.udc.fbellas.j2ee.strutstutorial.portal3.http.view.actionforms.loginform (y 3) public void reset(actionmapping mapping, HttpServletRequest request) { reset(); } public ActionErrors validate(actionmapping mapping, HttpServletRequest request) { } ActionErrors errors = new ActionErrors(); PropertyValidator.validateMandatory(errors, "loginname", loginname); PropertyValidator.validateMandatory(errors, "password", password); return errors; private void reset() { loginname = null; password = null; remembermypassword = false; } }

Comentarios LoginForm Juega el mismo papel que la clase vista en el apartado 4.2 Hereda de org.apache.struts.action.actionform Generalmente interesa redefinir reset y validate reset El Front Controller lo llama antes de dar valor a las propiedades validate Permite validar las propiedades después de que el Front Controller les haya dado valores Sólo se invoca si se ha especificado validate="true" para la acción correspondiente en struts-config.xml Si devuelve un ActionErrors no vacío, el Front Controller (1) no invocará el método execute sobre la acción correspondiente, (2) insertará un atributo con los errores en la request, y (3) hará un forward a la URL que especifica el atributo input del action correspondiente en strutsconfig.xml (formulario de entrada)

es.udc.fbellas.j2ee.strutstutorial.portal3.http.controller.actions DefaultAc tion (from action) MainPageAction LoginAction LogoutAction <<use>> <<use>> <<use>> LoginManager <<us e>> UserFacadeDelegate (from delegate)

es.udc.fbellas.j2ee.strutstutorial.portal3.http.controller.actions.loginaction (1) public class LoginAction extends DefaultAction { public ActionForward doexecute(actionmapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException, InternalErrorException { /* Get data. */ LoginForm loginform = (LoginForm) form; String loginname = loginform.getloginname(); String password = loginform.getpassword(); boolean remembermypassword = loginform.getremembermypassword(); /* Do login. */ ActionMessages errors = new ActionMessages();

} es.udc.fbellas.j2ee.strutstutorial.portal3.http.controller.actions.loginaction (y 2) try { LoginManager.login(request, response, loginname, password, remembermypassword); } catch (InstanceNotFoundException e) { errors.add("loginname", new ActionMessage( "ErrorMessages.loginName.notFound")); } catch (IncorrectPasswordException e) { errors.add("password", new ActionMessage( "ErrorMessages.password.incorrect")); } /* Return ActionForward. */ if (errors.isempty()) { return mapping.findforward("mainpage"); } else { saveerrors(request, errors); return new ActionForward(mapping.getInput()); } }

Comentarios Las acciones utilizan el método saveerrors (heredado de org.struts.apache.action.action) para insertar un atributo con los errores en la request org.apache.struts.action.actionmessages Se introdujo en Struts 1.2 Similar a ActionErrors Existen dos versiones del método saveerrors, una que acepta ActionErrors ( deprecated ) y otra que acepta ActionMessages (el usado en el ejemplo)

es.udc.fbellas.j2ee.strutstutorial.portal3.http.controller.actions.logoutaction public class LogoutAction extends DefaultAction { public ActionForward doexecute(actionmapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException, InternalErrorException { /* Do logout. */ LoginManager.logout(request, response); /* Return ActionForward. */ return mapping.findforward("mainpage"); } }

MainPage.jspx (1) <html xmlns="http://www.w3.org/1999/xhtml" xmlns:jsp="http://java.sun.com/jsp/page" xmlns:fmt="http://java.sun.com/jsp/jstl/fmt" xmlns:html="http://struts.apache.org/tags-html" xmlns:c="http://java.sun.com/jsp/jstl/core"> <jsp:output doctype-root-element="html" doctype-public="-//w3c//dtd XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/tr/xhtml1/dtd/xhtml1-strict.dtd" omit-xml-declaration="true" /> <jsp:directive.page contenttype="text/html; charset=iso-8859-1" /> <head> <title><fmt:message key="mainpage.title" /></title> <c:url var="stylesurl" value="/css/styles.css" /> <link rel="stylesheet" href="${stylesurl}" type="text/css" media="all" /> <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> </head> <body>

MainPage.jspx (2) <!-- Welcome --> <div id="header"> <c:choose> <c:when test="${empty sessionscope.loginname}"> <fmt:message key="mainpage.welcome" /> </c:when> <c:otherwise> <fmt:message key="mainpage.hello" /> <c:out value=" ${sessionscope.loginname}" /> </c:otherwise> </c:choose> </div>

MainPage.jspx (y 3) <!-- Links to Login or Logout --> <p> <c:choose> <c:when test="${empty sessionscope.loginname}"> <c:url var="loginurl" value="login.jspx" /> <a href="${loginurl}"> <fmt:message key="mainpage.login" /> </a> </c:when> <c:otherwise> <html:link action="logout.do"> <fmt:message key="mainpage.logout" /> </html:link> </c:otherwise> </c:choose> </p> </body> </html>

Comentarios (1) La página JSP es un documento XML bien formado Importación de librerías En el tag raíz aprovechamos para importar las librerías de tags que se precisan, especificando sus espacios de nombres El espacio de nombres por defecto es el correspondiente a los tags de XHTML (http://www.w3.org/1999/xhtml) Librerías http://java.sun.com/jsp/page (jsp) Tags estándar de JSP (proporcionados por el contenedor) http://java.sun.com/jsp/jstl/fmt (fmt) Tags I18n de JSTL http://struts.apache.org/tags-html (html) Tags HTML de Struts http://java.sun.com/jsp/jstl/core (c) Tags Core de JSTL

Comentarios (2) Importación de librerías (cont) Cuando se importa una librería, el contenedor busca automáticamente su descriptor (fichero.tld) en WEB-INF (y sus subdirectorios) En los ficheros.jar que usa la aplicación Dentro del fichero.jar busca debajo de META-INF (y sus subdirectorios) El descriptor especifica, entre otras cosas, La URI del espacio de nombres (que es lo que utiliza el contenedor para saber que éste es el descriptor de la librería) Los nombres de los tags que proporciona la librería y los nombres de las clases que los implementan En el servlet generado por el contenedor, por cada aparición de un tag de una librería, se crea una instancia la clase correspondiente y se invocan los métodos necesarios a través de un interfaz estándar

Comentarios (3) Importación de librerías (cont) Cuando el contenedor encuentra un tag no JSP (ej.: html), que importa librerías JSP (ej.: xlmns:fmt="... "), en la respuesta generada no incluye los xlmns:xxx correspondientes Ej.: Para... <html xmlns="http://www.w3.org/1999/xhtml" xmlns:jsp="http://java.sun.com/jsp/page" xmlns:fmt="http://java.sun.com/jsp/jstl/fmt" xmlns:html="http://struts.apache.org/tags-html" xmlns:c="http://java.sun.com/jsp/jstl/core">... genera... <html xmlns="http://www.w3.org/1999/xhtml">

Comentarios (4) En MainPage.jspx se utiliza <jsp:output doctype-root-element="html" doctype-public="-//w3c//dtd XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/tr/xhtml1/dtd/xhtml1-strict.dtd" omit-xml-declaration="true" />... lo que genera... <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/tr/xhtml1/dtd/xhtml1-strict.dtd">... y se genera en el lugar adecuado, es decir, antes de que se genere el tag raíz html omit-xml-declaration="true" provoca que no se genere la declaración XML Por defecto, el contenedor añade la declaración XML al principio del documento generado por un documento JSP Lo lógico sería generar la declaración XML (dado que todo documento XML debería tenerla), sin embargo causa problemas en algunos navegadores

Comentarios (5) Tags jsp:directive.xxx Equivalentes a las directivas <%@ XXX... %>, con sus mismos atributos En MainPage.jspx se utiliza <jsp:directive.page contenttype="text/html; charset=iso-8859-1" /> Permite especificar el contenttype de la respuesta HTTP En un documento JSP, el contenttype por defecto es text/xml, lo que provoca que algunos navegadores (ej.: Internet Explorer) visualicen la respuesta como un documento XML (y no como una página HTML) En una página JSP (que no sea un documento JSP), el contenttype por defecto es text/html, y por eso nunca lo hemos tenido que especificar en los ejemplos anteriores

Comentarios (6) En el ejemplo se usa <c:url var="stylesurl" value="/css/styles.css" /> <link rel="stylesheet" href="${stylesurl}" type="text/css" media="all" /> Alternativamente se podría haber usado <link rel="stylesheet" href="css/styles.css" type="text/css" media="all" /> Pero esto resultaría tedioso y propenso a errores en una aplicación web grande con páginas JSP en directorios con cierto nivel de anidamiento (ej.: href="../../../css/styles.css") Usar <link rel="stylesheet" href="/css/styles.css" type="text/css" media="all" /> No funcionaría, dado que la URL /css/styles.css no existe

Comentarios (7) Usar <link rel="stylesheet" href="/strutstutorial/css/styles.css" type="text/css" media="all" /> Sería una mala idea, dado que el administrador del servidor de aplicaciones web podría querer instalar la aplicación web con otro nombre El ejemplo usa el tag c:url Aplica URL rewriting si el navegador no acepta cookies (aunque en este caso no es útil) Si la URL es de tipo path relativo a contexto (ej.: /css/styles.css), le antepone el nombre de la aplicación web, de manera que la URL resultante es de tipo path absoluto (ej.:. /StrutsTutorial/css/styles.css)

Comentarios (y 8) html:link Genera el enlace HTML (<a href=... </a>) Cuando se utiliza el atributo action, aplica URL rewriting si el navegador no acepta cookies El atributo action tiene que especificar la URL de una acción de Struts NOTA: también dispone (alternativamente) del atributo href La URL puede apuntar a cualquier sitio No se aplica URL rewriting

Login.jspx (1) <html xmlns="http://www.w3.org/1999/xhtml" xmlns:jsp="http://java.sun.com/jsp/page" xmlns:fmt="http://java.sun.com/jsp/jstl/fmt" xmlns:html="http://struts.apache.org/tags-html" xmlns:c="http://java.sun.com/jsp/jstl/core"> <jsp:output doctype-root-element="html" doctype-public="-//w3c//dtd XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/tr/xhtml1/dtd/xhtml1-strict.dtd" omit-xml-declaration="true" /> <jsp:directive.page contenttype="text/html; charset=iso-8859-1" /> <head> <title><fmt:message key="login.title" /></title> <c:url var="stylesurl" value="/css/styles.css" /> <link rel="stylesheet" href="${stylesurl}" type="text/css" media="all" /> <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> </head> <body>

Login.jspx (2) <!-- Struts tags must render XHML --> <html:xhtml/> <!-- Print login form --> <html:form action="login.do"> <!-- Login name --> <div class="field"> <span class="label"> <fmt:message key="login.loginname" /> </span> <span class="entry"> <html:text property="loginname" size="16" maxlength="16" /> <html:errors property="loginname" /> </span> </div>

Login.jspx (3) <!-- Password --> <div class="field"> <span class="label"> <fmt:message key="login.password" /> </span> <span class="entry"> <html:password property="password" size="16" maxlength="16" /> <html:errors property="password" /> </span> </div> <!-- Remember my password --> <div class="field"> <span class="label"> <fmt:message key="login.remembermypassword" /> </span> <span class="entry"> <html:checkbox property="remembermypassword" /> </span> </div>

Login.jspx (y 4) <!-- Login button --> <div class="button"> <html:submit><fmt:message key="buttons.login" /></html:submit> </div> </html:form> </body> </html>

Comentarios (1) html:xhtml Causa que los tags de Struts de la librería HTML que se usen en esa página generen XHTML en vez de HTML (por defecto, algunos tags, como html:text, html:password o html:checkbox generan los tags sin cerrarlos, mientras que otros sí los cierran, como por ejemplo, html:link) html:text, html:password y html:checkbox recuperan el valor de la propiedad asociada a través del método getxxx (property="xxx") sobre la instancia de LoginForm enganchada a la request (con nombre loginform) Saben que el ActionForm asociado se llama loginform, dado que el atributo action de html:form es igual a Login.do struts-config.xml especifica loginform como el nombre del ActionForm para la URL /Login.do

Comentarios (y 2) html:errors Imprime el mensaje de error asociado a la propiedad especificada si figura en el ActionErrors/ ActionMessages enganchado a la request El mensaje vendrá flanqueado por errors.header y errors.footer (Messages.properties)

InternalError.jspx (1) <html xmlns="http://www.w3.org/1999/xhtml" xmlns:jsp="http://java.sun.com/jsp/page" xmlns:fmt="http://java.sun.com/jsp/jstl/fmt" xmlns:c="http://java.sun.com/jsp/jstl/core"> <jsp:output doctype-root-element="html" doctype-public="-//w3c//dtd XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/tr/xhtml1/dtd/xhtml1-strict.dtd" omit-xml-declaration="true" /> <jsp:directive.page contenttype="text/html; charset=iso-8859-1" /> <head> <title><fmt:message key="internalerror.title" /></title> <c:url var="stylesurl" value="/css/styles.css" /> <link rel="stylesheet" href="${stylesurl}" type="text/css" media="all" /> <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> </head> <body>

InternalError.jspx (y 2) <p> <fmt:message key="internalerror.title" />. <fmt:message key="errormessages.retry" /> </p> </body> </html>

Un pequeño problema Situación Imaginemos que la página de bienvenida fuese MainPage.jspx En realidad es Index.jspx Un usuario se autentica seleccionando Remember my password Termina la sesión Accede dos días después tecleando la URL de la aplicación en su navegador (ej.: http://www.acme.org/strutstutorial) Se ejecuta MainPage.jspx La sesión no contendrá el atributo loginname, dado que no se ha ejecutado LoginManager.getLoginName

Una solución El navegador nunca invocará a /MainPage.jspx directamente Index.jspx Página de bienvenida Hace un forward a /MainPage.do => se ejecuta MainPageAction MainPageAction LoginManager.getLoginName y forward a /MainPage.jspx Cuando se haga un sendredirect a la página principal se hará siempre con la URL /MainPage.do y nunca con /MainPage.jspx /MainPage.jspx nunca aparecerá en la caja de diálogo del navegador, de manera que el usuario nunca hará un bookmark a esa página, sino a /MainPage.do En MiniPortal volveremos a discutir este problema

es.udc.fbellas.j2ee.strutstutorial.portal3.http.controller.actions.mainpageaction public class MainPageAction extends DefaultAction { public ActionForward doexecute(actionmapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException, InternalErrorException { /* * "LoginManager.getLoginName" creates an appropriate session * if the session had expired, or the user had not logged in, * but he/she had selected "remember my password" in the last * login. */ LoginManager.getLoginName(request); /* Return ActionForward. */ return mapping.findforward("showmainpage"); } }

Index.jspx <jsp:forward xmlns:jsp="http://java.sun.com/jsp/page" page="mainpage.do" />