Spring MVC, altres aplicacions

Partim de l’aplicació web Estoc de medicaments, que mostra una pàgina de benvinguda i. mitjançant l’URL adient, pot proporcionar la llista de tots els medicaments, els d’una categoria o els medicaments que compleixen cert filtre.

També pot mostrar el detall d’un medicament directament amb un URL o des de la llista de medicaments. Afegit a tot això, l’aplicació pot actualitzar l’estoc d’un medicament.

Aquesta aplicació segueix l’arquitectura i el procés de qualsevol aplicació Spring MVC. S’estructura en les quatre capes següents:

  • presentació (Presentation)
  • domini (Domain)
  • servei (Service)
  • persistència (Persistence)

Estoc de medicaments fa servir anotacions de Spring MVC:

  • @Controller, @Repository i @Service, que defineixen el tipus d’objecte Spring.
  • @RequestMapping, que caça l’URL definit i executa el mètode que el segueix.
  • @Autowired, que injecta l’objecte que segueix.
  • @PathVariable i @MatrixVariable per accedir a variables dins de l’URL.
  • @RequestParam per enllaçar amb els paràmetres del cos de la petició (Request params).

A més, Estoc de medicaments conté els fitxers XML amb la configuració necessària per dirigir el comportament de Spring MVC respecte a l’aplicació.

Anem a afegir funcionalitat a l’aplicació Estoc de medicaments que necessitareu en la majoria d’aplicacions web.

Afegireu a Estoc de medicaments un formulari per crear medicaments, la qual cosa us permetrà veure totes les possibilitats que ofereix Spring MVC per tractar els formularis, sobretot com passar dades des de la vista fins al model, perquè fins ara el sentit havia estat al contrari.

Construireu una pàgina de login (identificació) i el codi necessari a la resta de capes. Al voltant d’aquest objectiu, veureu etiquetes Spring relacionades amb la seguretat i diverses possibilitats de configuració.

Fareu servir la internacionalització de Spring per mostrar com fer l’aplicació multiidioma (parcialment), i veureu què són i com es comporten els interceptors de Spring.

Fareu servir diverses etiquetes pròpies de Spring amb les directives:

<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags" %>

Aquestes llibreries d’etiquetes proporcionen molta funcionalitat per als formularis, com ara la validació d’errors, però també per a altres finalitats.

Estoc de medicaments, formulari per afegir medicaments

A Estoc de medicaments, a tota la funcionalitat que hi vegeu implementada, el flux de dades és des del model fins a la vista.

Per veure com es genera i manega a Spring MVC un flux de dades des de la vista fins al model afegireu un formulari l’acció del qual sigui crear un medicament.

Un form-backing bean de Spring MVC és l’objecte que permet associar les dades d’un formulari web amb l’aplicació que rep la petició.

L’anotació @ModelAttribute i l’objecte form-backing bean ens permetran crear el medicament a partir de les dades del formulari.

Els paràgrafs següents descriuen el procés que heu de seguir per implementar el formulari.

Per presentar el formulari implementareu un primer mètode que passarà un objecte Medicament buit (newMedicament) com atribut a la nova vista addMedicament.

A la vista, l’etiqueta de formulari serà de la llibreria de Spring i associarà bidireccionalment newMedicament al formulari, és a dir, cada camp del formulari estarà també associat a les propietats de newMedicament i l’actualització d’uns farà que s’actualitzin els altres. Per això es diu que els objectes com newMedicament són els form-backing bean, ja que és on Spring MVC guarda els valors del formulari en el context.

Finalment, implementareu un segon mètode que reculli el submit del formulari. L’anotació:

@ModelAttribute("newMedicament") Medicament newMedicamentToAdd 

us permetrà fer servir els valors del formulari via newMedicamentToAdd.

El codi del projecte “stmedioc” en l’estat formulari per afegir medicament es pot descarregar des de l’enllaç que trobareu als annexos de la unitat.

Però per seguir el desenvolupament és millor partir del que ja heu fet servir i copiar-lo amb el nom de “stmedioc401”.

Treballant les capes de domini, persistència i servei

A la classe Medicament, afegiu un constructor sense paràmetres.

    public Medicament() {
    }

És necessari, perquè necessitem crear un objecte medicament buit.

A la interfície MedicamentRepository, afegiu la següent declaració de mètode:

    void addMedicament(Medicament medicament);

I a la classe InMemoryMedicamentRepository, implementeu el mètode amb el codi que es mostra.

    public void addMedicament(Medicament medicament) {
        listOfMedicaments.add(medicament);
    }

Veieu que simplement s’afegeix un objecte Medicament a la vostra llista de medicaments en memòria. Això és equivalent a un INSERT en el cas que la nostra persistència hagués estat implementada amb una base de dades (relacional amb SQL).

A la interfície MedicamentService, afegiu la següent declaració de mètode:

        void addMedicament(Medicament medicament);

I a la classe MedicamentServiceImpl, implementeu el mètode amb el codi que es mostra.

    public void addMedicament(Medicament medicament) {
        medicamentRepository.addMedicament(medicament);
    }

Simplement es crida el repositori sense més regles de negoci a afegir.

Formulari a la capa de presentació

Al controlador heu d’implementar els mètodes inclosos en el procés que havíeu de seguir. És a dir, un mètode per mostrar el formulari i un mètode per processar-lo.

A la classe MedicamentController, heu d’afegir els mètodes amb el codi que es mostra.

    @RequestMapping(value = "/add", method = RequestMethod.GET)
    public ModelAndView getAddNewMedicamentForm(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        ModelAndView modelview = new ModelAndView("addMedicament");
        Medicament newMedicament = new Medicament();
        modelview.getModelMap().addAttribute("newMedicament", newMedicament);
        return modelview;
    }

    @RequestMapping(value = "/add", method = RequestMethod.POST)
    public String processAddNewMedicamentForm(@ModelAttribute("newMedicament") Medicament newMedicamentToAdd) {
        medicamentService.addMedicament(newMedicamentToAdd);
        return "redirect:/medicaments/all";
    }

El mètode getAddNewMedicamentForm és el que fareu servir per mostrar el formulari. En aquest cas, es dispararà amb una petició GET (method = RequestMethod.GET) creant un objecte buit Medicament que retorna a la vista addMedicament com a atribut de nom newMedicament. L’associació que fareu en aquesta vista el converteix en el form-backing bean.

El mètode processAddNewMedicamentForm és el que fareu servir per processar el formulari. En aquest cas, es dispararà amb una petició POST (method = RequestMethod.POST). L’anotació:

@ModelAttribute("newMedicament") Medicament newMedicament

associa les dades del formulari (guardades a newMedicament) a la variable newMedicamentToAdd que fareu servir al codi del mètode.

Com que no heu de tornar un ModelAndView perquè només voleu redirigir a la vista que mostra la llista de medicaments, feu que processAddNewMedicamentForm sigui de tipus string. No manegueu ModelAndView ni necessiteu HttpServletRequest request i HttpServletResponse response com a paràmetres.

La instrucció:

return "redirect:/medicaments/all"; 

fa que el navegador faci una nova petició amb aquest URL i, per això, el Dispatcher Servlet tornarà a cercar quin controlador específic s’encarrega de l’URL passat com a redirect. En aquest cas és el mateix controlador, i mostrarà la llista de tots els medicaments.

Abans de codificar la vista, a l’arxiu de configuració web.xml heu d’afegir el filtre CharacterEncodingFilter amb les etiquetes que es mostren.

    <filter>  
        <filter-name>encodingFilter</filter-name>  
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>  
        <init-param>  
            <param-name>encoding</param-name>  
            <param-value>UTF-8</param-value>  
        </init-param>  
        <init-param>  
            <param-name>forceEncoding</param-name>  
            <param-value>true</param-value>  
        </init-param>  
    </filter>  
    <filter-mapping>  
        <filter-name>encodingFilter</filter-name>  
        <url-pattern>/*</url-pattern>  
    </filter-mapping>

Aquest filtre permet la utilització d’accents als camps del nostre formulari. En realitat, CharacterEncodingFilter permet l’especificació de la codificació de caràcters que forcem en les peticions, ja que actualment existeixen navegadors que no fan cas de la codificació especificada en la pàgina HTML (a la vista, en el vostre cas).

Afegiu a la carpeta views una nova vista addMedicament.jsp amb el codi que es mostra a continuació:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"  %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
        <link
            rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">
        <title>Medicaments</title>
    </head>
    <body>
        <section>
            <div class="jumbotron">
                <div class="container">
                    <h1>Medicament</h1>
                    <p>Afegir medicament</p>
                </div>
            </div>
        </section>
        <section class="container">
            <form:form modelAttribute="newMedicament" class="form-horizontal">
                <fieldset>
                    <legend>Afegir medicament</legend>
                    <div class="form-group">
                        <label class="control-label col-lg-2 col-lg-2" for="medicamentId">Codi</label>
                        <div class="col-lg-10">
                            <form:input id="medicamentId" path="medicamentId" type="text" class="form:input-large"/>
                        </div>
                    </div>
                    <div class="form-group">
                        <label class="control-label col-lg-2 col-lg-2" for="name">Nom</label>
                        <div class="col-lg-10">
                            <form:input id="name" path="name" type="text" class="form:input-large"/>
                        </div>
                    </div>
                    <div class="form-group">
                        <label class="control-label col-lg-2 col-lg-2" for="price">Preu</label>
                        <div class="col-lg-10">
                            <form:input id="price" path="price" type="text" class="form:input-large"/>
                        </div>
                    </div>
                    <div class="form-group">
                        <label class="control-label col-lg-2 col-lg-2" for="producer">Laboratori</label>
                        <div class="col-lg-10">
                            <form:input id="producer" path="producer" type="text" class="form:input-large"/>
                        </div>
                    </div>   
                    <div class="form-group">
                        <label class="control-label col-lg-2 col-lg-2" for="category">Categoria</label>
                        <div class="col-lg-10">
                            <form:input id="category" path="category" type="text" class="form:input-large"/>
                        </div>
                    </div> 
                    <div class="form-group">
                        <label class="control-label col-lg-2 col-lg-2" for="stockQuantity">Unitats en estoc</label>
                        <div class="col-lg-10">
                            <form:input id="stockQuantity" path="stockQuantity" type="text" class="form:input-large"/>
                        </div>
                    </div>                     
                    <div class="form-group">
                        <label class="control-label col-lg-2 col-lg-2" for="stockInOrder">Unitats en comandes</label>
                        <div class="col-lg-10">
                            <form:input id="stockInOrder" path="stockInOrder" type="text" class="form:input-large"/>
                        </div>
                    </div>                     
                    <div class="form-group">
                        <label class="control-label col-lg-2"
                               for="description">Descripció</label>
                        <div class="col-lg-10">
                            <form:textarea id="description" path="description" rows ="2"/>
                        </div>
                    </div>
                    <div class="form-group">
                        <label class="control-label col-lg-2"
                               for="active">actiu</label>
                        <div class="col-lg-10">
                            <form:radiobutton path="active" value="true" />Si
                            <form:radiobutton path="active" value="false" />No                            
                        </div>
                    </div>
                    <div class="form-group">
                        <div class="col-lg-offset-2 col-lg-10">
                            <input type="submit" id="btnAdd" class="btn btn-primary"
                                   value ="Crear"/>
                        </div>
                    </div>
                </fieldset>
            </form:form>
        </section>
    </body>
</html>

Fixeu-vos que es fan servir les llibreries d’etiquetes de Spring que aporten més funcionalitat.

L’etiqueta que defineix el formulari:

<form:form modelAttribute="newMedicament" class="form-horizontal">

està associant els camps del formulari amb el form-backing bean newMedicament, de manera que quan s’enviï el formulari, el controlador podrà agafar els valors d’aquest objecte.

Les etiquetes dels camps del formulari:

<form:input id="medicamentId" path="medicamentId" type="text" class="form:input-large"/>

associen individualment cada control del formulari amb la propietat del form-backing bean mitjançant l’atribut path.

Si executeu l’aplicació amb l’URL localhost:8080/stmedioc401/medicaments/add us mostrarà la pàgina amb el formulari buit (vegeu la figura).

Figura Sortida de l’aplicació amb el formulari buit

Ompliu el formulari tal com es mostra en la figura.

Figura Sortida de l’aplicació amb el formulari emplenat

En prémer Crear us mostrarà la llista de medicaments amb el nou medicament afegit (vegeu la figura).

Figura Sortida de la llista amb el medicament afegit

Estoc de medicaments, llista blanca al formulari

El formulari per afegir medicaments de l’aplicació Estoc de medicaments conté totes les propietats de Medicament. S’hi inclouen tots els camps, i alguns no es poden omplir perquè és impossible tenir la informació en crear el medicament, com per exemple les unitats en comanda (encara no hem pogut fer cap comanda).

Spring MVC permet registrar una llista de propietats (Whitelist) que poden obviar-se en una petició d’un formulari.

Si una propietat de la Whitelist és a la petició, podrem identificar-la i decidir què fer. Per exemple, llançar una excepció.

Això s’aconsegueix afegint al controlador específic l’anotació @InitBinder i la implementació del mètode que es dispararà. En aquest mètode es posarà la llista de propietats sota la restricció esmentada, és a dir, la Whitelist.

El codi del projecte “stmedioc” en l’estat llista blanca al formulari es pot descarregar des de l’enllaç que trobareu als annexos de la unitat.

Però per seguir el desenvolupament és millor partir del que ja heu fet servir i copiar-lo amb el nom de “stmedioc402”.

Modifiqueu MedicamentController afegint el mètode per identificar la WhiteList (en el vostre cas només és stockInOrder). El codi és el que es mostra a continuació:

    @InitBinder
    public void initialiseBinder(WebDataBinder binder) {
        binder.setDisallowedFields("stockInOrder");
    }

A la mateixa classe, canvieu el mètode processAddNewMedicamentForm pel codi següent.

    public String processAddNewMedicamentForm(@ModelAttribute("newMedicament") Medicament newMedicamentToAdd, BindingResult result) {
        String[] suppressedFields = result.getSuppressedFields();
        if (suppressedFields.length > 0) {
            throw new RuntimeException("Attempting to bind disallowed fields: " + StringUtils.arrayToCommaDelimitedString(suppressedFields));
        }
        medicamentService.addMedicament(newMedicamentToAdd);
        return "redirect:/medicaments/all";
    }

Fixeu-vos que hem afegit el paràmetre BindingResult result. Aquest paràmetre ens serveix per determinar els camps continguts a la petició i són a la Whitelist (result.getSuppressedFields). Recordeu que no voleu que hi siguin, i per això llanceu una excepció.

Si executeu l’aplicació per mostrar el formulari, l’ompliu i l’envieu, haureu d’obtenir l’excepció (vegeu la figura).

Figura Sortida per a l’excepció de la ‘Whitelist’

Traieu aquest camp del formulari de la vista addMedicament.jsp i veureu que treballa normalment sense donar l’excepció.

Estoc de medicaments, pàgina de 'login'

L’aplicació Estoc de medicaments mostra la llista de medicaments i permet crear-ne, però normalment no és convenient que qualsevol usuari connectat pugui afegir-hi medicaments.

Fareu que l’aplicació Estoc de medicaments tingui una pàgina de login que es cridi quan l’usuari intenti anar a la pàgina d’afegir medicaments. Aquesta pàgina de login autenticarà l’usuari mitjançant un codi d’usuari i una contrasenya, i segons qui sigui l’autoritzarà a anar a la pàgina d’afegir medicaments (control d’accés).

Per a aquesta implementació fareu servir funcionalitat Spring Security, afegint l’autenticació bàsica en les nostres pàgines web.

A Estoc de medicaments es fa servir part de la funcionalitat de Spring Security, tot un framework que proporciona autenticació i control d’accés configurable: bit.ly/2nywYZy.

El codi del projecte “stmedioc” en l’estat pàgina de login es pot descarregar des de l’enllaç que trobareu als annexos de la unitat.

Però per seguir el desenvolupament és millor partir del que ja heu fet servir i copiar-lo amb el nom de “stmedioc403”.

Configurant Spring Security

Spring Security és un framework que permet configurar i personalitzar l’autenticació i el control d’accés en aplicacions Spring.

Per fer servir Spring Security a la vostra aplicació heu de configurar les dependències del framework que afegiran les llibreries necessàries.

Afegiu les dependències que es mostren en el fitxer pom.xml.

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>3.1.4.RELEASE</version>
            <exclusions>
                <exclusion>
                    <artifactId>spring-asm</artifactId>
                    <groupId>org.springframework</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>3.1.4.RELEASE</version>
        </dependency>

Recordeu que també les podeu afegir amb l’assistent de NetBeans a tal efecte, la qual cosa us pot ajudar a trobar la versió més recent.

A la carpeta WEB-INF/spring, creeu l’arxiu security-context.xml amb el codi que es mostra.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:security="http://www.springframework.org/schema/security"
       xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
            http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <security:http auto-config="true" use-expressions="true">
        <security:intercept-url pattern="/medicaments/add" access="hasRole('adminRol')"/>        
        <security:form-login login-page="/login" default-target-url="/medicaments/add" login-processing-url="/j_spring_security_check"
                             username-parameter="j_username" password-parameter="j_password" authentication-failure-url="/loginfailed"/>
        <security:logout logout-success-url="/logout" />
    </security:http>
    <security:authentication-manager>
        <security:authentication-provider>
            <security:user-service>
                <security:user name="ioc" password="ioc123" authorities="adminRol"  />
            </security:user-service>
        </security:authentication-provider>
    </security:authentication-manager>
</beans>

L’etiqueta security:http, els seus atributs i el seu contingut defineixen el veritable comportament de la seguretat.

L’etiqueta i els atributs

<security:intercept-url pattern="/medicaments/add" access="hasRole('adminRol')"/> 

indiquen que una petició sobre /medicaments/add serà interceptada i només hi podrà accedir qui hagi estat identificat amb rol adminRol.

L’etiqueta i els atributs

<security:form-login …> 

defineixen les pàgines que controlen l’autenticació efectiva, la fallida i el nom dels camps de login.

L’etiqueta i l’atribut

<security:logout logout-success-url="/logout" /> 

defineixen la pàgina de desconnexió.

L’etiqueta i els atributs

<security:user name="ioc" password="ioc123" authorities="adminRol"  /> 

defineixen els usuaris i les seves credencials. L’atribut authorities determina el rol de l’usuari. En aquest cas només heu configurat un usuari, però se’n poden configurar més repetint l’etiqueta.

Completeu la configuració a nivell de l’aplicació. A web.xml, afegiu la següent configuració dins de <web-app>:

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/security-context.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

Amb el paràmetre de context <context-param> esteu indicant el nom del fitxer on heu configurat la seguretat. Com que el listener és ContextLoaderListener, es carregarà la configuració indicada a security-context.xml a l’inici de l’aplicació i en aquest nivell, sense esperar cap petició i sense dependre del Dispatcher Servlet.

Sense entrar en més detalls de Spring Security, el filtre està indicant que qualsevol petició (<url-pattern>) serà manegada pel filtre springSecurityFilterChain, i fent servir DelegatingFilterProxy esteu deixant a Spring que crei els beans necessaris.

El controlador per al 'login'

A la configuració que heu fet a security-context.xml heu assignat les URL que corresponen al login, al logout i a les credencials errònies. Són, respectivament, /login, /logout i /loginfailed.

Amb aquesta informació, al paquet cat.xtec.ioc.controller podeu crear un nou controlador LoginController amb el codi que es mostra a continuació:

package cat.xtec.ioc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class LoginController {

    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public String login() {
        return "login";
    }

    @RequestMapping(value = "/loginfailed", method = RequestMethod.GET)
    public String loginerror(Model model) {
        model.addAttribute("error", "true");
        return "login";
    }

    @RequestMapping(value = "/logout", method = RequestMethod.GET)
    public String logout(Model model) {
        return "login";
    }
}

Quan algú intenti accedir a l’URL /medicaments/add, Spring Security mostrarà la pàgina login.jsp. Si l’usuari no s’autentica o no està en el rol adient, segons la configuració actuarà /loginfailed, i s’hi afegirà l’atribut error.

Si l’usuari provoca una desconnexió, llavors actuarà /logout i es mostrarà un altre cop la pàgina de login.

La vistes

Com que un cop connectats l’aplicació us dirigirà a la vista addMedicament, podeu afegir aquí la desconnexió.

Afegiu el codi següent a addMedicament.jsp dins de la div de classe jumbotron.

<a href="<c:url value="/j_spring_security_logout" />" class="btn btndanger btn-mini pull-right">desconnectar</a>

A views, creeu la vista login.jsp amb el codi que es mostra a continuació:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"  %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
        <link
            rel="stylesheet"href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">
        <title>Medicaments</title>
    </head>
    <body>
        <section>
            <div class="jumbotron">
                <div class="container">
                    <h1>Medicament</h1>
                    <p>Afegir medicament</p>
                </div>
            </div>
        </section>
        <div class="container">
            <div class="row">
                <div class="col-md-4 col-md-offset-4">
                    <div class="panel panel-default">
                        <div class="panel-heading">
                            <h3 class="panel-title">Si us plau, proporcioni les seves dades</h3>
                        </div>
                        <div class="panel-body">
                            <c:if test="${not empty error}">
                                <div class="alert alert-danger">
                                    Credencials incorrectes
                                </div>
                            </c:if>
                            <form action="<c:url value= "/j_spring_security_check"> </c:url>" method="post">
                                <fieldset>
                                    <div class="form-group">
                                        <input class="form-control" placeholder="Usuari" name='j_username' type="text">
                                    </div>
                                    <div class="form-group">
                                        <input class="form-control" placeholder="Contrasenya" name='j_password' type="password">
                                    </div>
                                    <input class="btn btn-lg btn-success btn-block" type="submit" value="Connectar">
                                </fieldset>
                            </form>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </body>

L’estructura de selecció <c:if …> mostrarà el missatge en el cas que l’atribut error no sigui buit. Recordeu que el controlador l’omplirà en cas que l’usuari no s’hagi autenticat.

L’acció del formulari es correspon a la configuració feta a security-context.xml, concretament l’atribut login-processing-url=”/j_spring_security_check”, i els noms dels camps “usuari” i “contrasenya” també es corresponen amb la configuració.

Provant d'entrar

Executeu l’aplicació amb l’URL localhost:8080/stmedioc403/medicaments/add.

Veureu la pàgina de login tal com es mostra en la figura.

Figura Formulari de ‘login’ (autenticació)

Proveu amb dades no correctes i l’aplicació us mostrarà el missatge de credencials no vàlides (vegeu la figura).

Figura Formulari de ‘login’ (autenticació), credencials incorrectes

Proveu, doncs, amb credencials correctes, i llavors us mostrarà la pàgina per afegir el medicament. Fixeu-vos que hi ha l’enllaç a desconnexió (vegeu la figura).

Figura Formulari de medicament amb enllaç a desconnexió

Amb aquest exemple heu assegurat una pàgina de la vostra aplicació, i es pot interpretar la generalització a qualsevol pàgina de l’aplicació.

Heu fet servir Spring Security baixant les dependències i l’heu configurat amb l’arxiu security-context.xml, on heu definit el comportament de Spring Security quant a:

  • Quines pàgines s’ha d’assegurar (interceptar) i quins rols estaran autoritzats a accedir-hi.
  • Quines són les pàgines i URL de login, logout i error.
  • Quin és el formulari que feu servir per a l’autenticació.
  • Quins són els camps d’usuari i contrasenya que conté el formulari.
  • Quins usuaris hi ha a l’aplicació.

La configuració d’aquest arxiu i el bean que manega la seguretat es carreguen a l’inici de l’aplicació, i serà vàlida per a tot el context de l’aplicació segons el que s’ha indicat a web.xml.

Heu implementat un controlador que manega les peticions segons les URL de l’arxiu de configuració.

Finalment, heu implementat la vista amb el formulari d’autenticació (login).

Estoc de medicaments, internacionalització

L’aplicació Estoc de medicaments està practicament completada segons els objectius pedagògics que s’havien proposat. Però avui dia les aplicacions han de ser multiidioma, i per això cal dotar la nostra aplicació de la possibilitat de mostrar-se en diversos idiomes.

No tan sols això, sinó que es parlarà d’una internacionalització que permet definir més paràmetres, com quina moneda es fa servir i quin és el separador decimal, entre altres personalitzacions.

La internacionalització s’abrevia sovint com “i18n” (de l’anglès I-eighteen letters-N), que prové de la “i” del començament, les 18 lletres següents i la “n” del final.

Fareu servir la funcionalitat que es coneix com a externalització de missatges a Spring MVC per mostrar el text de les etiquetes en diversos idiomes, segons arxius de propietats.

L’externalització és el pas previ a la internacionalització, ja que cal tenir el text de les etiquetes dels diferents idiomes en diferents arxius de configuració per tal que la funcionalitat d’i18N vagi a buscar-los.

Un cop externalitzats els missatges, ja es pot configurar i implementar tot el que és relatiu a i18n. Per detectar i canviar d’idioma fareu servir SessionLocaleResolver a nivell de web application context i l’interceptor LocaleChangeInterceptor també en aquest nivell.

El codi del projecte “stmedioc” en l’estat internacionalització es pot descarregar des de l’enllaç que trobareu als annexos de la unitat.

Però per seguir el desenvolupament és millor partir del que ja heu fet servir i copiar-lo amb el nom de “stmedioc404”.

Externalitzem els missatges

Per externalitzar els missatges de la vostra aplicació i després poder traduir-los i fer-los servir en diversos idiomes fareu canvis en diversos textos d’algunes etiquetes del formulari de la vista addMedicament.jsp.

Per altra banda, creareu diversos fitxers de propietats, un per defecte i altres per a altres idiomes (concretament, un addicional per a l’anglès).

A més, haureu de configurar un bean de Spring per connectar la vista amb els fitxers de propietats que contenen els textos.

A addMedicament.jsp, modifiqueu les etiquetes de code, name, price, producer i category substituint el text, respectivament, per les etiquetes Spring que s’hi mostren.

<spring:message code= "addMedicament.form.medicamentId.label"/>
<spring:message code= "addMedicament.form.name.label"/>
<spring:message code= "addMedicament.form.price.label"/>
<spring:message code= "addMedicament.form.producer.label"/>
<spring:message code= "addMedicament.form.category.label"/>

Amb l’etiqueta spring:message, mitjançant l’atribut code, esteu associant el text amb la propietat definida al fitxer de propietats que creareu per a cada idioma de l’aplicació.

messages.properties

A la carpeta /src/main/resources (el que veieu dins d’Other Sources en la pestanya de projectes de NetBeans), creeu un arxiu de propietats de nom messages.properties. Serà l’arxiu per defecte, és a dir, que quan no trobi cap idioma serà el que farà servir.

Afegiu-hi les propietats de cada etiqueta.

addMedicament.form.medicamentId.label = Codi de medicament
addMedicament.form.name.label = Nom
addMedicament.form.price.label = Preu
addMedicament.form.producer.label = Laboratori - Fabricant
addMedicament.form.category.label = Categoria

Finalment, heu de configurar el bean que connecta el fitxer de propietats amb les etiquetes spring:message que heu afegit al jsp. Es configura a nivell de web application context, és a dir, afegiu un bean a l’arxiu DispatcherServlet-servlet.xml.

<bean id= "messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages"/>
</bean>

L’atribut value=“messages” és el que determina que el fitxer de propietats és messages.

Executeu l’aplicació amb l’URL localhost:8080/stmedioc404/medicaments/add per mostrar el formulari. Això sí, us haureu d’identificar i, un cop fet, us mostrarà el formulari amb els valors de l’arxiu de propietats (vegeu la figura).

Figura Formulari amb textos externalitzats

Configurant i implementant el canvi d'idioma

A resources, creeu l’arxiu messages_en.properties per poder tenir dos idiomes. Afegiu-hi les propietats de messages.properties, però canvieu els valors.

addMedicament.form.medicamentId.label = Medicament ID
addMedicament.form.name.label = Name
addMedicament.form.price.label = Price
addMedicament.form.producer.label = Producer
addMedicament.form.category.label = Category

A la vista addMedicament.jsp, afegiu el selector per canviar d’idioma just després de l’enllaç de desconnexió.

<div class="pull-right" style="padding-right:50px">
<a href="?language=ca" >Català</a>|<a href="?language=en" >Anglès</a>
</div>

A l’arxiu de configuració de web application context DispatcherServlet-servlet.xml, afegiu la definició del bean.

<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
<property name="defaultLocale" value="ca"/>
</bean>

SessionLocaleResolver és l’objecte que assigna atributs locals sobre la sessió de l’usuari. Una de les propietats que conté l’objecte és defaultLocale. En el vostre cas, dieu que el llenguatge per defecte és el català (ca).

També a DispatcherServlet-servlet.xml, heu d’afegir l’interceptor LocaleChangeInterceptor tal com es mostra a continuació:

    <mvc:interceptors>
        <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
            <property name="paramName" value="language"/>
        </bean>
    </mvc:interceptors>

Amb aquest interceptor esteu dient que el nom del paràmetre que correspon al canvi d’idioma és language, i això es correspon amb el que heu afegit a addMedicament.jsp.

Provant a canviar d'idioma

Executeu l’aplicació amb l’URL localhost:8080/stmedioc404/medicaments/add i, un cop autenticats satisfactòriament, us mostrarà el formulari (vegeu la figura).

Figura Formulari amb els textos en l’idioma per defecte

Fixeu-vos que les etiquetes són en català. Si premeu l’enllaç Anglès, llavors les etiquetes canviaran (vegeu la figura).

Figura Formulari amb els textos en anglès

Si torneu a prémer Català tornarà a mostrar les etiquetes en aquest idioma. També podeu veure que les URL contenen el paràmetre language.

Què s'ha après?

Per veure com es genera i manega a Spring MVC un flux de dades des de la vista fins al model heu afegit el formulari per crear medicaments.

Al controlador, al mètode que desencadena mostrar el formulari, es crea un atribut que passarà a la resposta amb un domain object (Medicament) buit. A la vista, a l’etiqueta form:form, mitjançant l’atribut d’etiqueta modelAttribute, es relacionen els camps del formulari amb les propietats de l’objecte. Canvis en els valors del formulari provoquen canvis en l’objecte, i per això s’anomena form-backing bean.

El mètode que recull el POST del formulari amb l’anotació:

@ModelAttribute("newMedicament") Medicament newMedicamentToAdd

està recollint l’objecte form-backing bean i el podrà fer servir amb la variable declarada (newMedicamentToAdd).

Heu afegit una llista de propietats (Whitelist) per obviar un dels camps del model (stockInOrder). Ho heu aconseguit amb l’anotació @InitBinder i la implementació del mètode que la segueix al controlador específic.

Heu fet servir Spring Security, un framework que permet configurar i personalitzar l’autenticació i el control d’accés en aplicacions Spring. Per usar el framework heu desenvolupat la pàgina de login, afegint l’arxiu de configuració de la seguretat security-context.xml, on heu definit els usuaris i el comportament de la vostra aplicació respecte a la seguretat (per exemple, quines pàgines necessitaran autenticació i quines pàgines han de mostrar-se per a login, logout i error).

Finalment, heu fet que la vostra aplicació tingui internacionalització (i18n). Heu externalitzat missatges ajudats pels fitxers de propietats i etiquetes específiques als jsp (spring:message) i heu configurat la internacionalització amb els beans necessaris a nivell de web application context: SessionLocaleResolver i LocaleChangeInterceptor.

Anar a la pàgina anterior:
Annexos
Anar a la pàgina següent:
Activitats