Pag. 1/12 Trasformare una Java (Console) Application in una Web Application (con utilizzo di un database MySQL) Prendiamo in esame un qualsiasi progetto NetBeans tra quelli studiati che fa uso di un database MySQL (nel nostro caso il database si chiama studenti_db e contiene le classiche tabelle studenti_tbl e classi_tbl). Riporto di seguito lo schema ER che abbiamo usato in classe in diverse occasioni. Fig. 1: Diagramma ER con le entità Studente e ClasseScol L'applicativo da cui partiamo per dimostrare la tesi contenuta nel titolo di questo articolo si chiama StudentiJavaApp. Si tratta di un applicativo classico nel senso che l'output è la “console”, quindi in effetti è poco utile nella pratica. L'applicativo è organizzato come si può vedere nella figura. La classe ClasseScol “mappa” la tabella classi_tbl e la classe Studente “mappa” la tabella studenti_tbl. Fig. 2: Struttura dell'applicazione, delle classi e dei package Pag. 2/12 L'interfaccia IDAO contiene tutti i metodi necessari al corretto funzionamento dell'applicativo e soddisfa a diverse richieste (per esempio, esiste un metodo che individua l'elenco degli studenti appartenenti a una certa classe, come pure esiste un metodo che conta le classi con più di 3 studenti ecc) mentre la classe DAO è una implementazione concreta dei metodi dell'interfaccia IDAO. Torno a ripetere che l'interfaccia IDAO non è strettamente necessaria ma serve solo a impedire che lo studente alteri di sua iniziativa i nomi dei metodi dell'interfaccia. La classe Main sostanzialmente ci serve per fare i test necessari. Quello che dobbiamo fare, in sintesi, è trasformare la classe DAO in una Servlet in modo da eseguirla direttamente sul server ottenendo dal server in una pagina web i risultati desiderati. Procedo come segue: creo innanzitutto un'applicazione web In NetBeans, scelgo File > New Project > Web Application Fig. 3: Step 1: Java Web > Web Application Pag. 3/12 Fig. 4: Step2: Nome dell'applicazione (consiglio di aggiungere il suffisso WebApp per ricordare che si tratta questa volta di un'applicazione web) Pag. 4/12 Fig. 5: Step 3: Scelta del server web (java) Tomcat che risponderà alle richieste del client. Vi ricordo che Tomcat dispone di un JDK perchè è necessario che i file .java vengano compilati (ottenendo dei file .class) Nella schermata successiva (che qua non appare), salto la voce Framework e scelgo Finish. A questo punto tutto quello che devo fare è copiare nel nuovo progetto tutti i package del progetto StudentiJavaApp (con un banale copia-incolla dei file). Seleziono tutti i package del progetto StudentiJavaApp col CTRL e faccio copia; poi vado nel nuovo progetto StudentiWebApp e in Source Packages faccio incolla. Pag. 5/12 La situazione quindi è la seguente: Fig. 6: Ecco i package copiati (dentro ovviamente ci sono le classi che ci interessano) Ho copiato anche il package main (con all'interno la classe Main) per comodità, perchè poi userò parte del codice di questa classe, nella Servlet che devo ancora creare. Creo ora la Servlet (tasto destro del mouse sul progetto StudentiWebApp e scelgo New > Servlet). Mantenete le impostazioni della figura seguente: Pag. 6/12 Fig. 7: Creazione della Servlet DAOServlet. Conviene inserire la Servlet in un package apposito Pag. 7/12 Fig. 8: Registrazione della Servlet La Servlet va registrata (cioè le va assegnato un nome interno) e poi occorre specificare un URL con il quale richiamarla (la registrazione per fortuna è automatica: NetBeans memorizza queste informazioni nel file web.xml, contenuto nella directory WEB-INF). Premo Finish e in tal modo termina la procedura di creazione della Servlet. NetBeans crea per me uno scheletro di Servlet che è il seguente (ho solo aggiunto in alto il nome e decommentato gli output del metodo processRequest()): /* * DAOServlet.java */ package it.itiscastelli.servlet; import import import import import import java.io.IOException; java.io.PrintWriter; javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; /** * @author maurizio */ public class DAOServlet extends HttpServlet { /** * Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods. * @param request servlet request Pag. 8/12 * @param response servlet response * @throws ServletException if a servlet-specific error occurs * @throws IOException if an I/O error occurs */ protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); try { out.println("<html>"); out.println("<head>"); out.println("<title>Servlet DAOServlet</title>"); out.println("</head>"); out.println("<body>"); out.println("<h1>Servlet DAOServlet at " + request.getContextPath () + "</h1>"); out.println("</body>"); out.println("</html>"); } finally { out.close(); } } // <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code."> /** * Handles the HTTP <code>GET</code> method. * @param request servlet request * @param response servlet response * @throws ServletException if a servlet-specific error occurs * @throws IOException if an I/O error occurs */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } /** * Handles the HTTP <code>POST</code> method. * @param request servlet request * @param response servlet response * @throws ServletException if a servlet-specific error occurs * @throws IOException if an I/O error occurs */ @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } /** * Returns a short description of the servlet. * @return a String containing servlet description */ @Override public String getServletInfo() { Pag. 9/12 return "Short description"; }// </editor-fold> } Probabilmente mi verrebbe in mente di aggiungere alla Servlet il metodo init() per aprire la connessione al database e il metodo destroy() per chiudere la connessione. Questo però può causare dei problemi! I motivi sono documentati all'indirizzo: http://www.codestyle.org/java/servlets/faq-Lifecycle.shtml FAQ: Q: Should I get my database connection in the init() method? R. If you create a single database connection in the init(ServletConfig) method and use it to all handle servlet requests, you must ensure all operations are synchronized or you will get unpredictable results. Cioè potrei ottenere risultati imprevedibili perchè dovrei assicurarmi che tutte le richieste dei client siano tra loro sincronizzate (ogni richiesta proveniente da un client è un thread distinto). Per agire in maniera corretta, occorrerebbe usare un pool di connessioni (i pool di connessioni sono un argomento complesso per cui rimandiamo lo studio di questo argomento). Quindi apriamo e chiudiamo la connessione nel momento in cui arriva la richiesta (cioè lo faremo nel metodo processRequest()). Vi ricordo che il solo attributo che devo usare è l'oggetto conn di tipo Connection. Poi copio all'interno della Servlet tutti gli altri metodi previsti in origine dalla classe DAO. Abbiamo quasi terminato. Ora nel metodo processRequest() aggiungo, copiandolo in parte dal metodo main() della classe Main, il codice che risolve il mio problema (per esempio, voglio l'elenco degli studenti di 5N). out.println("Elenco studenti classe 5N"); // apro la connessione col db this.apriConn(); Map<String, Studente> studenti5N = this.trovaStudentiPerClasse("5N"); for (Studente s : studenti5N.values()) out.println(s.toString()); chiudiConn(); Devo ricordarmi di togliere la parola System dal codice che ho copiato, perchè in questo contesto non ha senso. Bisogna usare invece un oggetto di tipo PrintWriter (che in questo contesto si chiama out) il quale provvede a inviare l'output nella pagina web. Formattiamo meglio il codice precedente facendo in modo di avere un elenco puntato. out.println("Elenco studenti classe 5N"); this.apriConn(); Map<String, Studente> studenti5N = this.trovaStudentiPerClasse("5N"); out.println("<ul>"); for (Studente s : studenti5N.values()) out.println("<li>"+s.toString()+"</li>"); out.println("</ul>"); this.chiudiConn(); Se l'applicativo non parte, potrebbe voler dire che avete dimenticato di aggiungere i driver del database MySQL o che non avete lanciato il database server MySQL oppure che avete lanciato il file sbagliato (index.jsp): occorre lanciare invece la Servlet (tasto destro del mouse sulla Pag. 10/12 Servlet > Run). Fig. 9: Risultato dell'esecuzione della Servet DAOServlet L'URL (in questa figura poco visibile) è http://localhost:8084/StudentiWebApp/DAOServlet Per vostra comodità, ho allegato al post sul mio sito, in un unico file zip, entrambi gli applicativi (StudentiJavaApp e StudentiWebApp), oltre al file di generazione del database (studenti.sql). Un altro modo altrettando efficace consiste nell'immergere il codice Java direttamente nel codice HTML della pagina web evidenziandolo con dei marcatori (<% e %>). Ciò ci consente inoltre di selezionare da una casella a scorrimento una classe scolastica e di avere in output l'elenco degli studenti relativi alla classe scelta. Riportiamo di seguito il codice commentato della pagina web index.jsp e ricordiamo che Tomcat provvederà a generare in automatico il sorgente di una Servlet (di solito si chiama index_jsp.java), provvederà a compilarla e a generare il file .class che andrà in esecuzione sul server. Al codice della classe DAO è stato aggiunto un nuovo metodo, il metodo trovaClassi() che restituisce la lista di tutte le classi scolastiche. Pag. 11/12 File index.jsp <%-Document : index Created on : 4-mar-2010, 17.55.36 Author : maurizio --%> <%@page <%@page <%@page <%@page contentType="text/html" pageEncoding="UTF-8"%> import="it.itiscastelli.dao.DAO" %> import="it.itiscastelli.classi.*" %> import="java.util.*" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>JSP Page</title> </head> <body> <h1>Hello World!</h1> <% // istanzio un oggetto di tipo DAO DAO dao = new DAO(); // apro la connessione al database dao.apriConn(); // carico le classi e successivamente creo una combo box // con i dati delle classi List<ClasseScol> classi = dao.trovaClassi(); %> <form name="frmProva" action="${request.requestURI}" method="get"> <p> <select name="mnuClassi"> <% for (ClasseScol c : classi) { %> <!-- caricamento delle classi nella comboBox mnuClassi --> <option><%=c.getSigla() %></option> <% } // fine for %> </select> <input type="submit" name="btnInvia" value="Invia"/> </p> </form> <% // se l'utente ha premuto il tasto Invia if (request.getParameter("btnInvia")!=null) { // lettura della sigla della classe String siglaC = request.getParameter("mnuClassi"); out.println("Elenco studenti "+siglaC); Pag. 12/12 // il metodo restituisce una mappa degli studenti appartenenti alla classe // selezionata precedentemente Map<String, Studente> studentiPerClasse = dao.trovaStudentiPerClasse(siglaC); // genero una lista non ordinata out.println("<ul>"); for (Studente s : studentiPerClasse.values()) out.println("<li>"+s.toString()+"</li>"); out.println("</ul>"); } // chiudo la connessione col db dao.chiudiConn(); %> </body> </html> Fig 1: Risultato dell'esecuzione dell'applicativo StudentiWebApp2