Laboratorio di sistemi Fortune Java/Jsp Ricordiamo che in Ubuntu il comando “fortune” impartito da shell produce una frase umoristica, un anedotto o una frase bizzarra ecc. Si può installare e localizzare nella lingua italiana mediante il comando apt-get. Proviamo anche noi a scrivere due differenti versioni dello stesso applicativo, ovviamente usando il linguaggio di programmazione java; il primo sarà dotato di una GUI (interfaccia utente grafica) mentre l'altro consisterà in una web application e manderà il proprio output all'interno di un browser come IE o Firefox; lasciamo a voi il compito di scrivere la versione da riga di comando. Come prima cosa, supponiamo che le frasi (e i relativi autori) siano archiviati in un file testuale (fortune.txt). Frase e autore sono separati dal carattere | come nell'esempio. -"In realta', agente, il mio nome e' Bond. James Bond". -"Gia', e io sono Superman. E lei e' sempre in arresto".|Roger Moore, da "007 Bersaglio mobile" Così facendo possiamo poi aggiungere a piacimento le frasi più simpatiche ed eliminare quelle col tempo ritenute meno interessanti. Facciamo in modo che l'utente abbia l'opportunità di scegliere a caso una delle frasi precaricate nel database. fortune.pdf Pag. 1/13 Cozzetto © Laboratorio di sistemi Fortune Java/Jsp Classi di supporto Progettiamo due classi Frase e ListaFrasi: package fortunejavaapp; import java.util.Scanner; /** * * @author maurizio */ public class Frase { String contenuto; String autore; Frase() { contenuto = "Tanto va la gatta al lardo che ci lascia lo zampino"; autore = "Anonimo"; } Frase(String contenuto, String autore) { this.contenuto = contenuto; this.autore = autore; } Frase leggiFrase() { Scanner sc = new Scanner(System.in); String content = sc.next(); String author = sc.next(); return new Frase(content, author); } @Override public String toString() { return contenuto+" \n-- "+autore; } void stampaFrase() { System.out.println(toString()); } } // fine classe Frase fortune.pdf Pag. 2/13 Cozzetto © Laboratorio di sistemi Fortune Java/Jsp /* * ListaFrasi.java * */ package fortunejavaapp; import import import import import import java.io.BufferedReader; java.io.FileNotFoundException; java.io.FileReader; java.io.IOException; java.util.Random; java.util.StringTokenizer; /** * @author maurizio */ public class ListaFrasi { public Frase[] frasi; public int numFrasi = 0; /** Numero massimo di frasi che è possibile caricare in memoria */ public static final int MAX_FRASI = 100; /** Costruttore nullo */ public ListaFrasi() { frasi = new Frase[MAX_FRASI]; } /** Costruttore con parametro il numero di frasi */ public ListaFrasi(int n) { if (n>=1 && n<=MAX_FRASI) frasi = new Frase[n]; else frasi = new Frase[MAX_FRASI]; } /** Genero una frase casuale tra quelle caricate in memoria */ public Frase getFraseACaso() { Random generatore = new Random(); int i = generatore.nextInt(numFrasi); return frasi[i]; } /** Aggiungo una nuova frase */ public void aggiungiFrase(Frase f) { if (numFrasi<MAX_FRASI && f!=null) { frasi[numFrasi++]=f; } } fortune.pdf Pag. 3/13 Cozzetto © Laboratorio di sistemi Fortune Java/Jsp /** Prelevo dal disco le frasi e le porto in RAM */ public int caricaFrasi(String file) throws FileNotFoundException, IOException { int numRecord=0; try { // apertura del file in lettura FileReader fr = new FileReader(file); BufferedReader bfr = new BufferedReader(fr); // lettura di tutti i record e caricamento nel vettore String s = bfr.readLine(); while (s!=null) { StringTokenizer st = new StringTokenizer(s,"|"); Frase frase = new Frase(); frase.contenuto = st.nextToken(); frase.autore = st.nextToken(); aggiungiFrase(frase); numRecord++; s = bfr.readLine(); } // fine while // chiusura bfr.close(); fr.close(); } catch(FileNotFoundException e) { throw new FileNotFoundException(e.getMessage()); } catch (IOException e) { throw new IOException(e.getMessage()); } return numRecord; } // fine metodo caricaFrasi() } // fine classe ListaFrasi fortune.pdf Pag. 4/13 Cozzetto © Laboratorio di sistemi Fortune Java/Jsp Versione GUI Nel caso di una GUI, la frase appare in una textarea e l'utente sceglie la frase successiva cliccando sul pulsante “Random” come in figura. Per comodità, ipotizziamo che tutto il nostro database sia caricato interamente in memoria. Il file fortune.txt si trova nel package risorse. /* * Fortune.java */ package fortunejavaapp; import import import import import import import java.awt.FlowLayout; java.io.FileNotFoundException; java.io.IOException; javax.swing.JButton; javax.swing.JFrame; javax.swing.JOptionPane; javax.swing.JTextArea; /** * * @author maurizio */ public class Fortune extends JFrame { ListaFrasi lf = new ListaFrasi(); JTextArea frase; JButton random; /** Costruttore */ public Fortune(String title) { this.setTitle(title); int numRecord = 0; try { numRecord=lf.caricaFrasi(System.getProperty("user.dir") +"/build/classes/risorse/fortune.txt"); } catch (FileNotFoundException e) { fortune.pdf Pag. 5/13 Cozzetto © Laboratorio di sistemi Fortune Java/Jsp JOptionPane.showMessageDialog(null, "Il file non è stato trovato: "+e.getMessage()); System.exit(1); } catch (IOException e) { JOptionPane.showMessageDialog(null, "Vi è un errore di I/O: "+e.getMessage()); System.exit(1); } JOptionPane.showMessageDialog(null, "Sono stati caricati "+numRecord+" record dal database fortune.txt"); inizializzaComponenti(); // istruzione necessaria altrimenti la finestra continua ad essere // residente in memoria this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); this.pack(); this.setVisible(true); } // fine Costruttore /** predispone il layout grafico e istanzia l'oggetto delegato a gestire l'evento click sul pulsante */ void inizializzaComponenti() { random = new JButton("Random"); frase = new JTextArea(" L'amore e' come una clessidra: \nquando si riempie il cuore, si svuota il cervello.\n\n--Anonimo"); /** Impostiamo il layout FlowLayout */ this.setLayout(new FlowLayout()); /** Aggiungo al frame il testo */ this.add(frase); /** Aggiungo al frame il bottone */ this.add(random); /** Creo un ascoltatore per il bottone */ GestorePulsante gp = new GestorePulsante(frase, lf, this); /** Registro il gestore presso il bottone */ random.addActionListener(gp); // si può anche scrivere più brevemente // random.addActionListener(new GestorePulsante(frase, lf, this)); } // fine inizializzaComponenti } // fine classe Fortune fortune.pdf Pag. 6/13 Cozzetto © Laboratorio di sistemi Fortune Java/Jsp /* * GestorePulsante.java * */ package fortunejavaapp; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JTextArea; /** * * @author maurizio */ class GestorePulsante implements ActionListener { JTextArea frase; ListaFrasi lf; JFrame frame; /** Costruttore con 3 parametri */ public GestorePulsante(JTextArea frase, ListaFrasi lf, JFrame frame) { this.frase = frase; this.lf = lf; this.frame = frame; } /** L'interfaccia ActionListener dispone di un solo metodo da implementare actionPerformed appunto */ public void actionPerformed(ActionEvent evt) { /* se l'evento è stato generato da un pulsante */ if (evt.getSource() instanceof JButton) { frase.setLineWrap(true); /* Genero una frase a caso tra quelle disponibili in RAM */ Frase f = lf.getFraseACaso(); if (f!=null) frase.setText(f.contenuto+"\n\n-- "+f.autore); frame.pack(); } } // fine metodo gestore dell'evento } // fine classe GestorePulsante fortune.pdf Pag. 7/13 Cozzetto © Laboratorio di sistemi Fortune Java/Jsp /* * Main.java */ package fortunejavaapp; import javax.swing.WindowConstants; /** * @author maurizio */ public class Main { public static void main(String[] args) { Fortune f = new Fortune("Versione grafica di 'Fortune'"); } // fine metodo main } // fine classe Main Versione Web Nel caso di una versione web, le cose cambiano leggermente. Intanto il caricamento delle frasi deve avvenire la prima volta che l'utente si collega alla pagina. Per il messaggio, usiamo un po' di Javascript e formattiamo la frase usando i fogli di stile. Il codice delle due classi non cambia. Ipotizziamo inoltre che il file fortune.txt sia contenuto nel package risorsePkg. Dal momento che il caricamento delle frasi in memoria deve avvenire una sola volta, fortune.pdf Pag. 8/13 Cozzetto © Laboratorio di sistemi Fortune Java/Jsp ricorriamo ad una variabile di sessione. Se non ha nessun valore, vuol dire che è la prima volta che carichiamo la pagina per cui va fatto il caricamento delle frasi in Ram, altrimenti vuol dire che la fase di caricamento è già stata effettuata per cui il sistema genera una frase a caso. Per generare altre frasi è sufficiente cliccare sul pulsante Aggiorna del browser. Pagina index.jsp <%@page contentType="text/html" pageEncoding="UTF-8"%> <%@page errorPage="gestioneErrori.jsp" %> <!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>Fortune - Versione web</title> <style> body { font-family: Verdana, "Trebuchet MS"; font-size: 11pt; } #box { width: 400px; border: 1px blue dashed; padding: 5px; margin-left: auto; margin-right: auto; } fortune.pdf Pag. 9/13 Cozzetto © Laboratorio di sistemi Fortune Java/Jsp .centro { text-align: center; } </style> </head> <body> <h3 class="centro">Versione web di Fortune</h3> <div id="box"> <% /* è la prima volta che carico la pagina */ if (session.getAttribute("lf")==null) { /* istanzio l'oggetto lf che rappresenta le frasi */ fortunePkg.ListaFrasi2 lf = new fortunePkg.ListaFrasi2(); /* carico tutte le frasi in memoria */ int numRecord=lf.caricaFrasi(application.getRealPath("/")+"WEBINF/classes/risorsePkg/fortune.txt"); /* ne prendo una a caso */ fortunePkg.Frase f = lf.getFraseACaso(); /* la visualizzo nel browser */ if (f!=null) { out.println(f.contenuto+"<br/><br/>-- "+f.autore); } /* imposto la variabile di sessione lf */ session.setAttribute("lf", lf); String s = "<script>alert('Sono state caricate "+numRecord+ " frasi');</script>"; out.println(s); } else { /* non è la prima volta che accedo alla pagina */ fortunePkg.ListaFrasi2 lf = (fortunePkg.ListaFrasi2) session.getAttribute("lf"); /* prendo una frase a caso */ fortunePkg.Frase f = lf.getFraseACaso(); /* la visualizzo nel browser */ if (f!=null) { out.println(f.contenuto+"<br/><br/>-- "+f.autore); } } // fine if %> </div> </body> </html> fortune.pdf Pag. 10/13 Cozzetto © Laboratorio di sistemi Fortune Java/Jsp Pagina gestioneErrori.jsp <%@page contentType="text/html"%> <%@page pageEncoding="UTF-8"%> <%@page isErrorPage="true" %> <%@page import="java.io.PrintWriter" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <% PrintWriter writer=new PrintWriter(out); %> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Pagina di errore</title> <style type="text/css"> <!-body,td,th { font-family: Trebuchet MS; font-size: 10pt; } --> </style> </head> <body> <h3>Gestione degli errori</h3> <p>Si è verificato il seguente errore: <%=exception.getMessage()%></p> </body> </html> Nella versione web, abbiamo usato in realtà al posto della solita classe ListaFrasi (che avremmo potuto ugualmente usare senza alcuna modifica) la classe ListaFrasi2 che ottimizza lo spazio in RAM allocando lo spazio strettamente necessario (nella classe facciamo uso degli ArrayList e dei Generics). La logica nel complesso tuttavia non cambia. /* * ListaFrasi2.java * */ package fortunePkg; import import import import import import import java.io.BufferedReader; java.io.FileNotFoundException; java.io.FileReader; java.io.IOException; java.util.ArrayList; java.util.Random; java.util.StringTokenizer; fortune.pdf Pag. 11/13 Cozzetto © Laboratorio di sistemi Fortune Java/Jsp /** * @author maurizio */ public class ListaFrasi2 { ArrayList<Frase> frasi; int numFrasi =0; /** Costruttore senza parametri */ public ListaFrasi2() { frasi = new ArrayList<Frase>(); } /** Costruttore con parametro il numero di frasi */ public ListaFrasi2(int n) { if ( n >= 1 ) frasi = new ArrayList<Frase>(n); else frasi = new ArrayList<Frase>(); } /** Genero una frase casuale tra quelle caricate in memoria */ public Frase getFraseACaso() { Random generatore = new Random(); int i = generatore.nextInt(numFrasi); return frasi.get(i); // non è necessario il cast visto che // usiamo i generics } /** Aggiungo una nuova frase */ public void aggiungiFrase(Frase f) { if ( f != null ) { frasi.add(f); numFrasi++; } } /** Prelevo dal disco le frasi e le porto in RAM */ public int caricaFrasi(String file) throws FileNotFoundException, IOException { int numRecord=0; try { // apertura del file in lettura FileReader fr = new FileReader(file); BufferedReader bfr = new BufferedReader(fr); // lettura di tutti i record e caricamento nell'arraylist String s = bfr.readLine(); while (s != null) { fortune.pdf Pag. 12/13 Cozzetto © Laboratorio di sistemi Fortune Java/Jsp StringTokenizer st = new StringTokenizer(s,"|"); Frase frase = new Frase(); frase.contenuto = st.nextToken(); frase.autore = st.nextToken(); aggiungiFrase(frase); numRecord++; s = bfr.readLine(); } // fine while // chiusura bfr.close(); fr.close(); } catch(FileNotFoundException e) { throw new FileNotFoundException(e.getMessage()); } catch (IOException e) { throw new IOException(e.getMessage()); } return numRecord; } // fine metodo caricaFrasi() } // fine classe ListaFrasi2 fortune.pdf Pag. 13/13 Cozzetto ©