Formularis amb 'servlets' i EJB

En aquest apartat s’explicarà com enviar informació des d’un formulari web a un servlet. Aprendrem a obtenir aquesta informació, tractar-la i enviar una pàgina de resposta a l’usuari.

Ens endinsarem en l’aprenentatge de l’arquitectura Enterprise JavaBeans (EJB). Veurem com funciona, quins elements la componen i com interacciona amb l’arquitectura Java Web. Així, aprendrem a enviar i rebre informació entre els formularis, els servlets i els components EJB.

També aprendrem una tècnica per comprovar el format de les dades que ens envia l’usuari amb un formulari. Aquesta tècnica utilitza patrons i expressions regulars que es poden afegir, per automatitzar la comprovació de les dades, als components EJB.

Tots aquest conceptes s’explicaran partint de l’exemple. Començarem amb un exemple senzill per calcular el sou d’una família, i les dades s’enviaran a un servlet. Continuarem amb l’elaboració d’un blog on introduïren l’arquitectura EJB. Acabarem l’apartat demanant les dades d’un usuari per inscriure’l en l’aplicació, on veurem com tractar les dades i automatitzar el procés per comprovar les restriccions requerides pel programa.

Podeu descarregar-vos el codi Java, que utilitzarem en aquest apartat, des de l’enllaç que trobareu a l’apartat d’annexos de la unitat. Recordeu que podeu utilitzar la funció d’importar del Netbeans per accedir a aquest contingut.

Per poder fer aquest apartat es recomana haver fet l’apartat ”Servlets”, on se n’explica el funcionament i el seu cicle de vida.

Calculant el sou net

En aquest apartat aprendreu a utilitzar els servlets com a receptors de les peticions GET o POST que s’envien des dels formularis HTML. Utilitzareu els formularis per enviar informació des del navegador fins al servlet.

GET versus POST

El mètode GET és un mètode del protocol HTTP que envia la informació al servidor utilitzant la URL. En canvi, el mètode POST envia la informació utilitzant la capçalera de la petició i, per tant, sense embrutar la URL en el navegador.

Comenceu creant un nou projecte amb Maven (File / New Project / Maven / Web Application) i l’anomeneu formServlets. Posteriorment creareu un servlet anomenat CalculSouServlet. Recordeu que per crear un servlet amb NetBeans heu d’accedir a File / New File / Web / Servlet. No cal que afegiu la configuració del servlet al fitxer web.xml. En aquest cas, utilitzareu les anotacions per configurar-lo.

Aquest servlet calcularà el sou net que rebrà una família a final de mes tenint en compte el nombre de fills i el sou brut que es cobra. Suposeu que la retenció normal és del 21%, i que per cada fill que es tingui es rebaixarà 5 punts aquest percentatge.

Segons l’enunciat, necessiteu que cada família que vulgui calcular el sou net enviï al servlet les següents dades:

  • sou brut
  • nombre de fills

Necessiteu un formulari HTML amb els elements necessaris per demanar aquesta informació. Creareu una nova pàgina web (File / New File / Html5 / Html File ) amb el nom calcularSou.html. El contingut de la pàgina és el següent:

  1. <!DOCTYPE html>
  2. <head>
  3. <title>form-servlet</title>
  4. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  5. </head>
  6. <body>
  7. <h1>Càlcul del salari:</h1>
  8. <form method="GET" action="CalculSouServlet">
  9. <label for="salari">Salari brut:</label>
  10. <input id="salari" type="number" name="salariBrut" min="0"/>
  11. <label for="fill">Nombre de fills:</label>
  12. <input id="fill" type="number" name="fills" min="0">
  13. <input type="submit">
  14. </form>
  15. </body>
  16. </html>

Per elaborar la pàgina calcularSou.html s’ha creat un formulari web on s’han creat dos camps (inputs) que permeten a l’usuari introduir informació. El primer camp correspon al salari brut que cobra la família:

  1. <input id="salari" type="number" name="salariBrut" min="0"/>

Aquest camp del formulari és de tipus numèric, i com a mínim el valor que introdueixi l’usuari ha de ser 0, així es fa un petit control d’errors abans d’enviar qualsevol dada al servlet. Quan l’usuari introdueixi el seu sou, aquest s’enviarà amb l’identificador que surt al costat de l’atribut name.

El segon camp correspon al nombre de fills que té la família:

  1. <input id="fill" type="number" name="fills" min="0">

Igual que s’ha fet abans, també es fa un petit control d’errors. El tipus de dada que s’ha de posar és numèrica i més gran o igual que 0. Quan l’usuari introdueixi un valor, aquest s’enviarà al servlet amb el nom fills.

Una vegada s’han definit els camps que s’han d’enviar al servidor cal configurar el mètode d’enviament de la informació, així com el recurs que la rebrà. Aquesta informació es configura en la mateixa etiqueta form. El mètode pot ser POST o GET, i el recurs que rebrà la informació ha de ser el servlet CalculSouServlet.

  1. <form method="GET" action="CalculSouServlet">

Reviseu que teniu una anotació al servlet (fitxer CalculaSouServlet.java) que indica que escolta les peticions enviades a l’URL CalculSouServlet. L’anotació ha de ser semblant a aquesta:

  1. @WebServlet(name = "CalculSouServlet", urlPatterns = {"/CalculSouServlet"})

El nom del recurs configurat a la propietat urlPatterns del servlet ha de coincidir amb el nom de la propietat action configurada al formulari.

Una vegada arribats a aquest punt, ja teniu la part del client acabada, i continuareu amb la part del servidor. Ara calculareu el sou net que percebrà una família segons les dades que hagi enviat amb el formulari.

Modificareu la funció processRequest del servlet. Aquesta funció s’executa sempre que es rebi una petició POST o GET indistintament. El contingut de la funció és el següent:

  1. protected void processRequest(HttpServletRequest request, HttpServletResponse response)
  2. throws ServletException, IOException {
  3. response.setContentType("text/html;charset=UTF-8");
  4. try (PrintWriter out = response.getWriter()) {
  5. out.println("<!DOCTYPE html>");
  6. out.println("<html>");
  7. out.println("<head>");
  8. out.println("<title>Servlet formServlet</title>");
  9. out.println("</head>");
  10. out.println("<body>");
  11. out.println("<h1>Dades rebudes del formulari</h1>");
  12.  
  13. int brut = Integer.parseInt(request.getParameter("salariBrut"));
  14. out.println("<p>Salari brut: " + brut + "</p>");
  15.  
  16. int fills = Integer.parseInt(request.getParameter("fills"));
  17. out.println("<p>Nombre de fills: " + fills + "</p>");
  18.  
  19. out.println("<h1>Càlcul del salari net:</h1>");
  20. int retencio = 21 - (5*fills);
  21.  
  22. // Fórmula per calcular la retenció: k = (int)(value*(percentage/100.0f));
  23. int net = brut -((int)(brut * (retencio/100.0f)));
  24.  
  25. out.println("<p>Tens una retenció del " + retencio + " per cent</p>");
  26. out.println("<p>El teu salari net és de " + net + " euros</p>");
  27. out.println("<a href='calcularSou.html'> Tornar </a>");
  28. out.println("</body>");
  29. out.println("</html>");
  30. }
  31. }

Bàsicament, la funció processRequest obté els paràmetres que ha enviat l’usuari i fa el càlcul de la retenció per poder mostrar el salari net. Vegeu com s’obté el paràmetre salariBrut:

  1. int brut = Integer.parseInt(request.getParameter("salariBrut"));

Tota la informació que s’envia des del navegador cap al servlet s’emmagatzema a l’objecte request (petició). Aquest objecte té un mètode anomenat .getParameter(name) per obtenir les dades que ha enviat l’usuari. El nom que s’ha d’utilitzar per obtenir les dades és el nom que s’ha configurat a la propietat name de l’input corresponent. Per exemple, si voleu obtenir el salari brut hem de veure com s’ha creat el formulari:

  1. <input id="salari" type="number" name="salariBrut" min="0"/>

El nom configurat és salariBrut. Aquest nom és el que s’ha d’utilitzar al mètode .getParameter de l’objecte request per obtenir la dada introduïda:

  1. request.getParameter("salariBrut");

Finalment, com que voleu poder operar amb aquest número s’ha de convertir en un integer, ja que el tipus de dada que retorna el mètode getParametre és de tipus string.

Feu la mateixa operació amb el paràmetre que representa el nombre de fills.

  1. int fills = Integer.parseInt(request.getParameter("fills"));

Observeu que ambdós paràmetres s’han emmagatzemat en dues variables, anomenades brut i fills, de tipus integer, per poder operar amb elles i calcular el sou net.

El sou net es calcula multiplicant el salari brut per la retenció apropiada: (net = brut*(percentage/100)). Primer s’ha de calcular quina retenció s’ha d’aplicar.

  1. int retencio = 21 - (5*fills);

La retenció màxima s’estableix, segons l’enunciat, en el 21%. Per cada fill que tingui la família s’han de treure 5 punts de retenció. Per tant, amb aquesta fórmula: retenció = retencioMaxima - (numFills*5) obteniu la retenció real que s’ha d’aplicar.

Una vegada s’ha calculat la retenció real es pot aplicar a la fórmula per calcular el sou net:

  1. int net = brut -((int)(brut * (retencio/100.0f)));

Tingueu en compte que a Java, per fer el càlcul dels percentatges, les dades han d’estar en float. Si convertiu el valor 100 a float, la seva divisió amb un integer també dóna un float i obtindreu correctament aquest percentatge.

Finalment, es mostren les dades per pantalla amb el càlcul del sou net i es permet tornar a la pàgina principal per si es vol fer un altre càlcul.

Escrivint un 'post'

En aquest apartat s’explicarà la validació de dades d’un formulari utilitzant servlets i EJB. Aprendrem a integrar aquests elements perquè funcionin plegats validant dades d’un formulari. Es veuran diverses estratègies de funcionament de l’arquitectura EJB per fer-ne la validació.

Es vol crear una aplicació que permeti escriure un missatge per pantalla que no ha de superar els 150 caràcters. Per permetre escriure un missatge, l’aplicació demana un correu electrònic vàlid i la data de naixement de la persona. Si la persona té menys de 18 anys no se li permet escriure el missatge.

Per fer aquest apartat aprofitareu el mateix projecte que a l’apartat anterior. Creeu una nova pàgina HTML, amb el programa NetBeans, anomenada escriurePost.html (File / New File / HTML5 / HTML File), on afegireu les dades a un formulari web HTML. El codi de la pàgina pot ser semblant a aquest:

  1. <!DOCTYPE html>
  2. <head>
  3. <title>EJB-SERVLET</title>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. </head>
  7. <body>
  8. <h1>Escriu un missatge:</h1>
  9. <form id="formulari" method="POST" action="PostServlet">
  10. <label for="mail">Correu electrònic:</label>
  11. <input id="mail" type="text" name="mail"/>
  12. <label for="age">Edat:</label>
  13. <input id="age" type="number" name="edat" min="0">
  14. <input type="submit">
  15. </form>
  16. <label>Missatge:</label>
  17. <textarea name="missatgePost" form="formulari"></textarea>
  18. </body>
  19. </html>

La pàgina HTML consta d’un formulari on es demana a l’usuari tres dades:

  • adreça de correu electrònic (name=mail)
  • edat de la persona (name=edat)
  • missatge o post a escriure per pantalla (name=missatgePost)

Per poder escriure el missatge per pantalla, les dades s’han de validar. Les validacions són les següents:

  • La longitud del missatge (missatgePost) ha d’estar entre 0 i 150 caràcters.
  • L’edat de la persona ha de ser major o igual a 18 anys.
  • El correu electrònic ha de ser vàlid.

Per implementar la validació de les dades utilitzareu l’arquitectura EJB, que permet la reutilització dels seus components en aquesta o en altres aplicacions.

Un Enterprise JavaBean (EJB) és una arquitectura de components de servidor que simplifica el procés de construcció d’aplicacions de components empresarials distribuïts en Java.

S’utilitza l’arquitectura EJB sempre que vulgueu:

  • Que l’aplicació sigui escalable. Potser a mesura que el nombre d’usuaris vagi creixent es necessitarà distribuir l’aplicació en diferents màquines.
  • Integritat de dades mitjançant transaccions. Els components EJB poden crear transaccions i tenen mecanismes per accedir concurrentment a objectes compartits.
  • Que l’aplicació tingui una gran varietat de clients. Amb poques línies de codi, clients remots poden localitzar fàcilment Enterprise Beans.

Els frameworks faciliten al programador la seva tasca donat-li una metodologia estàndard, una organització del projecte i uns recursos propis que moltes vegades fan molt fàcil la tasca de programar grans aplicacions. Alguns frameworks que existeixen per a Java son els següents: JSP, JSF, Spring, Struts, Tapestry, Googgle Web Toolkit (GWT), entre d’altres.

Els components EJB no es poden utilitzar directament per interaccionar amb el protocol HTTP. Necessiten un framework, com pot ser Spring, JSF o aplicacions basades en servlets, per tractar amb el protocol HTTP i després enviar la informació necessària als components EJB.

Així, necessiteu crear un servlet que rebi les dades enviades amb el formulari i que després les enviï a un component EJB per a la seva validació.

Creeu un servlet nou anomenat PostServlet (File / New File / Web / Servlet ). No cal que afegiu la configuració del servlet al fitxer web.xml. En aquest cas, utilitzareu les anotacions per configurar-lo.

El servlet ha de portar a terme tres tasques:

  • Obtenir les dades que ha enviat l’usuari
  • Enviar les dades a un component EJB per validar-les
  • Mostrar per pantalla el resultat de la validació

El codi corresponent a aquest funcionament és el següent:

  1. @WebServlet(name = "PostServlet", urlPatterns = {"/PostServlet"})
  2. public class PostServlet extends HttpServlet {
  3.  
  4. @EJB
  5. private PostBeanLocal validation;
  6.  
  7. protected void processRequest(HttpServletRequest request, HttpServletResponse response)
  8. throws ServletException, IOException {
  9. response.setContentType("text/html;charset=UTF-8");
  10. try (PrintWriter out = response.getWriter()) {
  11. out.println("<!DOCTYPE html>");
  12. out.println("<html>");
  13. out.println("<head>");
  14. out.println("<title>Servlet ServletValidation</title>");
  15. out.println("</head>");
  16. out.println("<body>");
  17. out.println("<h1>EJB validation</h1>");
  18.  
  19. String mail = request.getParameter("mail");
  20. if (validation.isValidEmail(mail)) {
  21. out.println("<p>El correu electrònic " + mail + " és vàlid</p>");
  22. } else {
  23. out.println("<p>El correu electrònic no és vàlid.</p>");
  24. }
  25.  
  26. String edat = request.getParameter("edat");
  27. if (validation.isValidAge(edat)) {
  28. out.println("<p>Tens" + edat + " anys i pots escriure un missatge a l'aplicació.</p>");
  29. } else {
  30. out.println("<p>Has de ser major d'edat per escriure un missatge a l'aplicació.</p>");
  31. }
  32.  
  33. String missatge = request.getParameter("missatgePost");
  34. if (validation.isValidPost(missatge)) {
  35. out.println("<p>El missatge '" + missatge + "' és vàlid.</p>");
  36. } else {
  37. out.println("<p>El missatge no és vàlid. Ha de tenir menys de 150 caràcters.</p>");
  38. }
  39.  
  40. out.println("</body>");
  41. out.println("</html>");
  42. }
  43. }

Com podeu observar, en el servlet PostServlet es repeteix el funcionament esperat per a cadascuna de les dades enviades des del formulari.

Primer es recupera una de les dades enviades, per exemple l’edat:

  1. String edat = request.getParameter("edat");

A continuació, es valida l’edat:

  1. if (validation.isValidAge(edat)) {

Segons el resultat de la validació, s’envia un missatge o un altre a l’usuari:

  1. if (validation.isValidAge(edat)) {
  2. out.println("<p>Tens" + edat + " anys i pots escriure un missatge a l'aplicació.</p>");
  3. } else {
  4. out.println("<p>Has de ser major d'edat per escriure un missatge a l'aplicació.</p>");
  5. }

El codi que s’ha d’introduir en el servlet s’ha reduït molt. Ara el servlet només conté la part visual de la pàgina de resposta. Tota la lògica de l’aplicació ha estat externalitzada a un component EJB.

Un EJB (Enterprise JavaBean) és un component que ha d’executar-se en un contenidor d’EJB, i es diferencia molt d’un JavaBean normal. Un JavaBean és un objecte al qual accediu de manera directa des de la vostra aplicació (vegeu la figura).

Figura Accés a un JavaBean

En canvi, un EJB és un component al qual no podeu accedir d’una forma tan directa i sempre hi accediu a través d’algun tipus d’intermediari (vegeu la figura).

Figura Accés a un EJB

Aquest intermediari aportarà una sèrie de serveis definits pels estàndards en els quals l’EJB es pot recolzar. Existeixen dos tipus d’intermediaris, l’intermediari local i l’intermediari remot.

L’intermediari local és aquell que s’executa en la mateixa màquina virtual de Java que l’EJB. En canvi, el remot és aquell intermediari que s’executa en una màquina virtual de Java diferent de la màquina on s’executa l’EJB. Gràcies a aquests intermediaris es poden construir aplicacions distribuïdes (vegeu la figura).

Figura Arquitectura EJB distribuïda

Cadascuna d’aquestes interfícies intermediàries s’encarrega de definir els mètodes que estaran a la disposició dels clients que els invoquen. Aquestes interfícies són les encarregades de donar accés als EJB a tots els serveis addicionals que suporta el contenidor EJB (vegeu la figura), com són el maneig de transaccions, la concurrència, la seguretat, la gestió de recursos, els serveis de xarxa, etc.

Figura Contenidor d’EJB

Un contenidor EJB (EJB Container) és l’encarregat d’administrar l’execució dels components EJB. Controla totes les funcionalitats d’un EJB dintre del servidor actuant com a intermediari entre el servidor i l’aplicació.

Un contenidor d’EJB pot administrar dos tipus de components EJB: els beans de sessió (Session Beans) o els beans dirigits per missatges (message-driven beans).

Un contenidor web també és conegut com a Contenidor Servlet perquè la seva funció principal és administrar el cicle de vida dels servlets. Amb les aplicacions EJB van sorgir els seus contenidors propis per administrar els EJB.

Els beans de tipus message-driven processen missatges asíncrons. Els missatges es reben i s’envien mitjançant el servei de missatges de Java (JMS: Java Message Service).

Els beans de sessió encapsulen la lògica de l’aplicació i poden ser invocats per un client local, remot o un client d’un servei web (web service). El client ha d’accedir directament al mètode que vol executar del bean. El contenidor EJB s’encarrega de crear el bean corresponent i facilitar la interacció amb l’aplicació. Els beans de sessió no són persistents, és a dir, no s’emmagatzemen a la base de dades.

Si observeu el codi on executem la validació de l’edat no creem el bean validation (igual que no creem el servlet). El contenidor EJB crea per a nosaltres un bean que executarà la funcionalitat demanada.

  1. if (validation.isValidAge(edat)) {
  2. out.println("<p>Tens" + edat + " anys i pots escriure un missatge a l'aplicació.</p>");
  3. } else {
  4. out.println("<p>Has de ser major d'edat per escriure un missatge a l'aplicació.</p>");
  5. }

Existeixen tres tipus de beans de sessió:

  • Beans sense estat (stateless): no es modifiquen amb les crides dels clients. Els mètodes es posen a disposició de les aplicacions clients que els executen enviant dades i rebent resultats, però que no modifiquen internament el bean. Això permet al contenidor tenir un conjunt d’instàncies (pool) del mateix bean i anar assignant qualsevol instància del bean a qualsevol client. Fins i tot pot tenir la mateixa instància assignada a diferents clients, ja que l’assignació només dura el temps d’execució del mètode.
  • Beans amb estat (stateful): aquests beans poden tenir propietats. Cada bean emmagatzema les dades d’un client durant la seva interacció i no es pot compartir entre diferents clients. Aquestes propietats es poden modificar quan el client va fent les crides als mètodes del bean. Aquestes dades no es guarden quan el client finalitza la sessió. En acabar la comunicació, el bean s’esborra.
  • Bean singleton: és un bean que s’instancia una vegada i existeix durant tota vida de l’aplicació.

En aquesta ocasió s’utilitza, a la vostra aplicació, un bean EJB sense estat. Per crear-lo aneu a New File / Enterprise JavaBeans / Session Bean (vegeu la figura). Anomeneu-lo PostBean i activeu la creació de la interfície local.

Figura Nou ‘bean’ de sessió

Per fer la validació del correu electrònic, de l’edat i del missatge enviat es necessiten tres funcions, una per cada valor que es vol validar. Llavors creem aquestes funcions a la interfície local creada. El seu nom és el nom del bean de sessió creat acabat en “Local” (PostBeanLocal). Si heu activat l’opció de creació de la interfície quan heu creat el bean de sessió, aquesta ja estarà creada. El codi de la interfície és el següent:

  1. package cat.ioc.m7.formservlets;
  2.  
  3. import javax.ejb.Local;
  4.  
  5. @Local
  6. public interface PostBeanLocal {
  7.  
  8. public Boolean isValidEmail(String email);
  9.  
  10. public Boolean isValidAge(String age);
  11.  
  12. public Boolean isValidPost(String message);
  13.  
  14. }

Existeixen moltes maneres de fer la validació de les dades que introdueix l’usuari. Per exemple, HTML5 i Javascript, que són llenguatges del costat client, també proporcionen patrons per poder validar-les.

Fixeu-vos que l’anotació @Local s’utilitza per informar el contenidor d’EJB que correspon a una interfície local d’un EJB, és a dir, que una aplicació ubicada a la mateixa màquina virtual de Java, per exemple un servlet, que vulgui obtenir un PostBean ha d’utilitzar aquesta interfície per arribar als seus mètodes.

Aquestes mètodes es podran cridar de manera independent i el bean que les executi no emmagatzemarà cap informació. Així, pot haver-hi un pool de beans i en cada petició s’agafarà un bean qualsevol.

La classe PostBean és el bean de sessió que implementarà la interfície PostBeanLocal i, en conseqüència, les tres funcions anteriors. Modificareu aquest objecte amb la implementació de les tres funcions. Vegeu-ne una possible solució:

  1. @Stateless
  2. public class PostBean implements PostBeanLocal {
  3.  
  4. @Override
  5. public Boolean isValidEmail(String email) {
  6. //pattern
  7. String regex = "^(.+)@(.+)$";
  8. Pattern pattern = Pattern.compile(regex);
  9. Matcher matcher = pattern.matcher(email);
  10. return matcher.matches();
  11. }
  12.  
  13. @Override
  14. public Boolean isValidAge(String age) {
  15. //min 18
  16. if(age != null && !age.equals("")) return Integer.parseInt(age) >= 18;
  17. return false;
  18. }
  19.  
  20. @Override
  21. public Boolean isValidPost(String message) {
  22. //length post < 150
  23. return message.length() <= 150;
  24. }
  25. }

El bean PostBean utilitza l’anotació @Stateless, que indica que és un bean sense estat. El contenidor EJB, automàticament, crea les interfícies associades llegint aquest atribut durant el seu desplegament en el servidor.

Observeu que PostBean implementa la interfície PostBeanLocal i, per tant, ha de sobreescriure les seves funcions. Amb l’anotació @Override s’informa el compilador de Java que el mètode està sobreescrit i aquest comprova que el mètode de la interfície correspongui al mètode del bean.

La implementació de les funcions de validació de l’edat i del missatge no tenen cap misteri. En el primer cas, es retorna true si l’edat és més gran o igual a 18, i false en cas contrari. En el segons cas, es calcula la longitud del missatge amb el mètode .length() i es compara amb 150. Retorna true, indicant que és més petit i que té la longitud correcta, i false en cas contrari.

La implementació de la funció de validació del correu electrònic s’ha realitzat amb un patró (pattern). Si el correu electrònic coincideix amb el patró retorna true, i retorna false en cas contrari.

Un patró (pattern) és un objecte que conté la representació d’una expressió regular.

Un expressió regular és una seqüència de caràcters que ajuden a trobar cadenes dintre de cadenes utilitzant una sintaxi especial. Es poden utilitzar per cercar o manipular cadenes de text.

La expressió regular utilitzada és ^(.+)@(.+)$. Intentem desxifrar-la:

  • Un punt (.) simbolitza qualsevol caràcter.
  • El símbol ”+” indica que es pot repetir l’expressió anterior una o més vegades.
  • El símbol ”@” és ell mateix. Indica que ha d’aparèixer.
  • El símbol “^” indica que l’expressió següent ha d’aparèixer al principi de l’string.
  • El símbol ”$” indica que l’expressió anterior ha d’aparèixer al final de l’string.

En altres paraules, l’expressió regular “^(.+)@(.+)$” significa: al principi de l’string ha d’aparèixer com a mínim un caràcter, després hi ha d’haver una ”@”, i finalment ha d’acabar amb un caràcter o més.

No és un patró molt acurat per detectar correus electrònics vàlids i, per això, cal que intenteu elaborar un patró una mica més exacte amb aquestes opcions:

  • L’expressió “\d” vol dir que ha d’aparèixer un dígit. És equivalent a [0-9].
  • L’expressió “\D” vol dir que no ha de ser un dígit. És equivalent a [^0-9].
  • L’expressió “\s” vol dir que hi ha d’haver un espai en blanc. És equivalent a [\t\n\x0b\r\f].
  • L’expressió “\S” vol dir que no hi ha d’haver un espai en blanc. És equivalent a ”[^\s]”.
  • L’expressió “\w” vol dir que hi ha d’haver una lletra majúscula, minúscula, un dígit o el caràcter “_”. És equivalent a ”[a-zA-Z0-9_]”.
  • L’expressió “\W” vol dir que no hi ha d’haver una lletra majúscula, minúscula, un dígit o el caràcter “_”. És equivalent a ”[^/w]”.
  • L’expressió “\b” estableix el límit d’una paraula.
  • El símbol “*” indica que una expressió es pot repetir 0 o més vegades.
  • El símbol ”?” indica 0 o 1 vegades.

Exemples:

  • El símbol ”.” vol dir qualsevol caràcter.
  • L’expressió ”[^abc]” vol dir que ha de contenir qualsevol símbol excepte “a”, “b” o “c”. El símbol “^” dintre dels claudàtors indica negació.
  • L’expressió ”[a-z1-9]” indica un rang. Les lletres minúscules des de la ‘a’ fins a la “z” (incloent-hi ambdues) i els dígits 1 fins al 9 (també incloent-hi ambdues).

A Java no podeu utilitzar una sola “\”, s’han d’utilitzar dues. Així, si volem utilitzar l’expressió “\d” hauríem de posar “\d”.

Una vegada s’ha elaborat l’expressió regular, aquesta s’ha de compilar en un pattern. L’únic que falta és poder comparar-la amb una cadena de text per veure si aquesta compleix l’expressió regular. La comparació, a Java, la fa un objecte anomenat comparador (matcher).

Un objecte de tipus matcher és una eina que interpreta un pattern i fa una operació de comparació sobre una cadena de text donada.

Molts processadors de texts i llenguatges de programació fan ús d’expressions regulars per a procediments de cerca o bé de cerca i substitució de texts.

Vegeu com treballen conjuntament l’expressió regular, l’objecte pattern i l’objecte matcher per validar una adreça de correu electrònic:

  1. String email = "a@a";
  2. String regex = "^(.+)@(.+)$";
  3. Pattern pattern = Pattern.compile(regex);
  4. Matcher matcher = pattern.matcher(email);
  5. return matcher.matches();

Vegeu ara la utilització del bean de sessió sense estat al servlet. Vegeu com es declara el bean:

  1. @WebServlet(name = "PostServlet", urlPatterns = {"/PostServlet"})
  2. public class PostServlet extends HttpServlet {
  3.  
  4. @EJB
  5. private PostBeanLocal validation;
  6.  
  7. protected void processRequest(HttpServletRequest request, HttpServletResponse response)
  8. throws ServletException, IOException {
  9. response.setContentType("text/html;charset=UTF-8");
  10. try (PrintWriter out = response.getWriter()) {
  11. out.println("<!DOCTYPE html>");
  12. out.println("<html>");
  13. out.println("<head>");
  14. out.println("<title>Servlet ServletValidation</title>");
  15. out.println("</head>");
  16. out.println("<body>");
  17. out.println("<h1>EJB validation</h1>");
  18.  
  19. String mail = request.getParameter("mail");
  20. if (validation.isValidEmail(mail)) {
  21. out.println("<p>El correu electrònic " + mail + " és vàlid</p>");
  22. } else {
  23. out.println("<p>El correu electrònic no és vàlid.</p>");
  24. }
  25.  
  26. String edat = request.getParameter("edat");
  27. if (validation.isValidAge(edat)) {
  28. out.println("<p>Tens" + edat + " anys i pots escriure un missatge a l'aplicació.</p>");
  29. } else {
  30. out.println("<p>Has de ser major d'edat per escriure un missatge a l'aplicació.</p>");
  31. }
  32.  
  33. String missatge = request.getParameter("missatgePost");
  34. if (validation.isValidPost(missatge)) {
  35. out.println("<p>El missatge '" + missatge + "' és vàlid.</p>");
  36. } else {
  37. out.println("<p>El missatge no és vàlid. Ha de tenir menys de 150 caràcters.</p>");
  38. }
  39.  
  40. out.println("</body>");
  41. out.println("</html>");
  42. }
  43. }

El bean forma part del servlet i es defineix com una propietat de la classe. Com veieu, no podeu utilitzar directament el bean, s’ha d’emprar la seva interfície local, ja que el servlet s’executarà a la mateixa màquina virtual que el bean de sessió.

La seva declaració com a propietat del servlet és la següent:

  1. @EJB
  2. private PostBeanLocal validation;

Fixeu-vos en l’anotació @EJB. Vol dir que és un bean del tipus EJB i que la seva creació li pertany al contenidor de beans EJB.

Quan s’utilitzi la propietat validation, el contenidor EJB agafarà un bean del pool de beans que té disponibles per fer l’execució del mètode utilitzat. Per a cada execució de cadascun dels mètodes fa la mateixa operació. No es pot assegurar que sempre sigui el mateix bean.

Aquesta operació s’anomena injecció. El contenidor d’Enterprise JavaBeans subministra un bean quan una aplicació li demana un bean del seu pool de beans.

Vegeu el funcionament de l’aplicació Escriure un Post. A la figura podeu veure com ens mostra un formulari per introduir les dades següents: edat, correu electrònic i el missatge.

Figura Pantalla inicial de l’aplicació Escriure un Post

A la següent pantalla, les dades es validen amb EJB i es mostra el resultat de la validació, com podeu veure a la figura:

Figura Pantalla de validació de l’aplicació Escriure un Post

'Beans' de sessió amb estat

Vegeu ara una altra implementació de l’exercici Escriure un Post. Aquesta vegada s’utilitzarà un bean amb estat (stateful).

Els beans amb estat poden tenir propietats i, per tant, el contenidor EJB no tindrà un pool d’aquests tipus de beans. El contenidor d’EJB ens crearà el bean però ens assegura que mentre duri la petició aquest bean és nostre i podem utilitzar-lo per guardar informació a les seves propietats.

L’aplicació necessita tres dades de l’usuari: el correu electrònic, l’edat de l’usuari i el missatge a publicar. Aquestes dades corresponen a la informació que guardarà el bean.

L’objectiu de l’exercici és validar les dades. En comptes d’utilitzar tres mètodes per validar-les emprarem les validacions de Java del package validation.

JavaBeans Validation (Bean Validation) és un model de validació basat en restriccions (constraints). Les restriccions es posen com a anotacions en una propietat, un mètode o una classe JavaBean.

Les restriccions (constraints) les pot definir l’usuari (custom constraint), o bé utilitzar les que porta per defecte el model (built-in constraints).

Començareu creant el bean EJB amb estat ( File / New File / Enterprise JavaBeans/ Session Bean) i utilitzareu el mateix projecte Maven que heu emprat fins ara. La figura mostra la creació del bean amb estat:

Figura Creació d’un EJB ‘stateful bean’

El bean amb estat necessita, igual que el bean sense estat, d’una interfície local per accedir al bean. Activareu la creació automàtica d’aquesta interfície quan creeu el bean.

Aquesta interfície ha de tenir els mètodes que s’utilitzaran del bean, no les seves propietats. Així, la interfície, en aquest cas, tindrà els setters i getters de les propietats. Aquests mètodes se sobreescriuran al bean amb estat.

De moment, el codi de la interfície local és el següent:

  1. package cat.ioc.m7.formservlets;
  2.  
  3. import javax.ejb.Local;
  4.  
  5. @Local
  6. public interface PostBean2Local {
  7.  
  8. public int getEdat();
  9.  
  10. public String getMessage();
  11.  
  12. public String getEmail();
  13.  
  14. public void setEdat(String edat);
  15.  
  16. public void setMessage(String message);
  17.  
  18. public void setEmail(String email);
  19.  
  20. }

Fixeu-vos que el mètode setEdat rep un string com a paràmetre i el getEdat retorna un integer.

Quan s’utilitza el mètode setEdat, la informació, originàriament, es troba en un formulari. El protocol HTTP envia les dades al servlet i aquest cridarà el mètode setEdat. Totes les dades que s’envien amb un formulari són de tipus string. Així, la conversió de les dades es farà en el mateix bean.

El codi amb la implementació del bean és el següent:

  1. @Stateful
  2. public class PostBean2 implements PostBean2Local {
  3.  
  4. private int edat;
  5.  
  6. private String message;
  7.  
  8. private String email;
  9.  
  10. @Override
  11. public int getEdat() {
  12. return edat;
  13. }
  14.  
  15. @Override
  16. public void setEdat(String edat) {
  17. if(!edat.equals("")){
  18. this.edat=Integer.parseInt(edat);
  19. }
  20. else{
  21. this.edat = 0;
  22. }
  23. }
  24.  
  25. @Override
  26. public String getMessage() {
  27. return message;
  28. }
  29.  
  30. @Override
  31. public void setMessage(String message) {
  32. this.message = message;
  33. }
  34.  
  35. @Override
  36. public String getEmail() {
  37. return email;
  38. }
  39.  
  40. @Override
  41. public void setEmail(String email) {
  42. this.email = email;
  43. }
  44. }

Sembla un bean normal. L’única diferència és l’anotació @Stateful en declarar la classe del bean. Aquesta notació informa el servidor que es tracta d’un bean EJB amb sessió i estat.

Conté les propietats edat, missatge i correu electrònic. A més a més, implementa la interfície PostBean2Local.

Ara bé, on es posen les validacions? Hem dit que s’utilitzaran les anotacions del paquet Java Beans Validation, i aquestes es poden posar a les propietats, als mètodes o a la mateixa classe. On les poseu?

Heu de tenir en compte, per respondre a aquesta pregunta, que l’aplicació que utilitzi el bean no podrà accedir-hi directament. Llavors, si voleu veure que les restriccions de seguretat s’estan aplicant, necessiteu utilitzar la classe a la qual podeu accedir directament.

Aquesta classe és la interfície del bean. Llavors, com que aquesta interfície només posseeix els mètodes del bean, i no les seves propietats, posareu les validacions en els mètodes.

Fixeu-vos en la nova implementació d’aquesta interfície:

  1. @Local
  2. public interface PostBean2Local {
  3.  
  4. @Min(value=18, message = "Has de ser major d'edat per escriure un missatge.")
  5. public int getEdat();
  6.  
  7. @NotNull @Size(min=1, max=150, message = "El missatge no és vàlid. Ha de tenir menys de 150 caràcters.")
  8. public String getMessage();
  9.  
  10. @NotNull @Pattern(regexp="^(.+)@(.+)$", message = "El correu no és vàlid.")
  11. public String getEmail();
  12.  
  13. public void setEdat(String edat);
  14.  
  15. public void setMessage(String message);
  16.  
  17. public void setEmail(String email);
  18.  
  19. }

Per fer les validacions de cadascuna de les dades s’han utilitzat les següents anotacions:

  • @Min: comprova que el valor sigui un enter i sigui menor que l’atribut value passat com a paràmetre de la restricció. En el cas que es compleixi, es genera un violació de la restricció (ConstraintViolation) que té com a missatge el text de l’atribut message.
  • @NotNull: comprova si el valor és null. Si és null es genera una violació de la restricció.
  • @Size: admet dos atributs: min i max. La mida del text ha d’estar entre aquests dos valors. Si no és això es genera una violació de la restricció que té com a missatge el text de l’atribut message.
  • @Pattern: admet una expressió regular. La dada que retorna la funció ha de satisfer l’expressió regular. En el cas que no la compleixi es mostrarà per pantalla el missatge de l’atribut message.

Heu posat les restriccions en els mètodes get perquè us interessa que sigui quan vulguem accedir al valor que ens indiqui si es compleix la restricció.

Totes aquestes restriccions estan creades per defecte i pertanyen al paquet Java Validation. En aquest cas no heu necessitat crear restriccions noves.

Una vegada hem acabat d’implementar les validacions i el bean amb estat, creareu el servlet que rebrà les dades del formulari HTML i les enviarà al bean PostBean2. Creareu un nou servlet ( File / New File / Web / Servlet ) i l’anomenareu PostServlet2. No activareu la utilització del fitxer web.xml durant la seva creació, ja que fareu servir anotacions.

La implementació del servlet PostServlet2 és la següent:

  1. @WebServlet(name = "PostServlet2", urlPatterns = {"/PostServlet2"})
  2. public class PostServlet2 extends HttpServlet {
  3.  
  4. @Resource Validator validator;
  5.  
  6. protected void processRequest(HttpServletRequest request, HttpServletResponse response)
  7. throws ServletException, IOException {
  8.  
  9. response.setContentType("text/html;charset=UTF-8");
  10. try (PrintWriter out = response.getWriter()) {
  11. out.println("<!DOCTYPE html>");
  12. out.println("<html>");
  13. out.println("<head>");
  14. out.println("<title>Servlet ServletValidation2</title>");
  15. out.println("</head>");
  16. out.println("<body>");
  17.  
  18. String missatge = request.getParameter("missatgePost");
  19. String mail = request.getParameter("mail");
  20. String edat = request.getParameter("edat");
  21.  
  22. PostBean2Local bean = (PostBean2Local) new InitialContext().lookup(
  23. "java:global/formServlets/PostBean2");
  24.  
  25.  
  26. bean.setMessage(missatge);
  27. bean.setEmail(mail);
  28. bean.setEdat(edat);
  29.  
  30. out.println("<h1>Dades rebudes del formulari</h1>");
  31. out.println("<p>Missatge: " + bean.getMessage() + "</p>");
  32. out.println("<p>Edat: " + bean.getEdat() + "</p>");
  33. out.println("<p>Email: " + bean.getEmail() + "</p>");
  34.  
  35. out.println("<h1>Llistat de validacions:</h1>");
  36. for (ConstraintViolation c : validator.validate(bean)) {
  37. out.println("<p>" + c.getMessage() + "</p>");
  38. }
  39.  
  40. out.println("</body>");
  41. out.println("</html>");
  42. } catch (NamingException ex) {
  43. Logger.getLogger(PostServlet2.class.getName()).log(Level.SEVERE, null, ex);
  44. }
  45. }

Bàsicament, el servlet rep les dades enviades per l’usuari:

  1. String missatge = request.getParameter("missatgePost");
  2. String mail = request.getParameter("mail");
  3. String edat = request.getParameter("edat");

i les introdueix en el bean:

  1. PostBean2Local bean = (PostBean2Local) new InitialContext().lookup(
  2. "java:global/formServlets/PostBean2");
  3.  
  4. bean.setMessage(missatge);
  5. bean.setEmail(mail);
  6. bean.setEdat(edat);

Com que és un bean amb estat, s’ha de demanar la seva utilització. L’objecte InitialContext és l’encarregat de cercar qualsevol tipus d’objecte. Per tal que pugui trobar-lo, li heu de dir el seu nom i el projecte al qual pertany.

  1. PostBean2Local bean = (PostBean2Local) new InitialContext().lookup(
  2. "java:global/formServlets/PostBean2");

El codi anterior no crea l’objecte, us en cerca un per a vosaltres. Fixeu-vos que quan demanem per l’objecte PostBean2 ens retornen un objecte del tipus PostBean2Local. Això és degut al fet que el contenidor EJB no permet accedir directament a l’objecte.

Una vegada teniu l’objecte, ja podeu introduir les dades de l’usuari:

  1. bean.setMessage(missatge);
  2. bean.setEmail(mail);
  3. bean.setEdat(edat);

Una vegada el bean té les dades es força la seva validació manualment. Si hi ha alguna violació de les restriccions posades en els mètodes, en validar-se es generen les ConstraintViolation. El missatge d’aquestes violacions de restricció es mostrarà per pantalla.

  1. for (ConstraintViolation c : validator.validate(bean)) {
  2. out.println("<p>" + c.getMessage() + "</p>");
  3. }

En el cicle de vida dels servlets no existeix una etapa de validació de les dades. Aquesta s’ha de fer manualment, per això necessiteu accedir al validador de Java Validation. En canvi, en un framework com pot ser Spring o JSF, el validador s’executa automàticament en un moment determinat del cicle de vida del framework.

Per accedir al validador s’ha creat una propietat del servlet del tipus Validator i s’ha informat que aquest és un recurs de Java. El codi és el següent:

  1. @Resource Validator validator;

L’etiqueta @Resource informa el servidor que l’aplicació necessita un validador. Aquest validador s’injecta, igual que el contenidor EJB injecta beans sense estat, la primera vegada que s’utilitza.

Una vegada teniu el validador, s’executa el mètode validar i per paràmetre s’envia el bean que es vol validar.

  1. validator.validate(bean)

Aquest validador utilitzarà els mètodes get per obtenir les dades, i si hi ha alguna violació de restricció s’emmagatzemarà per retornar-lo una vegada s’hagin completat totes les validacions.

Es pot iterar aquest conjunt per accedir a totes les violacions i mostrar-les per pantalla:

  1. for (ConstraintViolation c : validator.validate(bean)) {
  2. out.println("<p>" + c.getMessage() + "</p>");
  3. }

Una vegada s’ha acabat d’implementar el servlet, creareu una rèplica de la pàgina HTML de l’exercici anterior on demanarem l’edat, el correu electrònic i el missatge per mostrar per pantalla. L’únic canvi serà el recurs al qual se li enviaran les dades (el mètode action del formulari). A aquesta pàgina l’anomenareu escriurePost2.html. El contingut de la pàgina és el següent:

  1. <!DOCTYPE html>
  2. <head>
  3. <title>Bean Validation</title>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. </head>
  7. <body>
  8. <h1>Escriu un missatge:</h1>
  9. <form id="formulari" method="POST" action="PostServlet2">
  10. <label for="mail">Correu electrònic:</label>
  11. <input id="mail" type="text" name="mail"/>
  12. <label for="age">Edat:</label>
  13. <input id="age" type="number" name="edat" min="0">
  14. <input type="submit">
  15. </form>
  16. <label>Missatge:</label>
  17. <textarea name="missatgePost" form="formulari"></textarea>
  18. </body>
  19. </html>

Vegeu el funcionament de l’aplicació Escriure un Post2. A la figura podeu veure com ens mostra un formulari per introduir-hi les dades: edat, correu electrònic i el missatge.

Figura Pantalla inicial de l’aplicació Escriure un Post2

A la següent pantalla, les dades es validen amb EJB i es mostra el resultat de la validació, com podeu veure en la figura.

Figura Pantalla de validació de l’aplicació Escriure un Post2

Dades de subscripció

En aquest apartat s’aprofundeix en la validació de dades utilitzant EJB de sessió amb estat. Anteriorment s’ha fet la validació amb restriccions proporcionades per la llibreria Java Validation (built-in constraint), i en aquest apartat introduirem les validacions de restriccions d’usuari (custom constraint).

Es vol crear un registre d’usuaris de l’aplicació. Es demanarà a l’usuari la següent informació:

  • nom
  • cognoms
  • data de naixement
  • gènere
  • correu electrònic
  • telèfon
  • color favorit
  • marca de cotxe
  • vehicle1 (valor “bicicleta”, si l’usuari en posseeix una)
  • vehicle2 (valor “moto”, si l’usuari en posseeix una)
  • navegador d’Internet favorit

S’han de validar totes les dades. En concret, es vol comprovar que:

  • No siguin null els camps nom, cognoms, gènere, marca de cotxe i navegador d’Internet favorit.
  • No estiguin buits els camps nom, cognom, data de naixement, correu electrònic, telèfon, vehicle1, vehicle2 i navegador d’Internet.
  • L’usuari sigui major d’edat.
  • El correu electrònic i el telèfon siguin vàlids.
  • El color estigui en hexadecimal i tingui la forma ”#hhhhhh” o ”#hhh”.

Java Interface

Una interfície a Java és una classe abstracta que especifica els mètodes que han d’implementar totes les classes que implementin aquesta interfície. Les classes que implementin una interfície han d’implementar tots els mètodes o declarar-se també com a abstractes. L’avantatge d’utilitzar interfícies és que simulen l’herència múltiple.

El projecte utilitzat serà el mateix que heu emprat fins ara, i començareu creant el bean EJB amb estat. Creareu també, automàticament, la interfície local associada. Per crear el bean amb l’IDE NetBeans aneu a File / New File / Enterprise JavaBeans / Session Bean i l’anomeneu SignupBean.

Aquest bean serà un bean de sessió amb estat; per tant, haurà d’aparèixer l’anotació @Stateful abans de la declaració de la classe. A més a més, tindrà totes les propietats corresponents al registre de l’usuari. Podeu veure la seva implementació en el següent codi:

  1. @Stateful
  2. public class SignupBean implements SignupBeanLocal {
  3.  
  4. private String nom;
  5.  
  6. private String cognoms;
  7.  
  8. private String naixement;
  9.  
  10. private String sexe;
  11.  
  12. private String email;
  13.  
  14. private String telefon;
  15.  
  16. private String color;
  17.  
  18. private String marcaCotxe;
  19.  
  20. private String vehicle1;
  21.  
  22. private String vehicle2;
  23.  
  24. private String navegador;
  25.  
  26. @Override
  27. public String print() {
  28.  
  29. return "<p>nom=" + nom + "</p> <p>"
  30. + "cognoms=" + cognoms + "</p><p>"
  31. + "naixement=" + naixement + "</p><p>"
  32. + "sexe=" + sexe + "</p> <p>email=" + email + "</p><p>"
  33. + "telefon=" + telefon + "</p> <p>"
  34. + "color=" + color + "</p> <p>"
  35. + "marcaCotxe=" + marcaCotxe + "</p> <p>"
  36. + "vehicle1=" + vehicle1 + "</p> <p>"
  37. + "vehicle2=" + vehicle2 + "</p> <p>"
  38. + "navegador=" + navegador + "</p>";
  39. }
  40.  
  41. @Override
  42. public String getNom() {
  43. return nom;
  44. }
  45.  
  46. @Override
  47. public void setNom(String nom) {
  48. this.nom = nom;
  49. }
  50.  
  51. @Override
  52. public String getCognoms() {
  53. return cognoms;
  54. }
  55.  
  56. @Override
  57. public void setCognoms(String cognoms) {
  58. this.cognoms = cognoms;
  59. }
  60.  
  61. @Override
  62. public String getNaixement() {
  63. return naixement;
  64. }
  65.  
  66. @Override
  67. public void setNaixement(String naixement) {
  68. this.naixement = naixement;
  69. }
  70.  
  71. @Override
  72. public String getSexe() {
  73. return sexe;
  74. }
  75.  
  76. @Override
  77. public void setSexe(String sexe) {
  78. this.sexe = sexe;
  79. }
  80.  
  81. @Override
  82. public String getEmail() {
  83. return email;
  84. }
  85.  
  86. @Override
  87. public void setEmail(String email) {
  88. this.email = email;
  89. }
  90.  
  91. @Override
  92. public String getTelefon() {
  93. return telefon;
  94. }
  95.  
  96. @Override
  97. public void setTelefon(String telefon) {
  98. this.telefon = telefon;
  99. }
  100.  
  101. @Override
  102. public String getColor() {
  103. return color;
  104. }
  105.  
  106. @Override
  107. public void setColor(String color) {
  108. this.color = color;
  109. }
  110.  
  111. @Override
  112. public String getMarcaCotxe() {
  113. return marcaCotxe;
  114. }
  115.  
  116. @Override
  117. public void setMarcaCotxe(String marcaCotxe) {
  118. this.marcaCotxe = marcaCotxe;
  119. }
  120.  
  121. @Override
  122. public String getVehicle1() {
  123. return vehicle1;
  124. }
  125.  
  126. @Override
  127. public void setVehicle1(String vehicle1) {
  128. this.vehicle1 = vehicle1;
  129. }
  130.  
  131. @Override
  132. public String getVehicle2() {
  133. return vehicle2;
  134. }
  135.  
  136. @Override
  137. public void setVehicle2(String vehicle2) {
  138. this.vehicle2 = vehicle2;
  139. }
  140.  
  141. @Override
  142. public String getNavegador() {
  143. return navegador;
  144. }
  145.  
  146. @Override
  147. public void setNavegador(String navegador) {
  148. this.navegador = navegador;
  149. }
  150.  
  151. }

De moment, la interfície local associada al bean anterior només conté els getters i setters de les propietats del bean.

A continuació s’han d’introduir les restriccions de validació necessàries per assegurar-nos que les dades compleixen els requisits establerts.

Les llibreries de validació proporcionen les restriccions més habituals. Així, podeu utilitzar les següents anotacions estàndard:

  • @NotNull: s’aplicarà als mètodes de la interfície local SignupBeanLocal que es vulgui comprovar que no és null. Llavors, aquesta anotació s’aplicarà als mètodes get de les propietats: nom, cognoms, gènere, marca de cotxe i navegador d’Internet favorit.
  • @Pattern: aquesta anotació només s’aplicarà per comprovar que el telèfon introduït és vàlid. Creareu una expressió regular que ho comprovi.

Vegeu com aplicar les restriccions anteriors a la interfície local:

  1. @Local
  2. public interface SignupBeanLocal {
  3.  
  4. @NotNull (message = "El nom no pot estar buit.")
  5. public String getNom();
  6.  
  7. @NotNull(message = "El cognom no pot estar buit.")
  8. public String getCognoms();
  9.  
  10. public String getNaixement();
  11.  
  12. @NotNull(message = "El gènere no pot estar buit.")
  13. public String getSexe();
  14.  
  15. public String getEmail();
  16.  
  17. @Pattern(regexp = "\d{3}\-\d{3}-\d{3}", message = "El telèfon no és vàlid.")
  18. public String getTelefon();
  19.  
  20. public String getColor();
  21.  
  22. @NotNull(message = "La marca del cotxe no pot estar buida.")
  23. public String getMarcaCotxe();
  24.  
  25. public String getVehicle1();
  26.  
  27. public String getVehicle2();
  28.  
  29. @NotNull(message = "El navegador no pot estar buit.")
  30. public String getNavegador();
  31.  
  32. public String print();
  33.  
  34. public void setNom(String nom);
  35.  
  36. public void setCognoms(String cognoms);
  37.  
  38. public void setNaixement(String naixement);
  39.  
  40. public void setSexe(String sexe);
  41.  
  42. public void setEmail(String email);
  43.  
  44. public void setTelefon(String telefon);
  45.  
  46. public void setColor(String color);
  47.  
  48. public void setMarcaCotxe(String marcaCotxe);
  49.  
  50. public void setVehicle1(String vehicle1);
  51.  
  52. public void setVehicle2(String vehicle2);
  53.  
  54. public void setNavegador(String navegador);
  55. }

Observeu que només s’ha de modificar el missatge de la restricció @NotNull perquè funcioni de la manera esperada. En canvi, a la restricció @Pattern de la propietat “telèfon” s’ha d’afegir l’expressió regular que utilitzarà per comprovar si és vàlid.

L’expressió utilitzada és la següent:

  1. @Pattern(regexp = "\d{3}\-\d{3}-\d{3}", message = "El telèfon no és vàlid.")

Amb la sintaxi {nombre}, d’una expressió regular, s’indica el nombre de repeticions de l’expressió anterior. Llavors, quan s’indica \d{3} significa que es volen 3 dígits.

La resta de restriccions no es troben implementades per defecte a la llibreria de validacions. Heu de crear les restriccions d’usuari següents per fer comprovacions específiques de la vostra aplicació. El fet de crear aquestes restriccions permet reutilitzar-les posteriorment en altres punts del programa:

  • @Check18: comprova que l’usuari sigui major d’edat. Rep un string per paràmetre i comprova que a data d’avui sigui més gran de 18 anys.
  • @Color: utilitzant una expressió regular comprova que el color introduït tingui el format #cccccc o bé #ccc, on c és un nombre hexadecimal. No s’utilitza la restricció @Pattern perquè es vol reaprofitar posteriorment.
  • @Email: utilitzant una expressió regular comprova que l’adreça electrònica sigui vàlida. No s’utilitza la restricció @Pattern perquè es vol reaprofitar posteriorment.
  • @NotBlank: comprova que el text no sigui buit (””).

Començareu creant la restricció @Check18. Anireu a File / New File / Bean Validation / Validation Constraint, l’anomenareu Check18 i activareu la creació d’una classe validadora del tipus string (vegeu la figura).

Figura Creació d’una restricció d’usuari nova

Aquest procediment crea dues classes. La classe Check18.java és la descripció de l’anotació @Check18. S’especifiquen els paràmetres de la restricció tals com si és una restricció de mètode, de classe o d’atribut.

  1. @Documented
  2. @Constraint(validatedBy = Check18Validator.class)
  3. @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
  4. @Retention(RetentionPolicy.RUNTIME)
  5. public @interface Check18 {
  6.  
  7. String message() default "{cat.ioc.m7.formservlets.constraints.PastDate}";
  8.  
  9. Class<?>[] groups() default {};
  10.  
  11. Class<? extends Payload>[] payload() default {};
  12. }

El més important de la classe anterior és el validador que s’utilitza. Observeu que amb l’anotació @Constraint s’informa que la classe Check18Validator és el validador associat a aquesta anotació. És a dir, allà on s’apliqui aquesta anotació, qui realment comprova si la dada és vàlida serà aquesta classe.

L’anotació @Target especifica on es pot utilitzar l’anotació @Check18. En aquest cas, s’ha permès la seva utilització en la declaració de mètodes, de propietats i d’altres anotacions.

L’altre classe que es crea és el validador de la classe. En aquest cas, el seu nom és Check18Validator. Aquesta classe ha de fer la validació de la dada. Per això, en la seva creació heu d’informar del tipus de dada que validarà.

Vegeu la implementació d’aquesta classe:

  1. public class Check18Validator implements ConstraintValidator<Check18, String> {
  2.  
  3. @Override
  4. public void initialize(Check18 constraintAnnotation) {
  5. }
  6.  
  7. @Override
  8. public boolean isValid(String value, ConstraintValidatorContext context) {
  9. try {
  10. if (!value.equals("")) {
  11. Date naixement = new SimpleDateFormat("yyyy-MM-dd").parse(value);
  12. int anyAra = Calendar.getInstance().get(Calendar.YEAR);
  13. Calendar cal = Calendar.getInstance();
  14. cal.setTime(naixement);
  15. int anyNaixement = cal.get(Calendar.YEAR);
  16. return anyAra - anyNaixement >= 18;
  17. }
  18. } catch (ParseException ex) {
  19. Logger.getLogger(Check18Validator.class.getName()).log(Level.SEVERE, null, ex);
  20. }
  21. return false;
  22. }
  23. }

En crear el validador, NetBeans afegeix dues excepcions: una en el mètode initialize i l’altra en el mètode isValid. Recordeu de treure-les perquè funcioni la validació.

El mètode Initialize s’executarà sempre abans del mètode isValid, i s’utilitza per inicialitzar qualsevol propietat que necessiti el validador.

El mètode isValid és qui fa la validació de la dada. Retorna false si la restricció es compleix i, per tant, s’ha de generar una violació de restricció. Retorna true en cas contrari.

El paràmetre value del mètode isValid correspon a la dada de la propietat a validar, i no es pot sobreescriure.

En aquest cas s’ha de calcular l’edat de la persona. Observeu la seva implementació:

  1. if (!value.equals("")) {
  2. Date naixement = new SimpleDateFormat("yyyy-MM-dd").parse(value);
  3. int anyAra = Calendar.getInstance().get(Calendar.YEAR);
  4. Calendar cal = Calendar.getInstance();
  5. cal.setTime(naixement);
  6. int anyNaixement = cal.get(Calendar.YEAR);
  7. return anyAra - anyNaixement >= 18;
  8. }
  9. return false;

Si el valor value no és buit es transforma en tipus Date. S’aconsegueix l’any actual utilitzant l’objecte Calendar i després, també amb el mateix objecte, s’aconsegueix l’any de la data de naixement. Si la resta, entre l’any actual i l’any del dia que va néixer, és més petita que 18, es retorna false i, llavors, es genera una violació de restricció. Es retorna true en cas contrari i no es genera cap violació de restricció.

Una vegada s’ha implementat aquest mètode ja es pot utilitzar. Vegeu com s’aplica al mètode getNaixement de la interfície SignupBeanLocal:

  1. @Check18 (message = "Has de tenir més de 18 anys.")
  2. public String getNaixement();

Existeix una altra manera per crear restriccions. La idea és que, a vegades, no cal crear un validador nou, perquè es pot aprofitar una altra restricció per validar la que es vol crear.

Per exemple, voleu crear una validació pel color. Com que per validar aquesta restricció utilitzareu una expressió regular i existeix ja una restricció per a patterns, llavors no cal crear un validador nou. Reaprofitareu aquesta restricció (@Pattern) i aplicareu l’expressió regular concreta.

Començareu creant una nova restricció (File / New File / Bean Validation / Validation Constraint ) anomenada Color.java sense activar la creació d’un validador (vegeu la figura).

Figura Creació d’una restricció sense validador

Una vegada creada la restricció Color afegireu l’anotació @Pattern com a anotació de restricció de la interfície. Aquesta anotació @Pattern accepta un paràmetre corresponent al llistat de patterns a comprovar. Vegeu-ho:

  1. @Pattern.List({@Pattern(regexp = "#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})")})

En incloure aquest llistat, la classe Color ha de definir una interfície anomenada List amb mètode anomenat value() que retorni un array de la classe Color. Exemple:

  1. @interface List {
  2. Color[] value();
  3. }

En introduir aquests dos elements ja no és necessari disposar d’un validador específic per a la classe Color, ja que s’utilitzarà la restricció @Pattern amb l’expressió regular ”#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})”.

Finalment, la restricció Color.java està implementada de la següent manera:

  1. @Pattern.List({@Pattern(regexp = "#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})")})
  2. @Constraint(validatedBy = {})
  3. @Documented
  4. @Target({ElementType.METHOD,
  5. ElementType.FIELD,
  6. ElementType.ANNOTATION_TYPE,
  7. ElementType.CONSTRUCTOR,
  8. ElementType.PARAMETER})
  9. @Retention(RetentionPolicy.RUNTIME)
  10. public @interface Color {
  11.  
  12. String message() default "{invalid.color}";
  13.  
  14. Class<?>[] groups() default {};
  15.  
  16. Class<? extends Payload>[] payload() default {};
  17.  
  18. @Target({ElementType.METHOD,
  19. ElementType.FIELD,
  20. ElementType.ANNOTATION_TYPE,
  21. ElementType.CONSTRUCTOR,
  22. ElementType.PARAMETER})
  23. @Retention(RetentionPolicy.RUNTIME)
  24. @Documented
  25. @interface List {
  26. Color[] value();
  27. }
  28. }

Observeu com l’anotació @Constraint, que s’utilitza per indicar el validador a fer servir, està buida.

Ara ja es pot aplicar aquesta restricció al mètode getColor de la classe SignUpBeanLocal.

  1. @Color(message = "El color no pot estar buit.")
  2. public String getColor();

Falta crear dues restriccions més: la restricció anomenada @NotBlank, que comprova si una cadena de caràcters és buida (””), i la restricció anomenada @Email, que comprova si un mail és vàlid amb una expressió regular.

Es proposa que intenteu fer vosaltres la implementació d’aquestes dues restriccions. La primera restricció, @NotBlank, s’ha de crear amb un validador associat i, en canvi, la restricció @Email cal crear-la sense validador associat utilitzant la restricció @Pattern.

Podeu trobar la solució d’aquestes restriccions en el codi proporcionat amb aquest apartat.

Finalment, la classe SignUpBeanLocal ja disposa de totes les validacions demanades:

  1. @Local
  2. public interface SignupBeanLocal {
  3.  
  4. @NotNull (message = "El nom no pot estar buit.")
  5. @NotBlank(message = "El nom no pot estar buit.")
  6. public String getNom();
  7.  
  8. @NotNull(message = "El cognom no pot estar buit.")
  9. @NotBlank(message = "El cognom no pot estar buit.")
  10. public String getCognoms();
  11.  
  12. @Check18 (message = "Has de tenir més de 18 anys.")
  13. @NotBlank(message = "La data de naixement no pot estar buida.")
  14. public String getNaixement();
  15.  
  16. @NotNull(message = "El gènere no pot estar buit.")
  17. public String getSexe();
  18.  
  19. @Email
  20. @NotBlank(message = "El correu no pot estar buit.")
  21. public String getEmail();
  22.  
  23. @Pattern(regexp = "\(\d{3}\)\d{3}-\d{4}", message = "El telèfon no és vàlid.")
  24. @NotBlank(message = "El telèfon no pot estar buit.")
  25. public String getTelefon();
  26.  
  27. @Color(message = "El color no pot estar buit.")
  28. public String getColor();
  29.  
  30. @NotNull(message = "La marca del cotxe no pot estar buida.")
  31. public String getMarcaCotxe();
  32.  
  33. @NotBlank(message = "El vehicle1 no pot estar buit.")
  34. public String getVehicle1();
  35.  
  36. @NotBlank(message = "El vehicle2 no pot estar buit.")
  37. public String getVehicle2();
  38.  
  39. @NotNull(message = "El navegador no pot estar buit.")
  40. @NotBlank(message = "El navegador no pot estar buit.")
  41. public String getNavegador();
  42.  
  43. public String print();
  44.  
  45. public void setNom(String nom);
  46.  
  47. public void setCognoms(String cognoms);
  48.  
  49. public void setNaixement(String naixement);
  50.  
  51. public void setSexe(String sexe);
  52.  
  53. public void setEmail(String email);
  54.  
  55. public void setTelefon(String telefon);
  56.  
  57. public void setColor(String color);
  58.  
  59. public void setMarcaCotxe(String marcaCotxe);
  60.  
  61. public void setVehicle1(String vehicle1);
  62.  
  63. public void setVehicle2(String vehicle2);
  64.  
  65. public void setNavegador(String navegador);
  66. }

Una vegada heu acabat d’implementar les validacions i el bean amb estat, creareu el servlet que rebrà les dades del formulari HTML i les enviarà al bean SignupBean. Creareu un nou servlet (File / New File / Web / Servlet) i l’anomenareu SignUpServlet. No activareu la utilització del fitxer web.xml durant la seva creació, ja que fareu servir anotacions.

La implementació del servlet SignUpServlet és la següent:

  1. @WebServlet(name = "SignUpServlet", urlPatterns = {"/SignUpServlet"})
  2. public class SignUpServlet extends HttpServlet {
  3.  
  4.  
  5. @Resource Validator validator;
  6.  
  7. protected void processRequest(HttpServletRequest request, HttpServletResponse response)
  8. throws ServletException, IOException {
  9. response.setContentType("text/html;charset=UTF-8");
  10. try (PrintWriter out = response.getWriter()) {
  11.  
  12. out.println("<!DOCTYPE html>");
  13. out.println("<html>");
  14. out.println("<head>");
  15. out.println("<title>Servlet SingUp</title>");
  16. out.println("</head>");
  17. out.println("<body>");
  18.  
  19. SignupBeanLocal bean = (SignupBeanLocal) new InitialContext().lookup(
  20. "java:global/formServlets/SignupBean");
  21.  
  22. try{
  23. out.println("<h1>Dades rebudes del formulari</h1>");
  24. BeanUtils.populate (bean, request.getParameterMap());
  25. out.println("<p>" + bean.print() + "</p>");
  26. }
  27. out.println(e.getMessage());
  28. }
  29.  
  30. out.println("<h1>Llistat de validacions:</h1>");
  31.  
  32. for (ConstraintViolation c : validator.validate(bean)) {
  33. out.println("<p>" + c.getMessage() + "</p>");
  34. }
  35.  
  36. out.println("</body>");
  37. out.println("</html>");
  38. } catch (NamingException ex) {
  39. Logger.getLogger(SignUpServlet.class.getName()).log(Level.SEVERE, null, ex);
  40. }
  41. }

La implementació del servlet és molt semblant al servlet PostServlet2. Per no ser repetitius només s’explicaran les diferències.

La primera diferència és el bean que es recupera del contenidor EJB mitjançant l’objecte InitialContext.

  1. SignupBeanLocal bean = (SignupBeanLocal) new InitialContext().lookup(
  2. "java:global/formServlets/SignupBean");

L’objecte EJB a recuperar és SignupBean, però el contenidor EJB retorna un objecte del tipus SignupBeanLocal, tal com s’esperava.

La segona diferència és la utilització de la classe BeanUtils per associar les propietats del bean amb les dades enviades per l’usuari. En utilitzar aquesta classe s’ha de cercar la seva dependència amb el programa NetBeans.

Apache Commons BeanUtils

La biblioteca de Java proporciona mecanismes per accedir dinàmicament a les propietats d’un objecte, és a dir, sense utilitzar els seus mètodes getXXX o setXXX. Aquests mecanismes són bastant complexos i requereixen l’ús dels paquets java.lang.reflect i java.beans. El component BeanUtils d’Apache proporciona un mecanisme fàcil d’utilitzar per accedir a aquestes funcionalitats.

  1. BeanUtils.populate (bean, request.getParameterMap());

Perquè funcioni el mètode populate de l’objecte BeanUtils s’ha de crear el formulari web de tal manera que el nom dels inputs del formulari correspongui amb el nom dels mètodes set de les propietats. Exemple:

  1. <input id="naix" type="date" name="naixement"><br>

L’atribut name de l’input és naixement, i aquest correspon al nom de la propietat del bean:

  1. private String naixement;

Aquesta propietat genera un mètode setNaixement que és cridat pel mètode populate de l’objecte BeanUtils en trobar un paràmetre anomenat naixement en l’objecte request.

D’aquesta manera us estalvieu de fer manualment tots els setters dels paràmetres.

Una vegada heu acabat d’implementar el servlet, creareu la pàgina HTML associada amb el formulari web i l’anomenareu subscripcio.html.

  1. <!DOCTYPE html>
  2. <head>
  3. <title>Subscripció</title>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. </head>
  7. <body>
  8. <h1>Per subscriure't necessitem les següents dades:</h1>
  9. <form id="formulari" method="POST" action="SignUpServlet">
  10. <label for="nom">Nom:</label>
  11. <input type="text" name="nom" id="nom"><br>
  12. <label for="cognoms">Cognoms:</label>
  13. <input type="text" name="cognoms" id="cognoms"><br>
  14. <label for="naix">Data de naixement:</label>
  15. <input id="naix" type="date" name="naixement"><br>
  16. <input type="radio" name="sexe" value="home" checked> Home<br>
  17. <input type="radio" name="sexe" value="dona"> Dona<br>
  18. <label for="email">Correu electrònic:</label>
  19. <input id="email" type="text" name="email"><br>
  20. <label for="telf">Telèfon:</label>
  21. <input id="telf" type="tel" name="telefon"><br>
  22. <label for="color">Color preferit:</label>
  23. <input id="color" type="color" name="color"><br>
  24.  
  25. <label for="cotxe">Marca del cotxe:</label>
  26. <select id="cotxe" name="marcaCotxe">
  27. <option value="volvo">Volvo</option>
  28. <option value="saab">Saab</option>
  29. <option value="fiat">Fiat</option>
  30. <option value="audi">Audi</option>
  31. <option value="audi">Altre</option>
  32. </select><br>
  33. <input type="checkbox" name="vehicle1" value="Bicicleta"> Tinc una bicicleta<br>
  34. <input type="checkbox" name="vehicle2" value="Moto"> Tinc una moto<br>
  35. <input list="browsers" name="navegador">
  36. <datalist id="browsers">
  37. <option value="Internet Explorer">
  38. <option value="Firefox">
  39. <option value="Chrome">
  40. <option value="Opera">
  41. <option value="Safari">
  42. </datalist>
  43. <input type="submit">
  44. </form>
  45.  
  46. </body>
  47. </html>

Vegeu el funcionament de l’aplicació Subscripció. En la figura podeu veure com ens mostra un formulari per introduir-hi les dades.

Figura Pantalla inicial de l’aplicació subscripció

Finalment, les dades es validen amb EJB i es mostra el resultat de la validació, com podeu veure en la figura:

Figura Resultat de la validació de la subscripció

Què s'ha après?

En finalitzar aquest apartat l’estudiant ha après a:

  • Tractar les dades enviades mitjançant un formulari a un servlet
  • Utilitzar l’arquitectura EJB juntament amb l’arquitectura Java Web
  • Diferenciar entre un Java Bean i un Enterprise Java Bean
  • Diferenciar entre un Bean EJB amb estat i un sense estat
  • Crear i utilitzar expressions regulars
  • Crear i utilitzar les restriccions (constraints) EJB

En acabar aquesta apartat, els estudiants ja estan preparats per començar les activitats proposades en aquest apartat i poden continuar amb el seu aprenentatge del llenguatge Java Web.

Anar a la pàgina anterior:
Exercicis d'autoavaluació
Anar a la pàgina següent:
Activitats