MokaByte 48 - Gennaio 2001
Foto dell'autore non disponibile
di
Antonio
Cisternino
Inviare oggetti Java 
via HTTP 
La comunicazione applet servlet
Nella puntata precedente abbiamo visto come serializzare un oggetto di un applet Java attraverso la rete utilizzando il protocollo HTTP in modo da attraversare un Firewall. In questo articolo prenderemo in considerazione possibili applicazioni della tecnica presentata nell'ipotesi in cui sul server sia disponibile il supporto per le servlet. 
Innanzitutto cerchiamo di capire come sia possibile realizzare un servlet in grado di ricevere l'oggetto da un applet. Successivamente vedremo come un servlet possa inviare ad un applet un oggetto. Infine discuteremo di possibili applicazioni di questa tecnica. 

Scrivere un servlet che riceve un oggetto
Per chi non sia familiare con la tecnologia Servlet facciamo un piccolo riepilogo. Un servlet HTTP è semplicemente una classe che estende la classe HttpServlet, parte del package javax.servlet.http. 
Per gestire le varie azioni del protocollo HTTP è sufficiente ridefinire il metodo doXXX, dove XXX indica l'azione del protocollo. Ad esempio per gestire le richieste di tipo GET basta ridefinire il metodo doGet. Lo stesso si può dire del metodo doPost. Questi metodi hanno due parametri: un oggetto di tipo HttpServletRequest e uno di tipo HttpServletResponse il cui ruolo dovrebbe essere chiaro dal nome. 
In sostanza un servlet HTTP analizza la richiesta utilizzando i metodi dell'oggetto HttpServletRequest e prepara ed invia la risposta utilizzando l'oggetto HttpServletResponse. 

Cerchiamo ora di capire come scrivere un servlet che riceva un oggetto serializzato da un applet e lo stampa utilizzando System.out. Il codice del Servlet è il seguente: 

import java.io.ObjectInputStream;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletRequest;
 

public class ObjectServlet extends HttpServlet {
  public void doPost(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException {
    ObjectInputStream in = new ObjectInputStream(request.getInputStream());
    try {
      Object o = in.readObject();
      System.out.println(o);
    } catch(ClassNotFoundException e) {}
  }
} // ObjectServlet
 

Come si può osservare si estende la classe HttpServlet e si ridefinisce il metodo doPost. Quando si riceve la richiesta di POST, secondo quanto visto nella puntata precedente, si recupera no i dati inviati e si ricostruisce l'oggetto. 
Nel metodo doPost si ottiene un oggetto di tipo InputStream sul quale costruiamo un ObjectInputStream necessario a deserializzare l'oggetto ricevuto con la richiesta. 

Osserviamo come l'input stream non contiene le informazioni dell'header della richiesta. Queste informazioni sono state infatti già lette dal Servlet Engine che li ha memorizzati nell'oggetto di tipo HttpServletRequest. 

La riscostruzione dell'oggetto si risolve quindi semplicemente in una chiamata al metodo readObject. Una volta ricostruito l'oggetto semplicemente viene stampato utilizzando System.out. 

L'applet che invia l'oggetto sarà simile al seguente: 

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.io.IOException;
import java.net.Socket;
import java.net.InetAddress;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.Button;
 
 

public class ObjectApplet extends java.applet.Applet implements ActionListener {
  public void send(InetAddress addr, int p, 
                   String path, Serializable obj) throws IOException {
    // Save the object's instance
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    ObjectOutputStream out = new ObjectOutputStream(buffer);
    out.writeObject(obj);

    // Prepare the header
    byte[] serobj = buffer.toByteArray();

    StringBuffer outb = new StringBuffer();
    outb.append("POST ");
    outb.append(path);
    outb.append(" HTTP/1.0\r\nUser-Agent: JavaObjectTunnel\r\n");
    outb.append("Content-Type: application/java-object\r\n");
    outb.append("Content-Length: ");
    outb.append(serobj.length);
    outb.append("\r\n\r\n");

    // Open the connection and send the data
    Socket s = new Socket(addr, p);
    OutputStream sout = s.getOutputStream();
    sout.write(outb.toString().getBytes());
    sout.write(serobj);
    sout.write("\r\n".getBytes());
    s.close();
  }
 
 

  public void init() {
    Button b = new Button("Invia");
    b.addActionListener(this);
    add(b);
  }
 
 

  public void actionPerformed(ActionEvent evt) {
    try {
      String s = "Hello Server";
      send(InetAddress.getByName(getCodeBase().getHost()), 
       8080, "/objser/servlet/ObjectServlet", s);
    } catch(IOException e) {
    }
  }
} // ObjectApplet
 

Il metodo send è quello discusso nella puntata precedente. L'applet crea un semplice bottone che quando viene premuto invia l'oggetto serializzato, in questo caso una stringa, al server. 
Nel caso si voglia inviare una risposta per l'avvenuta ricezione all'applet sarà necessario ottenere all'interno del metodo doPost un oggetto di tipo OutputStream, impostare il tipo MIME della risposta come application/java-object e aggiungere un oggetto serializzato in modo che sia inviato all'applet. Da parte sua l'applet dovrà leggere il risultato dell'operazione. Questo può essere un utile esercizio per verificare se si è realmente capito il meccanismo. 
 
 
 

Inviare un oggetto ad un applet
L'invio di un oggetto ad un applet avviene in modo assolutamente analogo alla comunicazione dall'applet al server. L'unica differenza sostanziale è che è sempre l'applet a dover iniziare la comunicazione con una POST. 
Il significato della richiesta sarà: so che mi deve essere inviato un oggetto. E la risposta consisterà nell'istanza dell'oggetto. Possiamo quindi intuire la struttura del metodo doPost in questo caso: 

  ...
  public void doPost(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException {
    ObjectInputStream in = new ObjectInputStream(request.getInputStream());
    try {
      ObjectRequest r = (ObjectRequest) in.readObject();

      // Trova l'oggetto o in qualche modo
      Serializable o = findObject(r);

      // Imposta il MIME type
      response.setContentType("application/java-object");

      // Invia l'oggetto
      ByteArrayOutputStream outb = new ByteArrayOutputStream();
      ObjectOutputStream out = new ObjectOutputStream(outb);
      out.writeObject(o);
      byte[] serobj = outb.toByteArray();
      response.setContentLength(serobj.length);
      OutputStream outd = response.getOutputStream();
      outd.write(serobj);
    } catch(ClassNotFoundException e) {}
  }
  ...
 

Facile immaginare anche il codice necessario all'applet per leggere i dati: 

  ...
  Object receiveObject(Socket s) throws IOException, ClassNotFoundException {
    InputStream in = s.getInputStream();
    LineNumberReader lin = new LineNumberReader(new InputStreamReader(in));
    Hashtable params = new Hashtable();
    String line = lin.readLine();
    int idx;

    // Legge l'header fino alla linea vuota
    while ((idx = line.indexOf(":")) != -1) {
      params.put(line.substring(0, idx), line.substring(idx + 1).trim());
      line = lin.readLine();
    }

    ObjectInputStream oin = new ObjectInputStream(in);
    o = oin.readObject();
    return o;
  }
  ...
 

Osseviamo come sia sempre presente il riferimento all'eccezione ClassNotFoundException. Questo è dovuto al fatto che il processo di deserializzazione di un oggetto richiede che sia presente la definizione della classe (il file .class). Ciò significa che sia l'applet che il servlet dovranno accedere alla definizione delle classi che vengono trasferite. 
Possibili applicazioni
La tecnica presentata in questi due articoli può essere utilizzata in numerose applicazioni. È infatti molto comodo poter inviare da un ambiente di esecuzione Java ad un altro l'istanza di una classe. 
Un'applicazione naturale può essere quella di rendere persistente lo stato di un applet Java sul server: quando l'applet sarà caricato nuovamente accderà al server e si farà inviare lo stato in forma di oggetto che sarà pronto per essere utilizzato per tornare al punto in cui era stato chiuso. 
Un'altra applicazione decisamente interessante è quella dell'invocazione di metodi remoti attraverso HTTP. In questo caso infatti basta inviare un oggetto al servlet che racchiuda gli oggetti che sono i parametri della chiamata di metodo. La risposta sarà inviata come risultato. Attraverso un opportuno bridge si può in questo modo attraversare un firewall e utilizzare tecnologie come RMI: si realizzano dei semplici wrapper per i metodi remoti che utilizzano HTTP per il tratto che deve attraversare il firewall. 
 
 
 

Conclusioni
In questo articolo abbiamo visto come realizzare un servlet che riceva da un applet un oggetto Java serializzato attraverso una connessione HTTP. Abbiamo anche discusso delle possibili applicazioni della tecnica presentata. 
In una possibile terza puntata di questa mini serie di articoli potrebbe essere interessante realizzare un bridge generico per RMI in modo da poter fruire della piattaforma distribuita di Java anche quando i firewall impediscono la comunicazione a protocolli differenti da HTTP. 

Gli esempi descritti in questo articolo (funzionanti con Tomcat) possono essere scaricati qui

Vai alla Home Page di MokaByte
Vai alla prima pagina di questo mese


MokaByte®  è un marchio registrato da MokaByte s.r.l.
Java® è un marchio registrato da Sun Microsystems; tutti i diritti riservati
E' vietata la riproduzione anche parziale
Per comunicazioni inviare una mail a
mokainfo@mokabyte.it