Ahora que hemos entendido c�mo construir los componentes del Modelo y de la Vista de nuestra aplicaci�n, es hora de enfocarnos en los componentes del Controller. Struts incluye un servlet que implementa la funci�n principal de mapeo de una solicitud URI a una clase Action. Por lo tanto, nuestras principales responsabilidades con el controlador son:
- Escribir una clase Action por cada solicitud l�gica que podr�a ser recibida (extendida desde org.apache.action.Action).
- Configurar un ActionMapping (en XML) por cada solicitud l�gica que podr�a ser enviada. El fichero de configuraci�n XML normalmente se llama struts-config.xml.
- Actualizar el fichero del descriptor de despliegue de la aplicaci�n Web (en XML) para nuestra aplicaci�n para que incluya los componentes Struts necesarios.
- A�adir los componentes Struts apropiados a nuestra aplicaci�n.
�Clases Action
La clase Action define dos m�todos que podr�an ser ejecutados dependiendo de nuestro entorno servlet:
public ActionForward perform(ActionMapping mapping, ActionForm form, ServletRequest request, ServletResponse response) throws IOException, ServletException; public ActionForward perform(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException;
La mayor�a de los proyectos s�lo usar�n la versi�n "HttpServletRequest".
El objetivo de una clase Action es procesar una solicitud, mediante su m�todo perform(), y devolver un objeto ActionForward que identifica d�nde se deber�a reenviar el control (por ejemplo a una JSP) para proporcionar la respuesta apropiada. En el patr�n de dise�o MVC/Model 2, una clase Action t�pica implementar� una l�gica como �sta en su m�todo perform():
- Validar el estado actual de la sesi�n del usuario (por ejemplo, chequear que el usuario ha hecho el login). Si la clase Action encuentra que no existe logon, la solicitud es reenviada a la p�gina JSP que muestra las peticiones del nombre de usuario y la password para logging on. Esto podr�a ocurrir porque un usuario intente entrar "en el medio" de una aplicaci�n (digamos, desde un bookmark), o porque la sesi�n ha expirado, y el contenedor servlet cre� una nueva.
- Si la validaci�n no se ha completado, valida las propiedades del bean formulario seg�n sea necesario. Si se encuentra un problema, almacena las claves de los mensajes de error apropiados como un atributo de la petici�n, y reenv�a el control de vuelta al formulario de entrada para que se puedan corregir los errores.
- Realizar el procesamiento requerido para tratar con esta solicitud (como grabar un fila de la base de datos). Esto se puede hacer mediante c�digo l�gico embebido dentro de la propia clase Action, pero generalmente deber�a realizarse llamando a un m�todo apropiado del bean de l�gica de negocio.
- Actualizar los objetos del lado del servidor que ser�n usados para crear la siguiente p�gina del interface de usuario (normalmente beans del �mbio de solicitud o de sesion, dependiendo de cu�nto tiempo necesitemos mantener estos �tems disponibles).
- Devolver un objeto ActionForward apropiado que identifica la p�gina JSP usada para generar esta respuesta, basada en los beans actualizados recientemente. T�picamente adquiriremos una referencia a dicho objeto llamando a findForward() o al objeto ActionMapping que recibimos (si estamos usando un nombre l�gico normal para este mapeo), o en el propio servlet controlador (si estamos usando un nombre l�gico global para la aplicaci�n).
Entre los problemas de dise�o a recordar cuando codificamos clases Action incluimos los siguientes:
- El servlet controlador crea un s�lo ejemplar de nuestra clase Action, y la usa para todas las solicitudes. Es decir, necesitamos codificar nuestra clase Action para que opere correctamente en un entorno multi-thread, como si estuvieramos codificando un m�todo service() de un servlet.
- El principio m�s importante que nos ayuda en la codificaci�n de threads seguros es usar s�lo variables locales, no variables de ejemplar, en nuestra clase Action. Las variables locales se crean en una pila que es asignada (por nuestra JVM) a cada thread solicitado, por eso no necesitamos preocuparnos de compartirlas.
- Los beans que representan el Modelo de nuestro sistema podr�a lanzar excepciones debido a problemas de acceso a bases de datos o a otros recursos. Deber�amos atrapar dichas excpeciones en la l�gica de nuestro m�todo perform(), y guardalas en el fichero de log de la aplicaci�n (junto con el seguimiento de pila correspondiente) llamando a:
servlet.log("Error message text", exception);
- Como regla general, asignar recursos y mantenerlos a trav�s de las solicitudes del mismo usuario (en la misma sesi�n de usuario) puede causar problemas de escalabilidad. Deber�amos pensar en liberar esos recursos (como las conexions a una base de datos) antes de reenviar el control al componente de la Vista apropiado -- incluso si un m�todo del bean que hemos llamado lanza una excepci�n.
Adem�s, queremos protegernos contra clases Action que son demasiado largas. La forma m�s f�cil de hacer que esto suceda es embeber la l�gica funcional en la propia clase Action, en vez codificarla en beans de l�gica de negocio independientes. Adem�s de hacer la propia clase Action dura de entender y de mantener, esta aproximaci�n tambi�n hace d�ficil re-utilizar el c�digo de la l�gica de negocio, porque est� embebido dentro de un componente (la clase Action) que est� concebido para ser ejecutado en un entorno de aplicaci�n Web.
Una Action puede dividirse en varios m�todos locales, mientras que todas las propiedades necesarias sean pasadas en las firmas de m�todos. La JVM maneja dichas propiedades usando la pila, y por eso son seguras ante los threads.
La aplicaci�n de ejemplo incluida con Struts no cumple este principio, porque la propia l�gica de negocio est� embebida dentro de las clases Action. Esto deber�a considerarse un bug en el dise�o de la aplicaci�n de ejemplo, en vez de una caracter�stica intr�nseca de la arquitectura , o una aproximaci�n a emular.
�La Implementaci�n de ActionMapping
Para poder operar satisfactoriamente, el servlet controlador Struts necesita conocer varias cosas sobre como se deber�a mapear toda URI solicitada a una clase Action apropiada. El conocimiento requerido ha sido encapsulado en un interface Java, llamado ActionMapping, estas son las propiedades m�s importantes:
- type - nombre totalmente cualificado de la clase Java que implementa la clase Action usada por este mapeo.
- name - El nombre del bean de formulario definido en el fichero de configuraci�n que usar� este action.
- path - El path de la URI solicitada que corresponden con la selecci�n de este mapeo.
- unknown - Seleccionado a true si este action deber�a ser configurado como por defecto para esta aplicaci�n, para manejar todas las solicitudes no manejadas por otros action. S�lo un Action puede estar definido como por defecto dentro de una s�la aplicaci�n.
- validate - Seleccionado a true si se deber�a llamar al m�todo validate() de la action asociada con este mapeo.
- forward - El path de la URI solicitada a la que se pasa el control cuando se ha invocado su mapeo. Esto es una alternativa a declarar una propiedad type.
�Fichero de Configuraci�n de los Mapeos de Action
�C�mo aprende el servlet controlador sobre los mapeos que queremos? Ser�a posible (pero tedioso) escribir una peque�a clase Java que simplemente ejemplarizara nuevos ejemplares de ActionMapping, y llamara a todos los m�todos set() apropiados. Para hacer este proceso m�s sencillo, Struts incluye un m�dulo Digester que es capaz de leer la descripci�n basada en XML de los mapeos deseados, creando los objetos apropiados de la misma forma. Puedes encontrar m�s informaci�n sobre este Digester en la documentaci�n del API
La responsabilidad del desarrollador es crear un fichero XML llamado struts-config.xml, y situarlo en el directorio WEB-INF de su aplicaci�n. Este formato de documento est� restringido por su definici�n en "struts-config_1_0.dtd". El elemento XML m�s exterior debe ser <struts-config>.
Dentro del elemento <struts-config>, hay dos elementos importantes que son usados para describir nuestras acciones:
- <form-beans>
Esta secci�n contiene nuestras definiciones de beans. Usamos un elemento <form-bean> por cada bean de formulario, que tiene los siguientes atributos importantes:- name: Un identificador �nico para este bean, que ser� usado para referenciarlo en los correspondientes mapeos de acciones. Normalmente, es tambi�n el nombre del atributo de solicitud o sesi�n bajo el que se almacena este bean de formulario.
- type: El nombre totalmente cualificado de la clase Java de nuestro bean de formulario.
- <action-mappings>
Esta secci�n contiene nuestras definiciones de acciones. Usamos un elemento <action> por cada una de nuestras acciones que queramos definir. Cada elemento action requiere que se definan los siguientes atributos:- path: El path a la clase action en relaci�n al contexto de la aplicaci�n.
- type: El nombre totalmente cualificado de la clase Java de nuestra clase Action.
- name: El nombre de nuestro elemento <form-bean> para usar con esta action.
El fichero struts-config.xml de la aplicaci�n de ejemplo incluye las siguientes entradas de mapeo para la funci�n "log on", que se usar� para ilustrar los requerimientos. Oserva que las entradas para otras acciones se han dejado fuera:
<struts-config> <form-beans> <form-bean name="logonForm" type="org.apache.struts.example.LogonForm" /> </form-beans> <global-forwards type="org.apache.struts.action.ActionForward" /> <forward name="logon" path="/logon.jsp" redirect="false" /> </global-forwards> <action-mappings> <action path="/logon" type="org.apache.struts.example.LogonAction" name="logonForm" scope="request" input="/logon.jsp" unknown="false" validate="true" /> </action-mappings> </struts-config>
Primero se define el bean formulario, Un bean b�sico de la clase "org.apache.struts.example.LogonForm" es mapeado al nombre l�gico "logonForm". Este nombre se usa como un nombre de atributo de sesi�n o solicitud para el bean de formulario.
La secci�n "global-forwards" se usa para crear mapeos de nombres l�gicos para p�ginas JSP usadas comunmente. Cada uno de estos reenv�os est� disponible a trav�s de una llamada a nuestro ejemplar de mapeo de action, por ejemplo actionMappingInstace.findForward("logicalName").
Como podemos ver, este mapeo corresponde con el path /logon (realmente, porque la aplicaci�n de ejemplo usa mapeo de extensi�n, la URI que especificamos en una p�gina JSP terminar�a en /logon.do). Cuando se recibe una solicitud que corresponde con el path, se crea un ejemplar de LogonAction (s�lo la primera vez). El Servlet controlador buscar� un bean de �mbito de sesi�n bajo la clave logonForm, creando y guardando un bean de la clase especificada si es necesario.
Opcionales pero muy �tiles son los elementos localizados en "forward". En la aplicaci�n de ejemplo, muchas acciones incluyen un reenvio local "success" y/o "failure" como parte de un mapeo de Action.
<!-- Edit mail subscription --> <action path="/editSubscription" type="org.apache.struts.example.EditSubscriptionAction" name="subscriptionForm" scope="request" validate="false"> <forward name="failure" path="/mainMenu.jsp"/> <forward name="success" path="/subscription.jsp"/> </action>
Usando estas dos propiedades extras, las clases Action de la aplicaci�n de ejemplo son casi totalmente independientes de los nombres reales de las p�ginas JSP que son usadas por los dise�adores, Las p�ginas, pueden renombrarse (por ejemplo) durante un redise�o, con un m�nimo impacto en las propias clases Action. Si los nombres de las p�ginas JSP "next" estuvieran codificados dentro de las clases Action, todas estas clases tendr�an que ser modificadas. Por supuesto, podemos definir cualquier propiedad de reenv�o local que tenga sentido para nuestra aplicaci�n.
Una secci�n m�s de buen uso es la secci�n <data-sources>, que especifica las fuentes de datos que puede usar nuestra aplicaci�n. Aqu� podemos ver c�mo especificar una fuente de datos para nuestra aplicaci�n dentro de struts-config.xml:
<struts-config> <data-sources> <data-source autoCommit="false" description="Example Data Source Description" driverClass="org.postgresql.Driver" maxCount="4" minCount="2" password="mypassword" url="jdbc:postgresql://localhost/mydatabase" user="myusername"/> </data-sources> </struts-config>
�Descriptor de Despliegue de la Aplicaci�n Web
El paso final en la configuraci�n de la aplicaci�n es configurar el descriptor de despliegue (almacenado en el fichero WEB-INF/web.xml) para incluir todos los componentes Struts que son necesarios. Usando el descriptor de despliegue del la aplicaci�n de ejemplo como gu�a, veremos que se necesitan crear o modificar la siguientes entradas.
�Configurar el Ejemplar de Action Servlet
A�adimos una entrada definiendo el propio servlet action, junto con los par�metros de inicializaci�n apropiados. Dicha entrada se podr�a parecer a esto:
<servlet> <servlet-name>action</servlet-name> <servlet-class> org.apache.struts.action.ActionServlet </servlet-class> <init-param> <param-name>application</param-name> <param-value> org.apache.struts.example.ApplicationResources </param-value> </init-param> <init-param> <param-name>config</param-name> <param-value> /WEB-INF/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>mapping</param-name> <param-value> org.apache.struts.example.ApplicationMapping </param-value> </init-param> <load-on-startup>2</load-on-startup> </servlet>
Los par�metros de inicializaci�n soportados por el servlet controlador se describen abajo. Los corchetes cuadrados describen los valores por defecto que se asumen si no proporcionamos un valor para el par�metro de inicializaci�n.
- application - El nombre de la clase Java para la clase base del paquete de recursos de la aplicaci�n. [NONE].
- bufferSize - El tama�o del buffer de entrada usado para procesar uploads de ficheros. [4096].
- config - Path relativo al contexto del recurso XML que contiene nuestra informaci�n de configuraci�n.[/WEB-INF/struts-config.xml].
- content - Tipo de contenido por defecto y codificaci�n de caracteres a seleccionar en cada respuesta; podr�a ser sobreescrito por un servlet re-enviado o una p�gina JSP. [text/html].
- debug - El nivel de detalle de depuraci�n para este servlet, que controla cuanta informaci�n se pone en el log. [0].
- detail - El nivel de detalles de depuraci�n para el Digester que utilizamos en initMapping(), que sale por System.out en lugar de servlet log. [0].
- factory - El nombre de la clase Java del MessageResourcesFactory usado para crear el objeto MessageResources de la aplicaci�n. [org.apache.struts.util.PropertyMessageResourcesFactory].
- formBean - El nombre de la clase Java de la implementaci�n de ActionFormBean a utilizar. [org.apache.struts.action.ActionFormBean].
- forward - el nombre de la clase Java de la implemetnaci�n de ActionForward a utilizar. [org.apache.struts.action.ActionForward]. Podr�amos usar aqu� dos clases de conveniencia:
- org.apache.struts.action.ForwardingActionForward - Subclase de org.apache.struts.action.ActionForward que por defecto pone la propiead redirect a false (lo mismo que el valor por defecto de ActionForward).
- org.apache.struts.action.RedirectingActionForward - Subclase de org.apache.struts.action.ActionForward que por defecto pone la propiedad redirect a true.
- locale - Si se selecciona a true, y hay una sesi�n de usuario, indentifica y almacena un objeto java.util.Locale apropiado (bajo la clave est�ndard indentificada por Action.LOCALE_KEY) en la sesi�n de usuario si no hay ya un objeto Locale. [true]
- mapping - El nombre de la clase Java de la implementaci�n del ActionMapping a utilizar. [org.apache.struts.action.ActionMapping]. Podr�amos usar aqu� dos clases de conveniencia:
- org.apache.struts.action.RequestActionMapping - Subclase de org.apache.struts.action.ActionMapping que por defecto deja la propiedad scope a "request".
- org.apache.struts.action.SessionActionMapping - Subclase de org.apache.struts.action.ActionMapping que por defecto deja la propiedad scope a "session". (Igual que el valor por defecto de ActionMapping).
- maxFileSize - El tama�o m�ximo (en bytes) para que un ficheo sea aceptado para upload. Puede expresarse como un n�mero seguido por una K" "M", o "G", que ser�n interpretadas como kilobytes, megabytes, o gigabytes, respectivamente. [250M].
- multipartClass - El nombre totalmente cualificado de la clase de la implementaci�n de MultipartRequestHandler usado para procesar uploads de ficheros. [org.apache.struts.upload.DiskMultipartRequestHandler].
- nocache - Si se selecciona a true, a�ade cabeceras HTTP a cada respuesta para evitar que el navegador almacene en el cah� cualquier respuesta generado o reenviada. [false].
- null - Si se selecciona a true, configura los recursos de nuestra aplicaci�n a devolver null si se usa una clave de mensaje desconocida. De otra forma, se devolver� un mensaje de error incluyendo la clave err�nea. [true].
- tempDir - El directorio de trabajo temporal usado cuando se procesan uploads de ficheros. [El directorio de trabajo proporcionado para esta aplicaci�n web como atributo contexto del servlet].
- validate - �Est�mos suando el nuevo formato de fichero de configuraci�n? [true].
- validating - �Deber�amos usar un analizador con validaci�n XML para procesar el fichero de configuraci�n (altamente recomendado? [true].
�Configurar el Mapeo del Servlet Action
Nota: El material de esta secci�n no es espec�fico de Struts. La configuraci�n del mapeo de servlets est� definida en la Java Servlet Specification. Esta secci�n describe los significados m�s comunes de configuraci�n de una aplicaci�n Struts.
Hay dos aproximaciones comunes para definir las URLS que ser�n procesadas por el servlet controlador -- correspondencia de prefijo y correspondencia de extensi�n.
La correspondencia de prefijo significa que queremos que todas las URLs que empiecen con (despu�s de la parte del path de contexto) un valor particular sean pasadas a este servlet. Dicha entrada se podr�a parecer a esto:
<servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>/execute/*</url-pattern> </servlet-mapping>
lo que significa que una URI que coincida con el path /logon descrito anteriormente podr�a parecerse a esto:
http://www.mycompany.com/myapplication/execute/logon
donde /myapplication es el path de contexto bajo el que se ha desplegado nuestra aplicaci�n.
Por otro lado, en el mapeo por extensi�n, se renvian las URIs solicitadas al servlet action bas�ndose en el hecho de que la URI termine en un punto seguido por un conjunto defindo por caracteres. Por ejemplo, el servlet de procesamiento JSP est� mapeado al patr�n *.jsp para que sea llamado cada vez que se solicite una p�gina JSP. Para usar la extensi�n *.do (que implica "hacer algo"), la entrada de mapeo se podr�a parecer a esta:
<servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
y una URI que corresponda con el path /logon descrito anteriormente se parecer�a a esto:
http://www.mycompany.com/myapplication/logon.do
�Configurar la Librer�a de Etiquetas de Struts
Luego, debemos a�adir una entrada definiendo la librer�a de etiquetas Struts. Actualmente hay cuatro librer�as que vienen con Struts.
La librer�a struts-bean contiene etiquetas �tiles para acceder a los beans y sus propiedades, as� como para definir nuevos beans (basados en esos accesores) que son accesibles para el resto de la p�gina mediante variables de scripting y atributos de �mbito de p�gina. Tambi�n se proporcionan mecanismos convenientes para crear nuevos beans basados en el valor de una cookie, de las cabeceras y de los par�metros.
La librer�a struts-html contiene etiquetas para crear formularios de entrada struts, as� como otras etiquetas generalmente �tiles en la creaci�n de interfaces de usuario basados en HTML.
La librer�a struts-logic contiene etiquetas que son �tiles para manejar la generaci�n condicional de salida de texto, hacer bucles sobre colecciones de objetos para generaci�n repetitiva de salida de texto y control del flujo de la aplicaci�n.
La librer�a struts-template contiene etiquetas que definen un mecanismo de plantillas.
Abajo podemos ver c�mo se definir�an todas las librer�as de etiquetas para usarlas en nuestra aplicaci�n, en realidad, s�lo deber�amos especificar las librer�as que vayamos a utilizar:
<taglib> <taglib-uri> /WEB-INF/struts-bean.tld </taglib-uri> <taglib-location> /WEB-INF/struts-bean.tld </taglib-location> </taglib> <taglib> <taglib-uri> /WEB-INF/struts-html.tld </taglib-uri> <taglib-location> /WEB-INF/struts-html.tld </taglib-location> </taglib> <taglib> <taglib-uri> /WEB-INF/struts-logic.tld </taglib-uri> <taglib-location> /WEB-INF/struts-logic.tld </taglib-location> </taglib> <taglib> <taglib-uri> /WEB-INF/struts-template.tld </taglib-uri> <taglib-location> /WEB-INF/struts-template.tld </taglib-location> </taglib>
Esto le dice al sistema JSP donde encontrar el descritor de librer�a de etiqueta para esta librer�a (en nuestro directorio WEB-INF de la aplicaci�n, en vez de en alg�n lugar exterior en Internet).
�A�adir Componentes Struts a nuestra Aplicaci�n
Para usar Struts, debemos copiar los ficheros .tld que necesitamos en nuestro directorio WEB-INF, y copiar struts.jar (y todos los otros ficheros commons-*.jar) en nuestro directorio WEB-INF/lib.