domingo, 3 de marzo de 2013

Un proyecto en NetBeans de principio a fin (IV). Páginas y servlet del controlador

Me temo que esta cuarta entrega va a ser enorme... Tenemos mucho que hacer aquí. Nuestro objetivo es empezar a organizar el front-end de la aplicación, preparando las vistas de las páginas y a situándolas en la estructura apropiada del proyecto (usaremos HTML y CSS para esto). La idea es tener una maqueta, ahora con código fuente, de los esquemas que obtuvimos en la fase de diseño. Crearemos un pie y una cabecera que aplicaremos a todas las vistas, y configuraremos el servlet del controlador para las peticiones entrantes. Empecemos sin más preámbulos. 


CREANDO LOS FICHEROS DEL PROYECTO 

Creación de las páginas JSP 

Esto no tiene la mayor ciencia. La página index.jsp fue generada por el IDE y será nuestra página de bienvenida. Ahora crearemos páginas JSP para las cuatro vistas restantes y, de momento, las ubicaremos en el directorio raíz del proyecto (junto con el index).

A través del asistente crearemos los ficheros category.jsp, cart.jsp, checkout.jsp y confirmation.jsp (New File>Web>JSP)

Creación de un fichero CSS 

Dentro del nodo Web Pages creamos una nueva carpeta que llamaremos "css". Haciendo click derecho sobre esta carpeta podremos crea un nuevo fichero de estilos (New>Cascading Style Sheet) al que llamaremos "affablebean.css".

IMPLEMENTANDO CONTENIDO HTML Y CSS 

Presupondré en este punto ciertos conocimientos de HTML y algo de CSS (Si no, ¿qué hacemos aquí? Más nos valdría volver a la fuente). A grandes rasgos, las plantillas de nuestras páginas serán una serie de etiquetas <div> convenientemente identificadas sobre las que aplicaremos una serie de estilos definidos en nuestro affablebean.css.

Aunque va a ser un poco cansino, a continuación está el código fuente de las páginas y la hoja de estilos, y la pinta que deberían algunas de las vistas. Sobre el aspecto de las páginas hay que decir que observaremos algunas diferencias -en ocasiones notorias- en función del browser que empleemos.

La implementación no tiene mayor problema. Simplemente es un trabajo algo laborioso -sobre todo el componer el fichero css sin que se nos quede descolgado ningún estilo-. Lo ideal es ir maquetando cada página con los <div> que precisemos, identificar cada capa y crear un estilo para ella (o aplicarle uno existente). Lo más difícil, a mi modo de ver, es tener claro en todo momento el aspecto visual de cada elemento. No en vano, la maquetación de páginas web es todo un arte que no está al alcance de todos (No hay más que ver, después de todos estos años del World Wide Web, la cantidad de adefesios que pululan por todas partes...).

Puesto que las capas que definen la cabecera y el pie de página son las mismas en todas las páginas, voy a incluir el código completo de la página index.jsp (más que nada por tener una composición de lugar y no perder de vista cuestiones como la referencia a la hoja de estilos). Para el resto de páginas sólo pondré la parte de código que cambia, por tener una idea visual de cómo y dónde aplicamos las reglas de estilo. A fin de cuentas, como veremos más adelante, usaremos un mecanismo para evitar repetir parte del código en todas las páginas.

Página "index.jsp"
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <link rel="stylesheet" type="text/css" href="css/affablebean.css">
        <title>The Affable Bean</title>
    </head>
    <body>
        <div id="main">
            <div id="header">
                <div id="widgetBar">
                    <div class="headerWidget">
                        [ language toggle ]
                    </div>
                    <div class="headerWidget">
                        [ checkout button ]
                    </div>
                    <div class="headerWidget">
                        [ shopping cart widget ]
                    </div>
                    <a href="#">
                        <img src="#" id="logo" alt="Affable Bean Logo">                        
                    </a>
                    <img src="#" id="logoText" alt="the affable bean">
                </div>
            </div>
            <div id="indexLeftColumn">
                <div id="welcomeText">
                    <p>[ welcome text ]</p>
                </div>
            </div>

            <div id="indexRightColumn">
                <div class="categoryBox">
                    <a href="#">
                        <span class="categoryLabelText">dairy</span>
                    </a>
                </div>
                <div class="categoryBox">
                    <a href="#">
                        <span class="categoryLabelText">meats</span>
                    </a>
                </div>
                <div class="categoryBox">
                    <a href="#">
                        <span class="categoryLabelText">bakery</span>
                    </a>
                </div>
                <div class="categoryBox">
                    <a href="#">
                        <span class="categoryLabelText">fruit & veg</span>
                    </a>
                </div>
            </div>
            <div id="footer">
                <hr>
                <p id="footerText">[ footer text ]</p>
            </div>
        </div>
    </body>
</html>

Página "category.jsp"
            <div id="categoryLeftColumn">
                <div class="categoryButton" id="selectedCategory">
                    <span class="categoryText">dairy</span>
                </div>
                <a href="#" class="categoryButton">
                    <span class="categoryText">meats</span>
                </a>

                <a href="#" class="categoryButton">
                    <span class="categoryText">bakery</span>
                </a>

                <a href="#" class="categoryButton">
                    <span class="categoryText">fruit & veg</span>
                </a>
            </div>

            <div id="categoryRightColumn">
                <p id="categoryTitle">[ selected category ]</p>
                <table id="productTable">
                    <tr>
                        <td class="lightBlue">
                            <img src="#" alt="product image">
                        </td>
                        <td class="lightBlue">
                            [ product name ]
                            <br>
                            <span class="smallText">[ product description ]</span>
                        </td>
                        <td class="lightBlue">[ price ]</td>
                        <td class="lightBlue">
                            <form action="#" method="post">
                                <input type="submit" value="purchase button">
                            </form>
                        </td>
                    </tr>

                    <tr>
                        <td class="white">
                            <img src="#" alt="product image">
                        </td>
                        <td class="white">
                            [ product name ]
                            <br>
                            <span class="smallText">[ product description ]</span>
                        </td>
                        <td class="white">[ price ]</td>
                        <td class="white">
                            <form action="#" method="post">
                                <input type="submit" value="purchase button">
                            </form>
                        </td>
                    </tr>

                    <tr>
                        <td class="lightBlue">
                            <img src="#" alt="product image">
                        </td>
                        <td class="lightBlue">
                            [ product name ]
                            <br>
                            <span class="smallText">[ product description ]</span>
                        </td>
                        <td class="lightBlue">[ price ]</td>
                        <td class="lightBlue">
                            <form action="#" method="post">
                                <input type="submit" value="purchase button">
                            </form>
                        </td>
                    </tr>

                    <tr>
                        <td class="white">
                            <img src="#" alt="product image">
                        </td>
                        <td class="white">
                            [ product name ]
                            <br>
                            <span class="smallText">[ product description ]</span>
                        </td>
                        <td class="white">[ price ]</td>
                        <td class="white">
                            <form action="#" method="post">
                                <input type="submit" value="purchase button">
                            </form>
                        </td>
                    </tr>
                </table>
            </div>


Página "cart.jsp"
<div id="centerColumn">
                <p>Your shopping cart contains x items.</p>
                <div id="actionBar">
                    <a href="#" class="bubble hMargin">clear cart</a>
                    <a href="#" class="bubble hMargin">continue shopping</a>
                    <a href="#" class="bubble hMargin">proceed to checkout</a>
                </div>
                <h4 id="subtotal">[ subtotal: xxx ]</h4>

                <table id="cartTable">

                    <tr class="header">
                        <th>product</th>
                        <th>name</th>
                        <th>price</th>
                        <th>quantity</th>
                    </tr>

                    <tr>
                        <td class="lightBlue">
                            <img src="#" alt="product image">
                        </td>
                        <td class="lightBlue">[ product name ]</td>
                        <td class="lightBlue">[ price ]</td>
                        <td class="lightBlue">

                            <form action="updateCart" method="post">
                                <input type="text"
                                       maxlength="2"
                                       size="2"
                                       value="1"
                                       name="quantity">
                                <input type="submit"
                                       name="submit"
                                       value="update button">
                            </form>
                        </td>
                    </tr>

                    <tr>
                        <td class="white">
                            <img src="#" alt="product image">
                        </td>
                        <td class="white">[ product name ]</td>
                        <td class="white">[ price ]</td>
                        <td class="white">

                            <form action="updateCart" method="post">
                                <input type="text"
                                       maxlength="2"
                                       size="2"
                                       value="1"
                                       name="quantity">
                                <input type="submit"
                                       name="submit"
                                       value="update button">
                            </form>
                        </td>
                    </tr>

                    <tr>
                        <td class="lightBlue">
                            <img src="#" alt="product image">
                        </td>
                        <td class="lightBlue">[ product name ]</td>
                        <td class="lightBlue">[ price ]</td>
                        <td class="lightBlue">

                            <form action="updateCart" method="post">
                                <input type="text"
                                       maxlength="2"
                                       size="2"
                                       value="1"
                                       name="quantity">
                                <input type="submit"
                                       name="submit"
                                       value="update button">
                            </form>
                        </td>
                    </tr>

                </table>
            </div>

Página "checkout.jsp"
<div id="centerColumn">
                <h2>checkout</h2>

                <p>[ text ]</p>

                <form action="purchase" method="post">

                    <table id="checkoutTable">
                        <tr>
                            <td>[ form containing fields to
                                <br>capture customer details ]</td>
                        </tr>
                        <tr>
                            <td></td>
                        </tr>
                        <tr>
                            <td><input type="submit" value="submit button"></td>
                        </tr>

                    </table>

                </form>

                <div id="infoBox">

                    <div style="border: black solid 1px; height:100px; padding: 10px">
                        [ purchase conditions ]
                    </div>

                    <div id="priceBox">
                        [ purchase calculations:<br>subtotal + delivery charge ]
                    </div>
                </div>
            </div>

Página "confirmation.jsp"
<div id="singleColumn">
                <p id="confirmationText">
                    [ text ]
                    <br><br>
                    [ order reference number ]
                </p>

                <div class="summaryColumn" >

                    <table id="orderSummaryTable" class="detailsTable" >
                        <tr class="header">
                            <th style="padding:10px">[ order summary table ]</th>
                        </tr>
                    </table>

                </div>

                <div class="summaryColumn" >

                    <table id="deliveryAddressTable" class="detailsTable">
                        <tr class="header">
                            <th style="padding:10px">[ customer details ]</th>
                        </tr>
                    </table>
                </div>
            </div>

Página "affablebean.css"
root { 
    display: block;
}
/* Etiquetas HTML */
body {
    font-family: Arial, Helvetica, sans-serif;
    width: 850px;
    text-align: center;
    margin: 20px auto;
}
hr {
    border: 0;
    background-color: #333;
    height: 1px;
    margin: 0 25px;
    width: 300px;
}
table {
    margin: 0 20px;
    border-spacing: 0;
    text-align: center;
    border: solid 1px #f5eabe;
}
/* Estilos generales */
#main {background: #eee}
#singleColumn {
    margin: 20px 30px;
    text-align: left;
}
.lightBlue { background-color: #edf8f7 }
.white { background-color: #fff }
.bubble {
    font-weight: bold;
    background-color: #f5eabe;
    padding: 5px;
    color: inherit;
}
.hMargin { margin: 0 30px }
.smallText { font-size: small }
.header {
    background-color: #c3e3e0;
    height: 30px;
}
/* Estilos de elementos contenidos en la cabecera */
#header {
    height: 250px;
    background: #aaa;    
}
#logo {
    height: 155px;
    width: 155px;
    float: left;
    margin-left: 30px;
    margin-top: 20px;
}
#logoText {
    float: left;
    margin: 20px 0 0 70px;
    /*estilos de fuentes aplicados al texto dentro de la etiqueta "alt".*/
    font-family: 'American Typewriter', Courier, monospace;
    font-size: 50px;
    color: #333;
}
#widgetBar {
    height: 50px;
    width: 850px;
    float: right;
    background: #ccc;
}
.headerWidget {
    width: 194px;
    margin: 20px 2px;
    font-size: small;
    float: right;
    line-height: 25px;
    background: #aaa;
}
/* Estilos para el pie de página */
#footer {
    height: 60px;
    width: 350px;
    clear: left;
    background: #aaa;
}
/* Estilos para la página index */
#indexLeftColumn {
    height: 400px;
    width: 350px;
    float: left;
    background: #ccc;
}
#indexRightColumn {
    height: 400px;
    width: 500px;
    float: left;
    background: #eee;    
}
.categoryBox {
    height: 176px;
    width: 212px;
    margin: 21px 14px 6px;
    float: inherit;
    background: #ccc;
}
.categoryLabelText {
    line-height: 150%;
    font-size: x-large;
}
/* Estilos para la página de categorías */
#categoryLeftColumn {
    width: 185px;
    float: left;
    margin-top: 25px;
    padding-left: 15px;
}
#categoryRightColumn {
    margin-top: 10px;
    width: 650px;
    float: left;
}
.categoryButton {
    margin: 15px 22px;
    padding: 13px;
    display: block;
    background-color: #d3ede8;
}
#selectedCategory {
    background-color: #b2d2d2;
    margin-left: 10px;
    width: 139px;
}
#categoryTitle {
    margin: -34px 180px 0 0;
    font-size: x-large;
    float: right;
    background-color: #f5eabe;
    padding: 7px;
}
.categoryText {
    line-height: 25px;
    font-size: x-large;
}
#productTable { 
    width: 600px; 
}
#productTable tr { 
    height: 90px; 
}
#productoTable td { 
    width: 145px; 
}
/* cart page styles */
#actionBar {
    margin: 30px;
    width: 750px;
    text-align: center;
}
#subtotal { margin: 40px 0 20px 430px }
#cartTable { width: 750px }
#cartTable td {
    width: 145px;
    height: 90px;
}
/* checkout page styles */
#checkoutTable {
    width: 360px;
    background-color: #f5eabe;
    float: left;
    height: 280px;
}
#infoBox {
    width: 300px;
    padding-left: 30px;
    float: left;
}
#priceBox {
    padding: 10px;
    margin: 10px 0;
    height: 128px;
    background-color: #c3e3e0;
}
/* confirmation page styles */
#confirmationText {
    margin: 0 20px 20px;
    padding: 10px;
    background-color: #f5eabe;
    float: left;
    width:540px;
}
.summaryColumn {
    margin-top: 15px;
    width: 50%;
    float: left;
}
#orderSummaryTable {
    width: 100%;
    text-align: left;
    height: 200px;
}
#deliveryAddressTable {
    width: 70%;
    margin-left: 15%;
    text-align: left;
    height: 200px;
}

Podemos comprobar el aspecto de nuestras maquetas ejecutando en proyecto (Run Proyect) o ejecutando cada página por separado en el navegador (botón derecho en la ventana del código y después Run File).

Netbeans proporciona un interesante soporte para trabajar con CSS. Tenemos dos ventanas que pueden ser particularmente útiles:

La Previsualización de CSS nos permite ver el aspecto de las reglas de estilo tal y como serán renderizadas en el navegador (Window>Other>CSS Preview). Cuando posicionamos el cursor sobre una regla de estilo en el editor, la previsualización se refresca automáticamente para mostrar un ejemplo de texto acorde a las propiedades definidas en la regla.

El Constructor de Estilo CSS (Window>Other>CSS Style Builder) es muy interesante si no nos apetece enfangarnos codificando las reglas de estilo a mano. Esta herramienta nos permite construir reglas escogiendo propiedades y valores en una interfaz gráfica.

COLOCANDO LAS PÁGINAS JSP EN LA CARPETA WEB-INF

Si echamos un vistazo a las maquetas de las páginas que hemos creado, se puede apreciar que la página de bienvenida (nuestro index.jsp) siempre debe presentarse de la misma forma cuando se muestra, sea quien sea el que haga la petición. Esto es, su aspecto no viene determinado por la sesión de un usuario. Sin embargo, las otras páginas necesitan, de uno u otro modo, información específica de un usuario, esto es, su aspecto viene determinado por una sesión de usuario. Estas páginas no pueden ser convenientemente renderizadas por el servidor si éste no es capaz de relacionar la información específica de un usuario con una petición entrante. Y obviamente, no queremos que esas páginas puedan ser accedidas directamente a través de la barra de direcciones del navegador (por ejemplo poniendo: "http://localhost:8080/AffableBean/WEB-INF/view/category.jsp"...). Este es precisamente el objetivo de la carpeta WEB-INF. Cualquier recurso contenido en esta carpeta no será accesible directamente desde un browser.

Podemos crear una nueva carpeta -que llamaremos "view"- dentro de WEB-INF, y meter dentro nuestras páginas category.jsp, cart.jsp, checkout.jsp y confirmation.jsp. De esta manera esas páginas ya no estarán accesibles desde el navegador, y si ponemos en la barra de direcciones algo como "http://localhost:8080/AffableBean/WEB-INF/view/category.jsp", sólo obtendremos un contundente "HTTP Status 404" indicándonos que el recurso no está disponible.

CREANDO LA CABECERA Y EL PIE DE PÁGINA

En base a las maquetas de las páginas, podemos ver que nuestras cinco vistas comparten una serie de contenidos idénticos. En la parte superior, el logo de la compañía, selección de lenguaje y otros widgets asociados con el proceso de compra. En la parte inferior hay algo de texto con las políticas de privacidad, contacto y cosas así. En lugar de repetir este código, una y otra vez, en cada página, podemos extraerlo a dos fragmentos JSP, uno para la cabecera y otro para el pie de página. Después incluiremos estos fragmentos dentro de las vistas de las páginas donde sean necesarios.

Para esto podemos crear una nueva carpeta ("jspf") dentro de WEB-INF. Luego crearemos dos ficheros jspf ("header.jpsf" y "footer.jspf") con el código de la cabecera y el pie de página (New File>Web>JSP y marcar la opción Create as a JSP Segment).

A continuación podríamos copiar el código de la cabecera y el pie de página de cualquiera de las páginas JSP y pegarlo en los correspondientes ficheros .jspf. Los ficheros header.jspf y footer.jspf quedarían más o menos así:

"header.jsp"
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <link rel="stylesheet" type="text/css" href="css/affablebean.css">
        <title>The Affable Bean</title>
    </head>
    <body>
        <div id="main">
            <div id="header">
                <div id="widgetBar">
                    <div class="headerWidget">
                        [ language toggle ]
                    </div>
                    <div class="headerWidget">
                        [ checkout button ]
                    </div>
                    <div class="headerWidget">
                        [ shopping cart widget ]
                    </div>
                    <a href="#">
                        <img src="#" id="logo" alt="Affable Bean Logo">                        
                    </a>
                    <img src="#" id="logoText" alt="the affable bean">
                </div>
            </div>

"footer.jsp"
<div id="footer">
                <hr>
                <p id="footerText">[ footer text ]</p>
            </div>
        </div>
    </body>
</html>

Después borraríamos el código del header y del footer de todas las páginas JSP.

AÑADIENDO UNA DIRECTIVA AL DESCRIPTOR DE DESPLIEGUE 

Bien, ya hemos sacado la cabecera y el pie de página de nuestros ficheros JSP, eliminando de un plumazo ese código redundante que tanto nos molesta ¿Y ahora qué? Nuestra aplicación todavía necesita saber qué páginas requieren una cabecera y un pie de página... Podríamos añadir etiquetas <jsp:incluye>, pero haciendo esto podríamos reintroducir esa repetición de código que tanto nos hemos esforzado en suprimir... Afortunadamente tenemos otra alternativa. Podríamos crear un descriptor de despliegue, un web.xml, en el que añadir una directiva con un grupo de propiedades JSP que especifique que vistas de páginas necesitan los fragmentos de cabecera y pie de página.

El archivo web.xml proporciona la configuración y el despliegue de información para los componentes web que conforman una aplicación.

A través del asistente de Nuevo Archivo crearemos un Descriptor de Despliegue Estándar (New File>Web>Standard Deployment Descriptor (web.xml)) que aparecerá en la carpeta WEB-INF. Se nos abrirá, en la ventana del editor, una interfaz que nos dará acceso a diferentes pestañas para trabajar con el XML (servlets, filtros, referencias, seguridad...). Si estos ítems no están accesibles en forma de pestañas, podemos hacer click en el botón derecho del ratón sobre la pestaña del web.xml y buscar en el menú contextual la opción Editors (también podemos seleccionar, en la barra de menú del IDE la opción View>Show Editor Toolbar).

Si elegimos la opción "pages" podremos agregar un nuevo JSP Property Group. Añadiremos una descripción (el nombre es opcional) y, en URL Patterns especificaremos los path de todas nuestras páginas separados por comas, teniendo en cuenta que aquí podemos usar patrones del estilo de: /index.jsp, /WEB-INF/view/* (No hace falta ni explicar el uso del asterisco como carácter comodín. Creo que eso ya lo superamos con las primeras versiones del MS-DOS allá por los 80...).

Cuando pulsemos "OK", veremos cómo se actualiza nuestro código XML:
<jsp-config>
    <jsp-property-group>
        <description>header and footer settings</description>
        <url-pattern>/index.jsp</url-pattern>
        <url-pattern>/WEB-INF/view/*</url-pattern>
    </jsp-property-group>
</jsp-config>

Si seleccionamos otra vez la opción "pages" veremos un formulario más extenso para el JSP Group que acabamos de crear. En los campos Include Preludes e Include Codas podemos poner los path de los ficheros header.jspf y footer.jpsf. Volviendo al código XML, vemos que este ha vuelto a cambiar:
<jsp-config>
    <jsp-property-group>
        <description>header and footer settings</description>
        <url-pattern>/index.jsp</url-pattern>
        <url-pattern>/WEB-INF/view/*</url-pattern>
        <include-prelude>/WEB-INF/jspf/header.jspf</include-prelude>
        <include-coda>/WEB-INF/jspf/footer.jspf</include-coda>
    </jsp-property-group>
</jsp-config>

Después de todo esto, si ejecutamos de nuevo la aplicación, podremos ver que la cabecera y el pie de página están justo donde deben estar.

CREANDO EL SERVLET DEL CONTROLADOR

Si recordamos nuestro diagrama MVC, la función del servlet del controlador es encargarse de las peticiones entrantes, inicializando cualquier acción necesaria para generar el modelo de respuesta, para enviarlo después a la vista apropiada.

Netbeans, como en otras ocasiones tiene un asistente para definir un componente servlet en nuestra aplicación web (New File>Web>Servlet… Ojalá tuviéramos los mismos wizard para otros tantos avatares de la vida... ¡ains!). Existen dos formas de hacer esto: incluir la anotación @WebServlet en las clases que se generen, o añadir las directivas correspondientes a nuestro descriptor de despliegue web.xml. Nosotros optaremos por las anotaciones.

Nombraremos a nuestro servlet como "ControlerServlet" y lo ubicaremos en un nuevo package que llamaremos "controller". En el siguiente paso tendrá lugar algo de vital importancia para la configuración del servlet: La especificación de los Patrones URL (URL Patterns). Estos patrones identifican las URLs que invocan al servlet. Por ejemplo, si introducimos "/category", estaremos indicándole al servlet que debe manejar una petición de la forma http://localhost/affableBean/category.

Los patrones URL deben corresponderse con las acciones y las vistas que un usuario puede poner en marcha. Si miramos, por ejemplo, es esquema de la página de bienvenida, un usuario debe ser capaz de seleccionar una categoría. Podemos por tanto asociar la URL /category con la acción de seleccionar una imagen de categoría. Así mismo, en la página de categorías, un usuario debe poder añadir un elemento a su carrito, tiene pues sentido especificar el patrón /addToCart...

Podemos por tanto empezar agregando una serie de patrones -separados por comas- en el campo URL Patterns: "/category", "/addToCart" y "viewCart". Añadiremos algunos más cuando el servlet esté creado.

Al finalizar el asistente podremos ver el aspecto de nuestro ControllerServlet.java con sus URL Patterns:
@WebServlet(name="ControllerServlet", urlPatterns={"/category", "/addToCart", "/viewCart"})

Si antes de finalizar el asistente hubiéramos habilitado la casilla para añadir información al descriptor de despliegue, nuestro web.xml habría engordado con líneas como estas:
<servlet>
    <servlet-name>ControllerServlet</servlet-name>
    <servlet-class>controller.ControllerServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>ControllerServlet</servlet-name>
    <url-pattern>/category</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>ControllerServlet</servlet-name>
    <url-pattern>/addToCart</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>ControllerServlet</servlet-name>
    <url-pattern>/viewCart</url-pattern>
</servlet-mapping>

Sin embargo nosotros hemos optado por la alternativa de las anotaciones así que desterremos esto al baúl de las cosas que pudieron ser y no fueron…

A continuación podemos añadir algunos patrones URL más a nuestro servlet: "/updateCart", "/checkout", "/purchase" y "/chooseLanguage".

Y para terminar incluiremos el elemento loadOnStartup. Un valor de 0 o más, propicia que el servlet sea instanciado e inicializado cuando se despliegue la aplicación (su valor por defecto es -1).

Con estos últimos retoques y el conveniente reformateo, la cosa quedaría más o menos así:
@WebServlet(name="ControllerServlet",
            loadOnStartup = 1,
            urlPatterns = {"/category",
                           "/addToCart",
                           "/viewCart",
                           "/updateCart",
                           "/checkout",
                           "/purchase",
                           "/chooseLanguage"})


IMPLEMENTANDO EL SERVLET DEL CONTROLADOR

Si echamos un vistazo al código generado en ControllerServlet.java, podemos ver que la plantilla emplea un método processRequest que es llamado por los métodos doGet y doPost. Dado que nuestra aplicación va a diferenciar entre doGet y doPost, nosotros añadiremos código directamente en esos métodos, y nos quitaremos de en medio el método processRequest.

Ahora que ya hemos mapeado los patrones URL en el servlet usando la anotación @WebServlet, tenemos que configurarlo para manejar esos patrones e instanciar un RequestDispatcher para enviar los patrones solicitados a la vista apropiada.

Vamos a reescribir la clase ControlerServlet como se detalla a continuación:

package controller;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@WebServlet(name = "ControllerServlet", 
            loadOnStartup = 1,
            urlPatterns = { "/category", 
                            "/addToCart", 
                            "/viewCart",
                            "/updateCart",
                            "/checkout",
                            "/purchase",
                            "/chooseLanguage"})
public class ControllerServlet extends HttpServlet {
    /**
     * Handles the HTTP GET method.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {

        String userPath = request.getServletPath();

        // if category page is requested
        if (userPath.equals("/category")) {
            // TODO: Implement category request
        // if cart page is requested
        } else if (userPath.equals("/viewCart")) {
            // TODO: Implement cart page request
            userPath = "/cart";
        // if checkout page is requested
        } else if (userPath.equals("/checkout")) {
            // TODO: Implement checkout page request
        // if user switches language
        } else if (userPath.equals("/chooseLanguage")) {
            // TODO: Implement language request
        }

        // use RequestDispatcher to forward request internally
        String url = "/WEB-INF/view" + userPath + ".jsp";

        try {
            request.getRequestDispatcher(url).forward(request, response);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Handles the HTTP POST method.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {

        String userPath = request.getServletPath();

        // if addToCart action is called
        if (userPath.equals("/addToCart")) {
            // TODO: Implement add product to cart action
        // if updateCart action is called
        } else if (userPath.equals("/updateCart")) {
            // TODO: Implement update cart action
        // if purchase action is called
        } else if (userPath.equals("/purchase")) {
            // TODO: Implement purchase action
            userPath = "/confirmation";
        }

        // use RequestDispatcher to forward request internally
        String url = "/WEB-INF/view" + userPath + ".jsp";

        try {
            request.getRequestDispatcher(url).forward(request, response);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

En próximas entradas de este tutorial volveremos a la clase ControlerServlet para implementar cada uno de los patrones URL individualmente. Entre tanto echaremos un vistazo al código que hemos escrito, porque hay varias cosas que merece la pena comentar.
  • El servlet utiliza la variable userPath para obtener el patrón URL solicitado por el cliente. Esta variable es usada por los dos métodos doGet y doPost.
  • En principio, los patrones asociados con la petición de páginas son gestionados en el método doGet. Así, por ejemplo, los patrones /category, /viewCart y /checkout dan lugar a la presentación de las páginas de categorías, carrito y pago respectivamente.
  • Los patrones asociados con formularios de envío y transporte de datos sensibles son gestionados por el método doPost. Tal es el caso de /addToCart, /updateCart y /purchase.
  • Tanto para el método doGet como para el método doPost, el path para llegar a la vista apropiada se forma usando una cadena url.
  • El RequestDispatcher se obtiene de HttpServletRequest y usa la url para enviar la petición.
  • Las notas "TODO" nos señalan, en el código, el trabajo que todavía resta por hacer. Esto, junto con la ventana de tareas del IDE (Window>Action Items), es un modo eficaz de hacer un seguimiento de tareas pendientes.

Sólo queda comprobar si el ControllerServlet está enviando las peticiones a las vistas apropiadas. Para ello podemos ejecutar el proyecto y, una ver abierto el navegador, probar a poner las distintas URLs en la barra de direcciones, por ejemplo: http://localhost:8080/AffableBean/category
o http://localhost:8080/AffableBean/viewCart

Nótese que si ponemos http://localhost:8080/AffableBean/purchase no se nos permitirá ver la página de confirmación. Naturalmente, esto es porque el patrón URL /purchase en el método doPost del servlet, y las peticiones enviadas desde la barra de direcciones del navegador usan el método HTTP GET.

Y por hoy creo que ya es suficiente. Ahora que ya hemos creado las páginas JSP preparadas para acoger componentes funcionales; hemos configurado el front-end de la aplicación; hemos aprendido el uso de la carpeta WEB-INF; hemos sacado la cabecera y el pie de página a archivos separados; hemos configurado nuestro web.xml, y hemos preparado un servlet controlador capaz de manejar peticiones, ya podemos irnos a dormir, a tomar un tentempié, a ver una peli, a retozar con una moza (o mozo) o a hacer lo que sea que hayáis pospuesto para leeros (o en mi caso escribir) esta entrada interminable... Si todavía os queda paciencia, nos veremos en la siguiente entrada: "Un proyecto en NetBean de principio a fin. Parte V", en el que veremos cómo conectar la aplicación con la base de datos.

3 comentarios:

  1. no se si tendrías todo este ejemplo en JSF

    ResponderEliminar
  2. Lo siento Ronald, me temo que todo este "proyecto-ejemplo" no es obra mía. Me estoy limitando a traducir y explicar un desarrollo que está disponible en la web "netbeans.org", y lo estoy haciendo como ejercicio de auto-formación más que como difusión (que por cierto, tengo la próxima entrega bastante retrasada por complicaciones laborales y personales). Recibí hace un par de años un curso de JSF y me pareció sumamente interesante, desde luego sería genial un ejemplo de estas características sobre esa tecnología. Si retomo ese framework en algún momento seguro que dejo constancia por aquí.

    En cualquier caso, gracias por leernos.

    ResponderEliminar