MokaByte Numero  42  - Giugno 2000
 
JDBC e Web
III parte
di 
Mauro Molino
Come devo strutturare la mia applicazione se voglio 
interfacciare una base di dati con il web? 
In particolare cosa devo fare per permettere ad una applet di leggere i dati contenuti in tale db?

Eccoci arrivata all’ultimo articolo dedicato all’interrogazione di database via web. Nel primo articolo abbiamo posto le basi teoriche per implementare un’architettura di comunicazione client-server, nel secondo abbiamo implementato la struttura di base per il server. In quest’ultimo articolo vedremo invece come creare un applet di interrogazione, cioè il client. Le informazioni fornite nei tre articoli dovrebbero essere sufficienti per poter estendere le caratteristiche a seconda delle proprie necessità. In realtà, come già accennato, la serie di articoli è stata concepita in modo da mostrare una metodologia aperta, nel nostro caso applicata ad un sistema di interrogazione di database, ma virtualmente adattabile ad un qualsiasi sistema client server.

L’applet

Il nostro semplicissimo applet sarà composto da un campo di testo per l’inserimento dei comandi da inviare al server, un bottone per confermare l’invio del comando e una textArea per la visualizzazione dei dati di ritorno. Ci siamo quindi limitati al minimo indispensabile per avere un applet utilizzabile, semplice ma allo stesso tempo implementando il tutto in modo che sia estremamente facile estenderne le funzionalità a proprio piacimento/necessità.

Per rendere la lettura dell’articolo il più scorrevole possibile, commenteremo il codice pezzo per pezzo. Come vedrete il tutto è molto semplice e comunque risulta perfettamente speculare al codice scritto per la parte server. Infatti tutta la parte di comunicazione è esattamente la stessa. In realtà, l’unica differenza sta nel fatto che il client ha necessità di visualizzazione che il server può invece trascurare.
Passiamo al codice : 

10 import java.applet.Applet;
20 import java.awt.*;
30 import java.awt.event.*;
40 import java.net.*;
50 import java.io.*;

e fin qui tutto chiaro; Applet è il tipo di applicativo che scriviamo, awt.* ed awt.event .* servono per la gestione dell’interfaccia grafica e le sue interazioni, net.*  per la comunicazione con il server e io.* per l’input/output.
A questo punto creiamo la nostra classe, che oltre ad essere un applet implementa le interfacce ActionListener per poter rispondere agli eventi generati dall’interfaccia grafica e Runnable per poter essere trattata come thread.
Fatto questo definiamo variabili ed oggetti che ci serviranno nell’applet.
 

60 public class JDBCClient extends Applet implements ActionListener,Runnable {
70  TextField textField;
80  TextArea textArea;
90  Button bottone4;
100  String newline;
110  Color bgcolor = new Color(0xc2d3f1);
120  private Thread selfThread = null;
130  String hostname;
140  Socket theSocket;
150  BufferedReader in;
160  PrintWriter out;
170  int port = 3000;
180  boolean oktorun = true;
 

Implementiamo il metodo init(), proprio dell’oggetto Applet, che è il metodo richiamato all’inizializzazione dell’applet stesso. Per funzionare, il nostro applet ha necessità di sapere l’indirizzo del server al quale connettersi (riga 200, anche se sappiamo che per ragioni di sicurezza, per default un applet può solo connettersi al server dal quale sia stato scaricato). 
Il resto del codice del metodo init() si occupa di costruire l’interfaccia grafica. Ne abbiamo approfittato per inserire del codice didattico ai fini della costruzione di una interfaccia grafica (per quanto spartana), utilizzando il GridBagLayout e i GridBagContraints. Di tutti I layout, il GridBag è forse il più complesso e potente. Per chi non sia abituato a lavorare con i layout, all’inizio può sembrare più una complicazione che una potenzialità, ma quando si prova ad  allargare o restringere la finestra di lavoro e si vede l’auto resizing in azione, ci si ricrede immediatamente.
 

190  public void init() {
200   hostname = getParameter("HOST");
210   textField = new TextField(20);
220   textArea = new TextArea(5, 20);
230   bottone4 = new Button("Invia testo");
240   textArea.setEditable(false);
250   Font fnt = new Font("Arial",Font.PLAIN,12);
260   textArea.setFont(fnt);
270   textField.setFont(fnt);
280   GridBagLayout grid = new GridBagLayout();
290   GridBagConstraints c = new GridBagConstraints();
300   setLayout(grid);
310   c.fill = GridBagConstraints.BOTH;
320   c.weightx = 1.0;
330   c.weighty = 1;
340   c.gridwidth = 1;
350   add(textField,c);
360   c.gridwidth = GridBagConstraints.REMAINDER;
370   add(bottone4,c);
380   bottone4.addActionListener(this);
390   c.weightx = 1.0;
400   c.gridwidth = GridBagConstraints.REMAINDER;
410   c.weighty = 15.0;
420   textArea.setBackground(bgcolor);
430   add(textArea,c);
440   textField.addActionListener(this);
450   newline = System.getProperty("line.separator");
460  }
 

Il metodo start(), così come stop() e run(), appartengono invece all’interfacca Runnable, e sono quindi legati all’oggetto Thread. In particolare, start() nel nostro caso si occupa di creare il socket di connessione al server ( riga 490), aprire gli strema- di input/output con il server stesso (righe 500-510)  e di creare e lanciare un thread dell’applet stesso.

470  public void start() {
480   try {
490    theSocket = new Socket(hostname,port);
500    in = new BufferedReader(new InputStreamReader(theSocket.getInputStream()));
510    out = new PrintWriter(theSocket.getOutputStream(),true);
520    // 
530   } catch (IOException e) {
540    System.err.println("ERRORE!" + e);
550   }
560   selfThread = new Thread(JDBCClient.this);
570   oktorun = true;
580   selfThread.start();
590  }

Il metodo stop() viene eseguito se per qualche motivo viene stoppato il thread, e si occupa di chiudere il socket.
 

600  public void stop () {
610   oktorun = false;
620   try {
630    theSocket.close();
640   } catch (IOException ee) {
650   }
660  }
 

Il metodo run() è il cuore dell’applet, in quanto si occupa di stare in ascolto sul socket creato precedentemente e di interpretare i dati che arrivano dal server. Questo è il punto in cui si nota di più la somiglianza con il codice del server; infatti la tecnica utilizzata è la stessa. Il formato di dati accettato è : “comando:dati”. Nella stringa ricevuta dal server  si cerca il separatore (“:”),ponendo nella variabile “what” il comando e in “data” i dati. Nel nostro caso è riconosciuto solo il comando “getString” che segnala la richiesta del valore di un campo dal recordset creato in base alla query fatta in precedenza ( per dettagli sui comandi riconosciuti dal server è consigliabile vedere il precedente articolo della serie).
 

670  public void run () {
680   String inputLine,what,data;
690   while (true && oktorun) {
700    try {
710     inputLine = in.readLine();
720     int p = inputLine.indexOf(":");
730     if (p > 0) {
740      what = inputLine.substring(0,p);
750      data = inputLine.substring(p+1,inputLine.length());

Avendo implementato un solo commando riconosciuto abbiamo utilizzato un “if “( riga 760). Naturalmente, avendo un ventaglio di comandi più ampio sarà invece meglio utilizzare una struttura “switch”. Nel caso del comando “getString”, l’applet si limita a mostrare sulla textArea il valore del campo richiesto.

760      if (what.equals("getString")) {
770       textArea.append(data + "\n");
780      }
790     }
800    }
810    catch (IOException e) {
820     System.err.println("Error reading input");
830    }
840   }
850  }
 

Il metodo actionPerfomed deve essere implementato in quanto nella creazione della classe si è specificata come clausola “implements ActionListener”. Questo metodo viene richiamato in seguito al verificarsi di eventi da parte di  quegli oggetti per i quali sia stato definito ActionListener l’applet (righe 380 e 440 per bottone4 e textField rispettivamente). In questo caso l’ “if” alla riga 870 è superfluo perché il metodo viene chiamato solo nel caso in cui l’evento sia lanciato da uno di questi due oggetti, ma è stata inserita ugualmente in previsione di espansioni che riguardino il lancio di eventi da parte di altri oggetti all’interno dell’applet ( anche qui, se il numero di oggetti diventa alto sarebbe meglio sostituire  l’”if” con un “switch”).   Anche qui, è bene rileggere l’articolo precedente per vedere quali comandi siano riconosciuti dal server , oppure implementarli ...............
 

860  public void actionPerformed(ActionEvent e) {
870   if (e.getSource().equals(bottone4) || e.getSource().equals(textField)) {
880    String text = textField.getText();
890    out.println(text + "\n");
900    out.flush();
910   }
920  }
930 }
 
 

Conclusioni
Dalla lettura di questo articolo e di quelli precedenti dovrebbe essere possibile ricavare una visione abbastanza chiara di uno dei metodi più semplici per la scrittura di applicativi client-server via applet. I mezzi utilizzati sono quelli standard di Java 1.1, quindi non ci dovrebbero essere problemi di compatibilità con I vari browser in circolazione.
Prendendo come spunto il codice mostrato, è possibile ampliare a dismisura le capacità del progetto. Tanto per citare qualcosa, sarebbe possibile implementare : meccanismi di connection pooling per l’ottimizzazione delle connessioni a database,passaggio di recordset (interi o paginati) da server a client invece che un campo alla volta (occhio in questo caso alla mole di dati che si va a trasferire sulla rete), interfaccia di gestione del database ( usando i metadata), etc.
 

Chi volesse mettersi in contatto con la redazione può farlo scrivendo a mokainfo@mokabyte.it