Lezione 8 - Creazione di middleware in Java per l'accesso a database


Le prime 6 applicazioni in questa lezione sono state prese dai materiali dei seminari tenuti al Cern da Raúl Ramos-Pollán.Allo stesso indirizzo al Cern e' disponibile una copia zippata dello stesso materiale, mentre la S accanto al titolo di ogni applicazione punta a una copia locale dello stesso materiale.
La descrizione di un'applicazione completa realizzata con RMI puo essere trovata in questa tesi su Analisi e sviluppo di un'applicazione Java client-server per l'accesso a un database ad oggetti distribuito di Stefano Comarin col software disponibile a partire da questa pagina.
Altra documentazione interessante : RMI tutorial , JAXP tutorial e XML Sun tutorials
Col termine di middleware (software di mezzo) si intende il software che rende accessibile sul Web risorse hardware o soft che prima erano disponibili solo localmente o su reti non Internet. Un esempio tipico di middleware e' il software che rende accessibile dal Web un database. Si tratta di un sistema a tre strati (in gergo three-tiered), dove Java realizza l'interfaccia che sta nello strato di mezzo (percio' middleware) tra il server del database e l'utente finale col browser.
In effetti una definizione piu' tecnica di middleware e' indipendente dai database e consiste nel considerare come tale, tutto il software indipendente da una particolare applicazione, che utilizza i servizi base di rete come il TCP-IP. Insomma nella gerarchia a strati che definisce le reti, il middleware si situa nello strato intermedio tra quello di TCP-IP e quello delle applicazioni. Da questo punto di vista piu' generale vanno considerati come middleware: emulatori di terminali, WWW, Corba,rpc, etc
Java puo' comunicare con un data base attraverso 3 diversi meccanismi:
  1. Un meccanismo detto JDBC(Java Database Connectivity):un pacchetto applicativo(API) per collegarsi a database relazionali SQL.
  2. Attraverso API fornite dagli stessi fornitori di database per accedere a database ad oggetti: e' il caso dell'API Java di Objectivity.In questo caso e' lo stesso database che fornisce in maniera automatica la persistenza di alcuni oggetti definiti in maniera speciale.
  3. Attraverso l'XML .
Le API fornite col database non permettono di realizzare middleware ma solo di accedere al database localmente (o attraverso la rete se il database lo permette). (Ma se il database e' accessibile da un qualsiasi nodo Internet allora un programma JDBC puo' essere inserito in un'applet e permettere l'accesso dello stesso dal Web:questo tipo di accesso non usa il server Web percio' e' un accesso diretto.Non e' consigliabile per database da usare pesantemente in quanto ogni applet apre una nuova connessione al database.)

Il middleware vero e proprio viene invece realizzato attraverso la tecnica RMI che vedremo prima indipendentemente dal problema del database, e poi applicata per "pubblicare" sul Web un database ad oggetti. La stessa tecnica puo' essere usata per interfacciare al Web un database relazionale, ma qui non sara' considerata, in quanto al momento attuale esistono numerose soluzioni alternative a Java e tutte piu' semplici da realizzare.
L'uso del formato XML per interfacciarsi al Database e' venuto fuori solo da poco ed e' facilitato in Java da JAXP (Java API for XML Processing ) che e' ormai, dalla versione 1.4, parte integrante di Java.

Interfacciamento a data base relazionali SQL con JDBC

Ci sono vari tipi o modelli di database ma il piu' comune e' il database relazionale che si presenta appunto come una serie di tabelle e di relazioni tra le stesse.

In effetti nel gergo relazionale il termine relazione non indica la "relazione" tra due tabelle ma la stessa tabella. Si intende che e' sempre possibile rappresentare tutto il database con un'unica enorme tabella e le "relazioni" nel senso di collegamenti sono rappresentati dalle colonne. Quando dividiamo questa tabella in tabelle piu' piccole questi collegamenti sono rappresentati da colonne nelle due tabelle che hanno uguali valori. Questo processo si indica col nome di "normalizzazione" ed e' una delle cose piu' difficili da fare nel disegno di database relazionali.
Inoltre nel gergo dei database relazionali le righe di una tabella sono record e le colonne sono campi(fields in inglese). Ogni campo e' individuato da un nome ed ogni record da uno o piu' campi particolari che fanno da identificatore o chiave primaria dello stesso. I DBMS piu' diffusi come MySQL , Oracle e Access sono relazionali.

Per i data base relazionali o RDBMS e' stato creato un linguaggio standard di creazione e interrogazione di database che permette la comunicazione con RDBMS . Il linguaggio si chiama SQL o Structured Query Language ed e' capito dalla maggioranza dei database .Esso prevede otto tipi di dati:

Tipo di datiDichiarazioni
Numeri INTEGER,DECIMAL(m,n),REAL...
Stringhe di caratteriCHARACTER(N),TEXT
Stringhe di bit
Data
Ora
Timestamp
Durata

Un programma che acceda a un database usando SQL dovrebbe poter essere usato con database di diverso tipo,basta che capiscano l'SQL. Esistono al momento attuale almeno 3 interfacce standard per database SQL: Microsoft ODBC, Java JDBC e Perl DBI. In particolare Java contiene un pacchetto applicativo che permette di scrivere programmi di accesso a database relazionali. Perche' questo avvenga il costruttore di un particolare database deve fornire un insieme di classi detto JDBC driver che contiene le caratteristiche specifiche del database.

Per questo, se volete accedere a un database MySQL, dovete:

In questi esempi useremo MySQL perche' esso e' disponibile nella distribuzione di Linux e puo' essere usato con successo per database di alcuni Giga.

Nell'applicazione che segue useremo la tabella Persone. Ecco le istruzioni su come caricare la stessa tabella in MySQL.


Applicazione 1:Accesso a tabella MySQL da applicazione e da applet. S

    file Persone.java

/*
 * This sample shows how to list all the names from the Persone table
 */

// You need to import the java.sql package to use JDBC
import java.sql.*;

class Persone
{
 public static void main (String args [])
 {
  try {
    // Load the Mysql JDBC driver
    try {
      Class.forName("org.gjt.mm.mysql.Driver");
    } catch (ClassNotFoundException e) {
      System.out.println ("Mysql device driver does not exist");
      System.exit(1);
    }

    // Connect to the database
    // You can put a database name after the @ sign in the connection URL.
    Connection conn =
    DriverManager.getConnection ("jdbc:mysql://localhost/Persone?user=Persone&password=");

    // Create a Statement
    Statement stmt = conn.createStatement ();

    // Select the ENAME column from the EMP table
    ResultSet rset = stmt.executeQuery (" select ID, Cognome, Nome, Tel , Email,Homepage  from Persone");

    // Iterate through the result and print the employee names
    while (rset.next ())  {
      System.out.println   (rset.getString (1)+" "+rset.getString(2)+" "+
rset.getString(3)+" "+rset.getString(4)+" "+rset.getString(5));
    }
     

    // Close the RseultSet
    rset.close();

    // Close the Statement
    stmt.close();

    // Close the connection
    conn.close();   
   } catch (SQLException e) {
      System.out.println("Error accessing DB ");
      System.out.println("  Error code is : "+e.getErrorCode());
      System.out.println("  Error message is :"+e.getMessage());
   }
  }
}

    file PersoneApplet.java


/*
 * This sample shows how to list all the names from the Persone table
 */

// You need to import the java.sql package to use JDBC
import java.sql.*;
import java.awt.*;
import java.applet.*;

public class PersoneApplet extends Applet {

TextArea ta = new TextArea("In attesa di connessione...",30,70);

public void init()
 {
setLayout(new BorderLayout());
    add("Center",ta);

  try {
    // Load the Mysql JDBC driver
    try {
      Class.forName("org.gjt.mm.mysql.Driver");
    } catch (ClassNotFoundException e) {
      System.out.println ("Mysql device driver does not exist");
      System.exit(1);
    }

    // Connect to the database
    // You can put a database name after the @ sign in the connection URL.
    Connection conn =
    DriverManager.getConnection ("jdbc:mysql://alboot.ba.infn.it/Persone?user=Persone&password=");

    // Create a Statement
    Statement stmt = conn.createStatement ();

    // Select the ENAME column from the EMP table
    ResultSet rset = stmt.executeQuery (" select ID, Cognome, Nome, Tel , Email,Homepage  from Persone");

    StringBuffer buf = new StringBuffer();

    // Iterate through the result and print the employee names
    while (rset.next ())  {
      buf.append   (rset.getString (1)+" "+rset.getString(2)+" "+
rset.getString(3)+" "+rset.getString(4)+" "+rset.getString(5)+"\n");
    }
    ta.setText(buf.toString()); 

    // Close the RseultSet
    rset.close();

    // Close the Statement
    stmt.close();

    // Close the connection
    conn.close();   
   } catch (SQLException e) {
      System.out.println("Error accessing DB ");
      System.out.println("  Error code is : "+e.getErrorCode());
      System.out.println("  Error message is :"+e.getMessage());
   }
  }
}
Prova l'applet qui.
Applicazione 2:Crea una nuova tabella e inserisci dei dati . S

    file Insert.java

import java.sql.*;

class Insert
{
  public static void main (String args [])
       throws SQLException
  {
    // Load the Mysql JDBC driver
    try {
      Class.forName("org.gjt.mm.mysql.Driver");
    } catch (ClassNotFoundException e) {
      System.out.println ("Mysql device driver does not exist");
      System.exit(0);
    }

    // Connect to the database
    // You can put a database name after the @ sign in the connection URL.
    Connection conn =
    DriverManager.getConnection ("jdbc:mysql://localhost/Persone?user=Persone&password=");

    // Create a Statement
    Statement stmt = conn.createStatement ();
    stmt.executeUpdate ("drop table COFFEES");
    String createString;
    createString = "create table COFFEES " +
                   "(COF_NAME varchar(32), " +
                   "SUP_ID int, " +
                   "PRICE float, " +
                   "SALES int, " +
                   "TOTAL int)";


    stmt.executeUpdate (createString);
    System.out.println("Table COFFEES created");

    int r=0;
    r+=stmt.executeUpdate ("insert into COFFEES values ('Colombian', 101, 7.99, 0, 0)");
    r+=stmt.executeUpdate ("insert into COFFEES values ('Espresso',  150, 9.99, 0, 0)");
    System.out.println("A total of "+r+" rows were inserted");

    // Close the Statement
    stmt.close();

    // Close the connection
    conn.close();
  }
}



Applicazione 3:Aggiorna la tabella dell'Applicazione 2 . S

    file Update.java


// You need to import the java.sql package to use JDBC
import java.sql.*;

class Update
{
  public static void main (String args [])
       throws SQLException
  {
// Load the Mysql JDBC driver
    try {
      Class.forName("org.gjt.mm.mysql.Driver");
    } catch (ClassNotFoundException e) {
      System.out.println ("Mysql device driver does not exist");
      System.exit(0);
    }

    // Connect to the database
    // You can put a database name after the @ sign in the connection URL.
    Connection conn =
    DriverManager.getConnection ("jdbc:mysql://localhost/Persone?user=Persone&password=");

    // Create a Statement
    Statement stmt = conn.createStatement ();

    String s = "update COFFEES set PRICE=15.99 where COF_NAME='Espresso'";
    int r = stmt.executeUpdate (s);

    r+=stmt.executeUpdate ("delete from COFFEES where price < 8.");

    System.out.println(r+" rows where updated/modified");

    // Close the Statement
    stmt.close();

    // Close the connection
    conn.close();   
  }
}
Sebbene l'API Java permette di fare cose ancora piu' raffinate, non entreremo nei dettagli, avendo mostrato come sia facile comunicare da Java con un db relazionale.

I programmi JDBC possono girare negli applet se il database permette l'accesso remoto (un applet puo' comunicare solo con servizi Internet del server dal quale proviene e "jdbc" viene considerato un servizio Internet). Quindi Jdbc in alcuni casi si presta a realizzare middleware. Questo e' vero per MySQL se l'amministratore abilita l'accesso remoto al database. In caso contrario dovete richiamare il programma come un CGI script, o meglio ancora come un servlet in un server opportunamente abilitato a eseguire programmi Java (ad es. Apache). La soluzione che vedremo fra poco (RMI) e' invece sempre applicabile in quanto "rmi" e' un servizio Internet e puo' essere usato assieme a JDBC per rendere un data base relazionale accessibile da un'applet.

Comunicazione su Internet con richiamo di metodi remoti (RMI)

Java puo' comunicare su Internet attraverso 2 diversi meccanismi:
  1. Un meccanismo a basso livello detto in gergo socket:una sorta di collegamento attraverso il quale due programmi si scambiano dati.
  2. Un meccanismo ad alto livello che permette a un programma Java di richiamare i metodi di un oggetto che si trova in un'altro programma che gira su un computer remoto.
Qui vedremo solo la comunicazione via richiamo di metodi remoti.

In particolare questa puo' avvenire in due modi:

  1. RMI (Remote Object Invocation) un protocollo che puo' essere usato solo per la comunicazione tra programmi Java.
  2. CORBA(Common Object Request Broker Architecture) un protocollo che puo' essere usato per la comunicazione di programmi scritti in un qualsiasi linguaggio.
Questo tipo di comunicazione tra computer permette anche lo scambio degli oggetti indicati come valori di ritorno o parametri nella chiamata di un metodo. La comunicazione tra computer si basa su una serie di protocolli disposti a strati. Considerando solo i protocolli usati da Java per la comunicazione via Internet possiamo limitarci ai seguenti:

LivelloClientServer
programmaprogramma che usa l'oggetto remotoprogramma che rende disponibili gli oggetti remoti
richiamo locale del metodoattraverso Object Stubattraverso Object Skeleton
Protocollo di scambio oggetti:CORBA o RMIScambio di oggetti
Protocolli di rete:http,TCP,UDP,...Connessione attraverso socket
Livello hardware

Ogni protocollo di livello superiore ha bisogno dei livelli inferiori per funzionare.
Ogni oggetto remoto ha un suo indirizzo standard (URL) che gli permette di rendersi accessibile attraverso un servizio detto RMI Registry(simile se vogliamo al DNS). Per questo il computer che ospita gli oggetti remoti deve far girare 3 programmi:

La comunicazione vera e propria avviene tra lo Stub dalla parte del client e lo Skeleton dalla parte del server. Questi agiscono come rappresentanti locali dell'oggetto remoto(broker) gestendo tutta la comunicazione. Il client agisce come se chiamasse un metodo di un oggetto locale ed e' lo Stub, che sa dove si trova l'oggetto remoto, a preoccuparsi di preparare i valori da inviare come argomenti serializzando gli oggetti(questa operazione viene detta in gergo marshalling). In Corba il meccanismo seguito e' lo stesso, solo che gli oggetti remoti, potendo essere in un linguaggio qualsiasi,sono descritti da un linguaggio standard l'IDL (Interface Definition Language).

Anche il computer che gira il client ,in certi casi, potrebbe aver bisogno di un server Web.
Per capire l'uso del server Web in queste applicazioni, bisogna tener conto del fatto che una macchina virtuale Java ha la possibilita' di rendere disponibili le classi ad essa accessibili localmente a macchine virtuali remote, tramite un server Web. Questo si fa impostando la proprieta' codebase. In questo modo,ad esempio,il client puo' ottenere lo stub dal server. In alternativa, lo stub e altre classi del server che servono al client,possono essere copiate a mano all'inizio,una volta per tutte.

Per riuscire a capire che tipo di servizio viene offerto da un server di oggetti, consideriamo il problema concreto di realizzare un display di eventi delle alte energie come questo interamente in Java. Non e' difficile costruire la parte grafica di questo programma, ma ad un certo punto ci troveremo col problema di interfacciare questo programma con la sorgente degli eventi. Non si tratta solo di leggere un file, ma di usare numerosi sottoprogrammi scritti in Fortran e C che e' impensabile riscrivere in Java e che ci permettono di passare dai dati "bruti" alle coordinate degli oggetti da rappresentare.

La soluzione? Collegare ai tasti di selezione eventi l'indirizzo di un CGI script, un programma in Fortran che provvede ad accedere ai dati e a processarli ritornando le coordinate in un documento di testo HTML.
Funziona? Si, ma se piu' utenti richiedono nello stesso momento eventi e' il disastro in quanto il sistema non e' in grado di gestire piu' richieste contemporanee.

La soluzione ottimale e' nello scrivere "un server di eventi" che faccia da interfaccia tra l'applet e la sorgente degli eventi. Questo server potrebbe essere un semplice server Web modificato (vedi l'Applicazione 6 della lezione 7) oppure possiamo sviluppare una soluzione ancora piu' sofisticata attraverso RMI. Cio' che ora vedremo...

La messa a punto di un collegamento RMI e' una cosa complessa che richiede numerosi passi:

  1. Creazione dell'interfaccia remota:Scrivi e compila il codice Java per le interfacce.Si tratta di interfacce Java che devono estendere Remote e che definiscono tutti i metodi remoti.Nell'esempio che segue l'interfaccia si chiama Compute.
  2. Implementazione dell'interfaccia negli oggetti remoti:Scrivi e compila la o le classi che implementano l'interfaccia vista a 1.Nell'esempio:ComputeEngine Questa classe deve estendere UnicastRemoteObject.
  3. Stubs e Skeletons:Per generarli dai l'istruzione rmic ComputeEngine
  4. Server:scrivi e compila un server che fornira' il servizio. In questo programma viene dato un nome al servizio e vengono istanziati gli oggetti remoti (definiti dalla classe ComputeEngine) in uno o piu' copie.
  5. Fai partire il servizio facendo girare il server e rmiregistry.
  6. Sul computer che ospita il client scrivi e compila il programma cliente che richiede l'indirizzo dell'oggetto remoto usando il suo nome del tipo: rmi://remotehost/nomeservizio.Quindi usa l'indirizzo dell'oggetto per richiamare i suoi metodi remoti.Se sul computer che ospita il server, non e' disponibile un server Web, bisogna ricordarsi di copiare a mano tutte le classi che servono.
Da notare che il client deve avere accesso ai seguenti pezzi di codice remoti: Inoltre il client puo' a sua volta trasformarsi in server in modo da permettere al server remoto di comunicare per riferire eventuali problemi.
Bisogna fare attenzione a distinguere tra oggetti remoti e oggetti locali.Gli oggetti remoti hanno stubs e skeleton definiti e sono trasferiti solo per riferimento.Invece gli oggetti locali, se presenti come parametri o valore ritornato in una invocazione a metodo remoto, vengono trasferiti per valore. Perche' questo sia possibile devono essere serializzabili, cioe' implementare l'interfaccia Serializable.Questo significa anche che la loro modifica agisce solo sulla copia locale.


Applicazione 4:Client e Server Java RMI . S

    file Compute.java

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Compute extends Remote {
    int executeCalculation (int i) throws RemoteException;
    String tellmeWhoYouAre (String name) throws RemoteException;
}

    file ComputeEngine.java

import java.rmi.*; 
import java.rmi.server.*;

public class ComputeEngine extends UnicastRemoteObject
                       implements Compute 
{
     private String id;
     private int count=0;

     public ComputeEngine(String _id) throws RemoteException {
        super();
        id = _id;
     }
     public int executeCalculation(int i) {
        System.out.println("Calculation performed with: "+i);
        count=count+i;
        return count;
     }

     public String tellmeWhoYouAre(String name) {
        String s = new String("Hello, "+name+" I'm Calculator "+id);
        System.out.println("Greeting "+name);
        return s;
     }
}

    file Server.java

import java.rmi.*; 
import java.rmi.server.*;

public class Server
{

     public static void main(String[] args) { 
        if (System.getSecurityManager() == null) {
             System.setSecurityManager(new RMISecurityManager());                        
        }
        try {
             Compute engine1 = new ComputeEngine("32");
             Naming.rebind("Compute32", engine1);
             Compute engine2 = new ComputeEngine("33");
             Naming.rebind("Compute33", engine2);
             System.out.println("ComputeEngine bound");
        } catch (Exception e) {
             System.err.println("ComputeEngine exception: " +  e.getMessage());
             e.printStackTrace();
        }
      }
}

    file Client.java

import java.rmi.*;
 
public class Client {
 
    public static void main (String args[]) {
       if (System.getSecurityManager()==null) {
          System.setSecurityManager(new RMISecurityManager());
       }
       try {
          String name = "//"+args[0]+"/"+args[1];
          Compute comp = (Compute) Naming.lookup(name);
          int i=comp.executeCalculation(1);
          System.out.println("Calculation result is "+i);
          String s = comp.tellmeWhoYouAre("the Client");
          System.out.println("Greeiting is "+s);
       } catch (Exception e) {
          System.err.println("Exception in RMI: "+e.getMessage());
          e.printStackTrace();
       }
    }
}
 
Se non si fa uso del server Web sul computer dove gira il Server allora Stub e ComputeEngine vanno copiati a mano (ad es. con ftp) sul computer e la cartella del client. Allora basta far partire client e server coi comandi:
java Server
java Client alboot.ba.infn.it Compute32
se si fa uso del server Web allora l'istruzione per far partire il server e' piu' complicata:
java -Djava.security.policy=java.policy -Djava.rmi.server.hostname=alboot.ba.infn.it -Djava.rmi.server.codebase="http://www.ba.infn.it/~zito/jsem/java/sem8/step1/server/" Server 
e il file java.policy deve contenere:
grant {
   permission java.net.SocketPermission "*:1024-65535", "connect,accept";
   permission java.net.SocketPermission "*:80", "connect";
};

In definitiva ecco i passi da compiere per far girare questo esempio su alboot:

  1. Vai sulla cartella server
  2. Dai il comando rmiregistry &
  3. Dai il comando startserver &
  4. Vai sulla cartella client
  5. Dai il comando java Client alboot.ba.infn.it Compute32
  6. Ricordati di eliminare dopo la prova i processi creati da 2 e 3.



Applicazione 5:Client e Server Java RMI con invio di oggetto da server a client. S

    file Compute.java


import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Compute extends Remote {
    int executeCalculation (int i) throws RemoteException;
    String tellmeWhoYouAre (String name) throws RemoteException;
    Bakelite getBakelite (int id) throws RemoteException;
}

    file Bakelite.java

 import java.io.*;
public class Bakelite implements Serializable{
  String code;
  int id;
  double dati[] = new double[30];
 Bakelite(int id){
  code = "codicexx";
  this.id = id;
  for(int i=0;i<30;i++){dati[i]=Math.random()*10000.;}
  }

}

    file ComputeEngine.java

import java.rmi.*; 
import java.rmi.server.*;

public class ComputeEngine extends UnicastRemoteObject
                       implements Compute 
{
     private String id;
     private int count=0;

     public ComputeEngine(String _id) throws RemoteException {
        super();
        id = _id;
     }
     public int executeCalculation(int i) {
        System.out.println("Calculation performed with: "+i);
        count=count+i;
        return count;
     }

     public String tellmeWhoYouAre(String name) {
        String s = new String("Hello, "+name+" I'm Calculator "+id);
        System.out.println("Greeting "+name);
        return s;
     }
     public Bakelite getBakelite(int id) {
        Bakelite bak = new Bakelite(id);
        System.out.println("sending "+bak);
        return bak;
     }
}

    file Server.java

import java.rmi.*; 
import java.rmi.server.*;

public class Server
{

     public static void main(String[] args) { 
        if (System.getSecurityManager() == null) {
             System.setSecurityManager(new RMISecurityManager());                        
        }
        try {
             Compute engine1 = new ComputeEngine("32");
             Naming.rebind("Compute32", engine1);
             Compute engine2 = new ComputeEngine("33");
             Naming.rebind("Compute33", engine2);
             System.out.println("ComputeEngine bound");
        } catch (Exception e) {
             System.err.println("ComputeEngine exception: " +  e.getMessage());
             e.printStackTrace();
        }
      }
}

    file Client1.java

import java.rmi.*;
 
public class Client1 {
 
    public static void main (String args[]) {
       if (System.getSecurityManager()==null) {
          System.setSecurityManager(new RMISecurityManager());
       }
       try {
          String name = "//"+args[0]+"/"+args[1];
          Compute comp = (Compute) Naming.lookup(name);
          int i=comp.executeCalculation(1);
          System.out.println("Calculation result is "+i);
          String s = comp.tellmeWhoYouAre("the Client");
          System.out.println("Greeiting is "+s);
          Bakelite b = comp.getBakelite(20);
          System.out.println("Bakelite is "+b.id+" "+b.code+" "+b.dati[0]);
 
       } catch (Exception e) {
          System.err.println("Exception in RMI: "+e.getMessage());
          e.printStackTrace();
       }
    }



Applet6:Come il precedente ma ora usa un applet come client . S

    file ClientApplet.java

import java.rmi.*;
import java.awt.*;
import java.applet.*;
public class ClientApplet extends Applet {
     Bakelite b;
 public void init(){
 if (System.getSecurityManager()==null) {
          System.setSecurityManager(new RMISecurityManager());
       }
       try {
          String name = "//alboot.ba.infn.it/Compute32";
          Compute comp = (Compute) Naming.lookup(name);
          int i=comp.executeCalculation(1);
          System.out.println("Calculation result is "+i);
          String s = comp.tellmeWhoYouAre("the Client");
          System.out.println("Greeiting is "+s);
          b = comp.getBakelite(20);
          System.out.println("Bakelite is "+b.id+" "+b.code+" "+b.dati[0]);
 
       } catch (Exception e) {
          System.err.println("Exception in RMI: "+e.getMessage());
          e.printStackTrace();
       }
}
 public void paint(Graphics g){
   g.drawString("Bakelite"+b.id,5,25);
  }
 }
Prova l'applet qui. In definitiva, un server RMI, a differenza dello script CGI che ha la funzionalita' di un singolo programma, puo' fornire :

Uso di RMI per interfacciare un database ad oggetti

Il server RMI in questo caso risiede sullo stesso computer dove si trova il database ad oggetti e fornisce gli oggetti del database al client remoto realizzato con un'applet.
Il database ad oggetti e' realizzato da Objectivity/DB che e' in grado di gestire oggetti persistenti con i seguenti tipi di attributi.

TipoDescrizione
int8, int16, int32, int64 Interi
float32, float64Floating Point
ooVString Stringa di caratteri ASCII di lunghezza variabile
ooUTF8String Stringa di caratteri Unicode di lunghezza variabile
ooVArray(type) Array di dimensione variabile
ooRef(class) Riferimento a oggetto
ooVArray(ooRef(class))Array di riferimenti

Objectivity/DB si occupa di memorizzare i soli attributi di una classe lasciando all'applicazione il compito di definirne il comportamento con opportuni metodi.Inoltre e' possibile definire associazioni tra oggetti unidirezionali e bidirezionali, uno a uno , uno a molti e molti a molti. Si tiene conto delle associazioni nella memorizzazione degli oggetti e nelle operazioni di cancellazione e locking.

L'unita' di trasferimento tra il database e l'applicazione non e' l'oggetto ma la pagina di oggetti, per cui Objectivity/DB e' un database di tipo page server e non object server.
Gli oggetti sono quindi raggruppati in contenitori(pagina di oggetti), in database (singoli files), in federated database o federazioni(insieme di files+catalogo+ schema dei dati+boot file).Il singolo oggetto in un federated database ha un identificatore unico detto OID che serve per rintracciarlo.Una federazione Objectivity puo' essere distribuita su piu' computer collegati via TCP/IP. Per questo la gestione di database Objectivity prevede oltre a un server locale anche un server remoto(AMS nel gergo Objectivity) che provvede a comunicare con la parte di federazione memorizzata su altri computer.Questa possibilita' di distribuire il database rende Objectivity capace di gestire database molto piu' grandi di quelli relazionali che sono limitati dal fatto di essere costretti a girare su un solo computer.

L'api Java di Objectivity contiene i seguenti oggetti principali:

Ogni oggetto che estende la classe ooObj e' persistente. L'interazione tra applicazione e database avviene durante una sessione tra il session.begin() e il session.commit().Solo allora un oggetto persistente e' davvero persistente : in ogni caso esso deve essere esplicitamente letto dal db col metodo fetch() e scritto con markModified ogni volta che e' necessario. L'interazione col database avviene per transazioni in modo da preservare l'integrita' dei dati. Un programma detto lock server provvede a rinforzare i lock richiesti durante le transazioni. Esso di solito gira su un computer apposito e diverso da quello del database.
Le transazioni, che esistono anche per i database relazionali, sono un'importante caratteristica dei database in quanto ,assieme al locking, permettono l'uso di un database da parte di piu' utenti senza che le modifiche di un utente interferiscano con quelle degli altri.
Una transazione indica una serie di operazioni che devono essere eseguite insieme.
Se tutte le operazioni hanno successo, allora viene dato il commit e il data base e' aggiornato, altrimenti avviene il roll back e tutto torna a come era prima della transazione. (Immaginate un trasferimento di denaro in banca...). MySQL non ha le transazioni ma permette il lock di una tabella durante operazioni delicate.

Per usare RMI come livello di mezzo tra l'utente che lavora con l'applet su un qualsiasi computer collegato a Internet e il database Objectivity/DB avremo bisogno che sulla macchina server girino contemporaneamente:

Per far girare l'applicazione presentata dovremo percio' procedere coi passi seguenti:
  1. Fai partire il Web Server sulla macchina server se non e' in funzione.
  2. Controlla che il lock server sia in funzione e se non lo e', fallo partire : comandi oocheckls lockcms e ools
  3. Cancella eventualmente il database creato nel test precedente con oodeletefd RPC
  4. Fai andare lo script che crea il database createRPC.sh.Questo script invoca i comandi di Objectivity per creare il database.
  5. Popola il database invocando il programma Java con lo script fillRPC.sh.
  6. A questo punto il database dovrebbe essere visibile sul server con il comando di Objectivity/DB ootoolmgr
  7. Fai partire RMIRegistry
  8. Fai partire il server RMI
  9. Fai partire l'applet sul computer client
Gli oggetti persistenti sono resi remoti in modo che i loro metodi possano essere usati, ma non viaggiano in rete. L'applet puo' comunque riferirsi a un qualsiasi oggetto persistente utilizzando il suo OID che come abbiamo visto e' univoco. Inoltre esso puo' ricavare i valori dei vari attributi attraverso i metodi dell'oggetto.Essenzialmente, quando l'applet richiede gli oggetti di un contenitore, riceve un vettore di OID che puo' usare per ricavare ulteriori informazioni.Questo modo di procedere e' necessario perche' come abbiamo visto prima, la persistenza e' assicurata solo nel server e durante una transazione: se noi facessimo viaggiare gli oggetti persistenti, nulla ci assicura che al loro arrivo gli attributi non siano stati cambiati nel frattempo.

Nell'esempio che mostriamo abbiamo un unico contenitore l'oggetto Wheel che contiene 10000 oggetti Bakelite.


Applicazione 7:Crea un Data Base Objectivity S

    file FillDetector.java

                                                          
import com.objy.db.*;
import com.objy.db.app.*;
import java.util.Date;
import java.util.Enumeration;
import java.util.Properties;
import java.io.*;

public class FillDetector {
    Connection conn;
    Session session;
    ooFDObj fd;
    ooDBObj db;

    public static void main (String args[]) {
        (new FillDetector()).go(args);
    }

    void go(String[] args) {
        try {
            conn = Connection.open ("RPC", oo.openReadWrite);
            System.out.println ("Connection to RPC-database opened");
            session  = new Session();
            session.begin();
            conn.getSchemaPolicy().setChangeClassAllowed(false);
            conn.getSchemaPolicy().setFieldAccessControlEnforced(false);
            conn.setSchemaClassName("Bakelite","Bakelite");
            conn.getSchemaPolicy().setFieldAccessControlEnforced(false);
            
            checkDB("RPCdata");
            checkCont();
            if (checkBakelite())
                System.out.println("Bakelite plates objects already exist");
            else {
                System.out.print("Adding Bakelite plates objects ...");
                addBakelite();
                System.out.println(" done");
                session.checkpoint();
            }


            session.commit();
            session.terminate();
            conn.close();
        } catch (DatabaseNotFoundException exception) {
            System.out.println("Federated database \"Detector\" not found.");
        } catch (Exception e) {
            System.out.println("Exception in FillDetector: " + e.getMessage());
            e.printStackTrace();
        }
    }
  
    void checkDB(String db_name) {
        /* check if the CMS database exists, otherwise create it */
        fd = session.getFD();
        if (fd.hasDB(db_name)) {
            System.out.println("Database " + db_name + " exists");
            db = fd.lookupDB(db_name);
        } else {
            db = fd.newDB(db_name);
            System.out.println("Database " + db_name + " created");
        }
    }

    void checkCont() {
        /* check if the Wheel<-2..2> containers exist, otherwise create them */
        for (int i=2; i<3; i++) {
            String contName = "Wheel" + (i-2 <= 0 ?
                                         String.valueOf(i-2) :
                                         "+" + String.valueOf(i-2));
            if (db.hasContainer(contName)) {
                System.out.println(contName + " container exists");
            } else {
                Wheel cont = new Wheel();
                db.addContainer(cont, contName, 0, 500, 20);
                System.out.println(contName + " container created");
            }
        }
    }

    boolean checkBakelite() {
        /* check if there are some bakelite plates around */
        Iterator itr = db.scan ("Bakelite");
        return itr.hasMoreElements();
    }

    void addBakelite() {
        /* add some random chambers to the Detector database */
        for (int i=2; i<3; i++) {
            String contName = "Wheel" + (i-2 <= 0 ?
                                         String.valueOf(i-2) :
                                         "+" + String.valueOf(i-2));
            Wheel cont = (Wheel)db.lookupContainer(contName);
        /*    for (int j=0; j<4; j++) {
                String mb_type = "MB" + String.valueOf(j%4+1); */
                for (int k = 0; k < 10000; k++) {
                    Bakelite c = new Bakelite (k,"Codicexx",Math.random()*10000,
                                                Math.random()*10000,
                                                Math.random()*10000);
                    cont.cluster(c);
                }
           }
    }
 }

    file IObject.java

                                                          
package remote;public interface IObject extends java.rmi.Remote {    
    Record getAttrs() throws java.rmi.RemoteException;    
    void setAttrs(Record r) throws java.rmi.RemoteException;    
    String asString() throws java.rmi.RemoteException;    
    java.util.Vector getPathInfo() throws java.rmi.RemoteException;}

    file Record.java

                                                          
public interface Record extends java.io.Serializable {}

    file Bakelite.java

                                                          
import com.objy.db.app.*;
import java.util.Date;
import java.util.Vector;
import java.util.Enumeration;

public class Bakelite extends ooObj implements IObject {
    
        int id; 
        String codice; 
        double dato1;
        double dato2; 
        double dato3; 

    public Bakelite() {}

    public Bakelite (int id, String codice, double dato1, double dato2, 
                                        double dato3 ) {
        this.id = id;
        this.codice = codice;
        this.dato1 = dato1;
        this.dato2 = dato2;
        this.dato3 = dato3; }

    /* local methods */

    public void setcodice(String codice) {
        markModified();
        this.codice = codice;
    }
   
    public String getcodice() {
        fetch();
        return codice;
    }

    public void setid(int id) {
        markModified();
        this.id = id;
    }

    public int getid() {
        fetch();
        return id;
    }

    public void setdato1(double dato1) {
        markModified();
        this.dato1 = dato1;
    }

    public void setdato2(double dato2) {
        markModified();
        this.dato2 = dato2;
    }

    public void setdato3(double dato3) {
        markModified();
        this.dato3 = dato3;
    }

   public double getdato1() {
        fetch();
        return dato1;
   }

   public double getdato2() {
        fetch();
        return dato2;
   }

   public double getdato3() {
        fetch();
        return dato3;
   }


    public String toString() {
        fetch();
        return "Bakelite " + this.id;
    }                                           

    /* remote methods */

    public String asString() { return toString(); }

 
      public Vector getPathInfo() {
        Wheel cont = (Wheel) getContainer();
        String contName = cont.getName();
        char[] wheelChars = contName.toCharArray();
        int wheel = wheelChars[contName.length()-1] - 48;
        if (wheelChars[contName.length()-2] == '-')
            wheel = -wheel;
        String bak_type = getcodice();
        int type = bak_type.toCharArray()[bak_type.length()-1] - 48;
        int bakelite = getid();
        Vector result = new Vector(3);
        result.addElement (new Integer(wheel + 2));
        result.addElement (new Integer(type - 1));
        result.addElement (new Integer(bakelite - 1));
        return result;
    }

    public Record getAttrs() {
        return new BakeliteData(this);
    }

    public void setAttrs(Record r) {
        if (r instanceof BakeliteData) {
            BakeliteData data = (BakeliteData) r;
            setid(data.id);
            setcodice(data.codice);
            setdato1(data.dato1);
            setdato2(data.dato2);
            setdato3(data.dato3);


         }
    }
}

    file Wheel.java

                                                          
import com.objy.db.app.ooContObj;

public class Wheel extends ooContObj {
  public String toString() {
    fetch();
    return this.getName();
  }
}

    file BakeliteData.java

                                                          

/* import db.*; */

public class BakeliteData implements Record {
    
        public int id; 
        public String codice; 
        public double dato1;
        public double dato2; 
        public double dato3; 

    public BakeliteData (int id, String codice, double dato1, double dato2, 
                                        double dato3 ) {
        this.id = id;
        this.codice = codice;
        this.dato1 = dato1;
        this.dato2 = dato2;
        this.dato3 = dato3;  }

    public BakeliteData(Bakelite bak) {
        id = bak.getid();
        codice = bak.getcodice();
        dato1 = bak.getdato1();         
        dato2 = bak.getdato2();         
        dato3 = bak.getdato3();         
    }
}

Prima di far girare il programma che ha FillDetector come classe principale, occorre cancellare il database creato precedentemente e crearne uno nuovo con le istruzioni:
oodeletefd RPC
oonewfd -fdfilepath /afs/cern.ch/user/g/gzito/cms/RPC.fdb -fdnumber 3627 -pagesize 1024 -lockserverhost lockcms RPC
Se sono rimasti dei lock pendenti e' impossibile cancellare il database: bisogna prima rilasciare questi lock con oocleanup RPC. La lista dei lock si puo' ottenere con oolockmon RPC.

Gli oggetti Bakelite diventano persistenti solo dopo l'istruzione conn.cluster(c) in via temporanea. La fine della transazione con session.commit rende la loro memorizzazione definitiva.La classe Bakelite e' aggiunta in maniera automatica allo schema del database quando gli oggetti diventano persistenti. Comunque Objectivity contiene dei tools per creare esplicitamente il modello dei dati.

Da come e' stato creato il database, dovrebbe essere chiara la strategia usata per rendere gli oggetti accessibili all'applet client. Gli oggetti persistenti sono oggetti remoti e percio' implementano una serie di metodi definiti nell'interfaccia IObject che permettono di accedere/modificare le loro proprieta'.

Come si vede,questi metodi, prevedono per ogni oggetto persistente un oggetto locale che ha la stessa struttura (in questo caso BakeliteData) che viaggia tra client e server.


Applicazione 8:Interroga il Data Base Objectivity per trovare l'oggetto Bakelite con un certo valore di id S

    file Compute.java

                                                          
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.*;

public interface Compute extends Remote {
    /* java.util.Vector getLocalBakelite (int wheel, int id) throws RemoteException; */
         double getLocalBakelite (int wheel, int id) throws RemoteException;
}

    file ComputeEngine.java

                                                          
import java.rmi.*;
import java.rmi.server.*;
import java.util.*;
import java.io.*;
import java.rmi.registry.*;
import com.objy.db.*;
import com.objy.db.app.*;

public class ComputeEngine extends UnicastRemoteObject implements Compute
{
  private String id;
  private int count = 0;
  Session session;
  String db_name;
  Connection conn;
  
  public ComputeEngine(String _id) throws RemoteException {
    super();
    id = _id;
  }
  

/* returns all the local chambers, including those in the replicated databases */
 
   public double getLocalBakelite(int wheel, int id) throws RemoteException {

        double dato1 = 0.0;
        java.util.Vector result = new java.util.Vector();
        System.out.println("Proviamo a connetterci");

        try {
            conn = Connection.open("f:\\Prova\\RPC", oo.openReadWrite);
            conn.setThreadPolicy(oo.THREAD_POLICY_UNRESTRICTED);
            session = new Session();
            System.out.println("Connessione riuscita e sessione aperta");
            session.setOpenMode(oo.openReadOnly);
            session.setMrowMode(oo.MROW);
            session.begin();
            System.out.println("Dopo la connessione");
            conn.getSchemaPolicy().setChangeClassAllowed(false);
            conn.getSchemaPolicy().setFieldAccessControlEnforced(false);
            conn.setSchemaClassName("Wheel", "Wheel");
            conn.setSchemaClassName("Bakelite", "Bakelite");

        } catch (DatabaseNotFoundException exception) {
            System.err.println("Federated database \"RPC\" not found.");
            System.exit(1);
        } catch (DatabaseOpenException exception) {
            System.err.println("Federated database \"RPC\" already open.");
            System.exit(1);
        }

        ooFDObj fd = session.getFD();
        ooDBObj db = fd.lookupDB("Bari_rpc");
        System.out.println("Proviamo" +fd);
        System.out.println("Prima di lookup");
        ooContObj cont = db.lookupContainer("Wheel0");
        System.out.println("Dopo lookup " +cont);
        Iterator itr = cont.scan("Bakelite","id ==" +id);
                while (itr.hasMoreElements()) {
                        Bakelite bak = (Bakelite) itr.nextElement();
                        System.out.println(bak.getid() + " " + bak.getcodice() + "  " + bak.getdato1());
                        dato1 = bak.getdato1();
                }
        return dato1;
    }
}

    file Server.java

                                                          
import java.rmi.*;
import java.rmi.server.*;

public class Server
{

  public static void main(String[] args) {
    if (System.getSecurityManager() == null) {
      System.setSecurityManager(new RMISecurityManager());
    }
    try {
        Compute engine1 = new ComputeEngine("32");
        Naming.rebind("Compute32",engine1);
        System.out.println("ComputeEngine bound");
    } catch (Exception e) {
        System.err.println("ComputeEngine exception: " + e.getMessage());
        e.printStackTrace();
    }
  }
}

    file Client.java

                                                          
import java.rmi.*;
import java.util.*;

public class Client {

  public static void main (String[] args) {
    if (System.getSecurityManager() == null) {
      System.setSecurityManager(new RMISecurityManager());
    }
    try {
           String name = "//"+args[0]+"/"+args[1];
           Compute comp = (Compute) Naming.lookup(name);
           /* java.util.Vector result = comp.getLocalBakelite(0, 20); */
                double dato1 = comp.getLocalBakelite(0, 20);
             System.out.println("Il valore cercato e' " + dato1);
    } catch (Exception e) {
      System.err.println("Exception in RMI: "+e.getMessage());
      e.printStackTrace();
    }
  }
}          
Ecco la sequenza completa di operazioni:
  1. Fai partire il server Web su cms con java TinyHttpd 8000 &
  2. Fai partire lo rmiregistry su cms con rmiregistry &
  3. Modifica eventualmente startserver.sh se non si e' sullo stesso computer della prova precedente.
  4. Fai partire il server con sh startserver.sh &
  5. Su alboot in step9/client fai partire il client dando
    java Client cms1.cern.ch Compute32 
Il metodo scan dell'oggetto container puo' essere usato per ottenere un iteratore che ci permette di accedere a tutti gli oggetti del contenitore che verificano una condizione data. Objectivity/DB permette anche la memorizzazione di questi indici che permettono di accellerare l'accesso al database per domande molto frequenti.


Applicazione 9:Fatti inviare dal data base una copia di un'oggetto, modifica alcuni valori e rimandalo indietro per aggiornare l'oggetto nel database S

    file Compute2.java

                                                          
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.*;

public interface Compute2 extends Remote {
         void  setLocalBakelite (int wheel, int id, Record r) throws RemoteException;

         Record getLocalBakelite (int wheel, int id) throws RemoteException;

}

    file Server2.java

                                                          
import java.rmi.*;
import java.rmi.server.*;
import java.util.*;
import java.io.*;
import java.rmi.registry.*;
import com.objy.db.*;
import com.objy.db.app.*;

public class Server2 extends UnicastRemoteObject implements Compute2
{
  private String id;
  private int count = 0;
  Session session;
  String db_name;
  Connection conn;
  
  public Server2() throws RemoteException {
    super();
        System.out.println("Proviamo a connetterci");

        try {
            conn = Connection.open("RPC1", oo.openReadWrite);
            conn.setThreadPolicy(oo.THREAD_POLICY_UNRESTRICTED);
            session = new Session();
            System.out.println("Connessione riuscita e sessione aperta");
//            session.setOpenMode(oo.openReadOnly);
//            session.setMrowMode(oo.MROW);
            System.out.println("Dopo la connessione");
            conn.getSchemaPolicy().setChangeClassAllowed(false);
            conn.getSchemaPolicy().setFieldAccessControlEnforced(false);
            conn.setSchemaClassName("Wheel", "Wheel");
            conn.setSchemaClassName("Bakelite", "Bakelite");
        } catch (DatabaseNotFoundException exception) {
            System.err.println("Federated database \"RPC\" not found.");
            System.exit(1);
        } catch (DatabaseOpenException exception) {
            System.err.println("Federated database \"RPC\" already open.");
            System.exit(1);
        }
  }
  
 
 
   public Record getLocalBakelite(int wheel, int id) throws RemoteException {
        Record d = null;

        java.util.Vector result = new java.util.Vector();
            session.begin();


        ooFDObj fd = session.getFD();
        ooDBObj db = fd.lookupDB("RPCdata");
        System.out.println("Proviamo" +fd);
        System.out.println("Prima di lookup");
        ooContObj cont = db.lookupContainer("Wheel0");
        System.out.println("Dopo lookup " +cont);
        Iterator itr = cont.scan("Bakelite","id ==" +id);
                while (itr.hasMoreElements()) {
                        Bakelite bak = (Bakelite) itr.nextElement();
                        System.out.println(bak.getid() + " " + bak.getcodice() + "  " + bak.getdato1());
                        d = bak.getAttrs();
                }
               session.commit();
                return d;
    }

   public void setLocalBakelite(int wheel, int id, Record r) throws RemoteException {

        java.util.Vector result = new java.util.Vector();

        session.begin();

        ooFDObj fd = session.getFD();
        ooDBObj db = fd.lookupDB("RPCdata");
        System.out.println("Proviamo" +fd);
        System.out.println("Prima di lookup");
        ooContObj cont = db.lookupContainer("Wheel0");
        System.out.println("Dopo lookup " +cont);
        Iterator itr = cont.scan("Bakelite","id ==" +id);
                while (itr.hasMoreElements()) {
                        Bakelite bak = (Bakelite) itr.nextElement();
                        System.out.println(bak.getid() + " " + bak.getcodice() + "  " + bak.getdato1());
                        bak.setAttrs(r);
                }
               session.commit();
    }

  public static void main(String[] args) {
    if (System.getSecurityManager() == null) {
      System.setSecurityManager(new RMISecurityManager());
    }
    try {
        Server2 engine1 = new Server2();
        Naming.rebind("Compute32",engine1);
        System.out.println("Server ready");
         while(true) {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {}
            }

    } catch (Exception e) {
        System.err.println("Server error: " + e.getMessage());
        e.printStackTrace();
    }
  }
}

    file Client2.java

                                                          
import java.rmi.*;
import java.util.*;

public class Client2 {
  
  public static void main (String[] args) {
    if (System.getSecurityManager() == null) {
      System.setSecurityManager(new RMISecurityManager());
    }
    try {
           String name = "//"+args[0]+"/"+args[1];
           Compute2 comp = (Compute2) Naming.lookup(name);
           BakeliteData d =(BakeliteData) comp.getLocalBakelite(0, 20);
             System.out.println("Il valore cercato e' " + d.dato1);
           d.dato1 = 1111.11111;
           comp.setLocalBakelite(0,20,d);
    } catch (Exception e) {
      System.err.println("Exception in RMI: "+e.getMessage());
      e.printStackTrace();
    }
  }
}



Applet10: Come il precedente, ma ora rendendo la cosa possibile con un'interfaccia grafica all'interno di un'applet. S

    file ClientApplet2.java

                                                          
import java.rmi.*;
import java.awt.*;
import java.applet.*;
import java.awt.event.*;
public class ClientApplet1 extends Applet implements ActionListener{
   BakeliteData d ;
   Compute2 comp;
   TextField idt,codicet,dato1t,dato2t,dato3t;
   Button b1,b2;
 public void init(){
   add(idt = new TextField());
   add(codicet = new TextField());
   add(dato1t = new TextField());
   add(dato2t = new TextField());
   add(dato3t = new TextField());
   add(b1 = new Button("Get new object"));
   add(b2 = new Button("Change object"));
   b1.addActionListener(this);
   b2.addActionListener(this);
 if (System.getSecurityManager()==null) {
          System.setSecurityManager(new RMISecurityManager());
       }
       try {
           String name = "//cms2.cern.ch/Compute32";
           comp = (Compute2) Naming.lookup(name);
           d =(BakeliteData) comp.getLocalBakelite(0, 20);
           idt.setText(Integer.toString(d.id));
           codicet.setText(d.codice);
           dato1t.setText(Double.toString(d.dato1));
           dato2t.setText(Double.toString(d.dato2));
           dato3t.setText(Double.toString(d.dato3));
             System.out.println("Il valore cercato e' " + d.dato1);
         //  d.dato1 = 1111.11111;
         //  comp.setLocalBakelite(0,20,d);      
          System.out.println("Bakelite is "+d.id+" "+d.codice+" "+d.dato1);
 
       } catch (Exception e) {
          System.err.println("Exception in RMI: "+e.getMessage());
          e.printStackTrace();
       }
}
public void actionPerformed(ActionEvent ev){
    if (ev.getSource() == b1){
       try {
     d =(BakeliteData) comp.getLocalBakelite(0, Integer.parseInt(idt.getText()));
           idt.setText(Integer.toString(d.id));
           codicet.setText(d.codice);
           dato1t.setText(Double.toString(d.dato1));
           dato2t.setText(Double.toString(d.dato2));
           dato3t.setText(Double.toString(d.dato3));
           repaint();
       } catch (Exception e) {
          System.err.println("Exception in RMI: "+e.getMessage());
          e.printStackTrace();
       }
    }
    if (ev.getSource() == b2){
       try {
           d.id = Integer.parseInt(idt.getText());
           d.codice = codicet.getText();
           d.dato1 = Double.valueOf(dato1t.getText()).doubleValue();
           d.dato2 = Double.valueOf(dato2t.getText()).doubleValue();
           d.dato3 = Double.valueOf(dato3t.getText()).doubleValue();
           comp.setLocalBakelite(0,d.id,d);      
       } catch (Exception e1) {
          System.err.println("Exception in RMI: "+e1.getMessage());
          e1.printStackTrace();
       }
    }
    }
 }
Prova l'applet qui.

Per concludere vediamo come si estende questo schema per poter accedere a un qualsiasi oggetto di un database distribuito costituito magari da decine di federated database sparsi in tutto il momdo. Oppure,riferendoci all'esempio visto prima del display di eventi, cosa facciamo se l'evento puo' risiedere su uno qualsiasi di centinaia di computer collegati a Internet e sparsi in tutto il mondo?

Dotiamo ogni computer di un server RMI di eventi. Quando facciamo la richiesta di un particolare evento questa viene inviata a un qualsiasi server, ad esempio il piu' vicino. Questi puo' trovare l'evento e restituircelo oppure inoltrare la richiesta a un altro server. Cosi' la richiesta si propaga da un server all'altro finche' qualche server non trova l'evento.

Ora sostituite agli eventi un brano di musica mp3 oppure un qualsiasi file , ed ecco trovato il modo di condividere brani di musica o file con tutti gli altri utenti della rete.Cioe' musica gratis o quasi (dovete avere il vostro computer collegato a Internet 24/24 con una linea abbastanza veloce).

Interfacciamento a data base relazionali SQL con XML e JAXP

L'XML e' uno standard che permette di creare nuovi linguaggi di marcatura. In pratica ognuno puo' crearsi i comandi tipo HTML che piu' rispondono a descrivere un certo tipo di informazione.
Ad esempio potremmo inventare questi comandi per descrivere le schede di una biblioteca.
In gergo si dice che l'XML permette di rappresentare informazione strutturata. Esso dovrebbe servire a realizzare il sogno del creatore del Web, Tim Berners Lee, di rendere tutta l'informazione online classificabile, permettendo di rappresentare anche l'informazione ora non HTML in un formato classificabile e rendendo possibile la scrittura di documenti che si autodescrivono. Questo e' il sogno di realizzare il cosiddetto Web semantico ovvero tutta l'informazione online come un'enorme database in un'unico formato ,l'XML appunto,facilmente accessibile ai programmi di computer.


Applicazione 11: Scrivete il programma "CiaoATutti" dell'XML, cioe' un programma che non fa niente, se non indicare quando i vari comandi XML iniziano e quando finiscono, e il contenuto tra tag di apertura e chiusura. Applicatelo al file che descrive le schede di una biblioteca. S

    file Loadxml.java

                                               


import java.io.*;

import org.xml.sax.*;
import org.xml.sax.helpers.*;

import javax.xml.parsers.*;

public class Loadxml extends DefaultHandler
{
    public static void main(String argv[])
    {
        if (argv.length != 1) {
            System.err.println("Usage: cmd filename");
            System.exit(1);
        }
        
        // Use an instance of ourselves as the SAX event handler
        DefaultHandler handler = new Loadxml();
        // Use the default (non-validating) parser
        SAXParserFactory factory = SAXParserFactory.newInstance();
        try {
            // Set up output stream
            out = new OutputStreamWriter(System.out, "UTF8");

            // Parse the input
            SAXParser saxParser = factory.newSAXParser();
            saxParser.parse( new File(argv[0]), handler);

        } catch (Throwable t) {
            t.printStackTrace();
        }
        System.exit(0);
    }

    static private Writer  out;

    public void startDocument()
    throws SAXException
    {
    System.out.println("Start Document");
    }

    public void endDocument()
    throws SAXException
    {
    System.out.println("End Document");
    }

    public void startElement(String namespaceURI,
                             String lName, // local name
                             String qName, // qualified name
                             Attributes attrs)
    throws SAXException
    {
    String name = lName;
    if ("".equals(name)) name = qName;
    System.out.println("Start Element " + name);
    }

    public void endElement(String namespaceURI,
                           String sName, // simple name
                           String qName  // qualified name
                          )
    throws SAXException
    {
    String name = sName;
    if ("".equals(name)) name = qName;
    System.out.println("End Element " + name);
    }

 public void characters(char buf[], int offset, int len)
    throws SAXException
    {
        String s = new String(buf, offset, len);
        System.out.println("Contenuto comando " + s);
    }



}



Applicazione 12: Modificate l'applicazione 1 in modo da esportare i dati della tabella Persone in formato xml in un file Persone.xml. S

    file Persone.java

           


// You need to import the java.sql package to use JDBC
import java.sql.*;
import java.io.*;

class Persone
{
 public static void main (String args [])throws IOException
 {
  try {
    // Load the Mysql JDBC driver
    try {
      Class.forName("org.gjt.mm.mysql.Driver");
    } catch (ClassNotFoundException e) {
      System.out.println ("Mysql device driver does not exist");
      System.exit(1);
    }
    BufferedWriter myOutput;
    File outputFile = new File("Persone.xml");
    myOutput = new BufferedWriter(new FileWriter(outputFile)); 
    myOutput.write("\n");
    myOutput.write("\n");


    // Connect to the database
    // You can put a database name after the @ sign in the connection URL.
    Connection conn =
    DriverManager.getConnection ("jdbc:mysql://localhost/Persone?user=Persone&password=");

    // Create a Statement
    Statement stmt = conn.createStatement ();

    ResultSet rset = stmt.executeQuery (" select ID, Cognome, Nome, Tel , Email,Homepage  from Persone");

    // Iterate through the result and print the employee names
    while (rset.next ())  {
    myOutput.write("\n");
      myOutput.write (" "+rset.getString(2)+"\n" );
      myOutput.write(""+rset.getString(3)+"\n");
      if(rset.getString(4)==null) myOutput.write("\n"); else
      myOutput.write(""+rset.getString(4)+"\n");
      if(rset.getString(5)==null) myOutput.write("\n"); else
      myOutput.write(""+rset.getString(5)+"\n");
      if(rset.getString(6)==null) myOutput.write("\n"); else
         myOutput.write(""+rset.getString(6)+"\n");
    myOutput.write("\n");
    }
     

    // Close the ResultSet
    rset.close();
    myOutput.write("\n");

    // Close the Statement
    stmt.close();

    // Close the connection
    conn.close();   
    myOutput.close();


   } catch (SQLException e) {
      System.out.println("Error accessing DB ");
      System.out.println("  Error code is : "+e.getErrorCode());
      System.out.println("  Error message is :"+e.getMessage());
   }
  }
}
Ecco il risultato


Applicazione 13: Un database Oracle permette l'interrogazione sulla porta 3615 attraverso XML. Scrivete un programma che legge la query (SELECT) in XML dal file input.xml e scrive il risultato nel file output.xml S

    file Readxml.java

           

import java.io.*;
import java.net.*;

public class Readxml {
      public static void main(String[] args) throws IOException {

        Socket echoSocket = null;
        PrintWriter out = null;
        BufferedReader in = null;
        BufferedWriter myOutput;
        String inputLine;
        String line;
        BufferedReader data;


        data = new BufferedReader(new FileReader(new File("input.xml")));
        myOutput = new BufferedWriter(new FileWriter("output.xml")); 
        try {
            echoSocket = new Socket("cmstrkdb.in2p3.fr",3615);
            out = new PrintWriter(echoSocket.getOutputStream(), true);
            in = new BufferedReader(new InputStreamReader(echoSocket.getInputStream()));
        } catch (UnknownHostException e) {
            System.err.println("Don't know about host: "+args[0]);
            System.exit(1);
        } catch (IOException e) {
            System.err.println("Couldn't get I/O for the connection to: "+args[0]);
            System.exit(1);
        }


while ((line = data.readLine()) != null) {
        out.println(line);
      } 

        while ( (inputLine=in.readLine())!=null) {
              myOutput.write(inputLine+"\n");
         }

        out.close();
        in.close();
        myOutput.close();
        echoSocket.close();
    }
}
Ecco il risultato in uscita con la seguente query


Applicazione 14: Scrivete un'applicazione grafica che processa il file ricevuto dal database Oracle e fa un display di tutte le strip morte per i circa 800 sensori. S

    file Displayxml.java

           

import  java.awt.*;
import javax.swing.*;
import java.io.*;
import java.util.*; 
import org.xml.sax.*;
import org.xml.sax.helpers.*;

import javax.xml.parsers.*;

public class Displayxml extends DefaultHandler
{
    String nomecomando;
    int nr = 0;
    JPanel border=new JPanel(new BorderLayout());
    JFrame frame = new JFrame ("Bad strips display");
    Display disegno = new Display();
    JScrollPane base;
    static Displayxml lx;
    public Displayxml(){
       lx = this;
     }
    public static void main(String argv[])
    {
       
        if (argv.length != 1) {
            System.err.println("Usage: cmd filename");
            System.exit(1);
        }
        
        // Use an instance of ourselves as the SAX event handler
        DefaultHandler handler = new Displayxml();
        
        // Use the default (non-validating) parser
        SAXParserFactory factory = SAXParserFactory.newInstance();
        try {
            // Set up output stream
            out = new OutputStreamWriter(System.out, "UTF8");
            lx.frame.setContentPane(lx.border);
            lx.disegno.setPreferredSize( new Dimension(768,800));
            lx.base=new JScrollPane(lx.disegno, 
                JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,               
                JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
            lx.base.setBackground(Color.white);
            lx.border.add(lx.base, BorderLayout.CENTER);
            lx.frame.pack();
            lx.frame.show();


            // Parse the input
            SAXParser saxParser = factory.newSAXParser();
            saxParser.parse( new File(argv[0]), handler);

        } catch (Throwable t) {
            t.printStackTrace();
        }
//        System.exit(0);
    }

    static private Writer  out;

    public void startDocument()
    throws SAXException
    {
    System.out.println("Start Document");
    }

    public void endDocument()
    throws SAXException
    {
    System.out.println("Numero sensori = "+nr);
    }

    public void startElement(String namespaceURI,
                             String lName, // local name
                             String qName, // qualified name
                             Attributes attrs)
    throws SAXException
    {
    String name = lName;
    if ("".equals(name)) name = qName;
      nomecomando = name;
    if(nomecomando.equals("row"))nr++;
    }

    public void endElement(String namespaceURI,
                           String sName, // simple name
                           String qName  // qualified name
                          )
    throws SAXException
    {
    String name = sName;
    if ("".equals(name)) name = qName;
      nomecomando=" ";
    }

 public void characters(char buf[], int offset, int len)
    throws SAXException
    {
        String s = new String(buf, offset, len);
        if(nomecomando.equals("value")&& !(s.equals("null")||s.equals("NULL")))
          {
            StringTokenizer st = new StringTokenizer(s);
             while(st.hasMoreTokens()){
                lx.disegno.disegnastrip(nr,new Integer(st.nextToken()).intValue());}
    }
    }


	class Display extends JPanel{
                int xstrip,ystrip;
                public void disegnastrip(int x,int y){xstrip=x;ystrip=y;repaint();}
		public void paintComponent(Graphics g){
                g.setColor(Color.black);
                g.fillRect(xstrip,ystrip,1,1);

}
}
}



Applicazione 15:Scrivete un programma che mostra le notizie presenti in un documento in formato RSS. S

    file Displaynews.java

           

import  java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import java.util.*; 
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import javax.xml.parsers.*;

public class Displaynews extends DefaultHandler 

{
    String nomecomando;
    ArrayList titoli;
    int nr = 0;
    JPanel border=new JPanel(new BorderLayout());
    JFrame frame = new JFrame ("Bad strips display");
    Display disegno = new Display();
    JScrollPane base;
    static Displaynews lx;
    Image o;
    Graphics og;
    public Displaynews(){
       lx = this;
       titoli = new ArrayList();
     }
    public static void main(String argv[])
    {
       
        if (argv.length != 1) {
            System.err.println("Usage: cmd filename");
            System.exit(1);
        }
        
        // Use an instance of ourselves as the SAX event handler
        DefaultHandler handler = new Displaynews();
        
        // Use the default (non-validating) parser
        SAXParserFactory factory = SAXParserFactory.newInstance();
        try {
            // Set up output stream
            out = new OutputStreamWriter(System.out, "UTF8");
            lx.border.setPreferredSize( new Dimension(400,400));
            lx.frame.setContentPane(lx.border);
            lx.disegno.setPreferredSize( new Dimension(768,800));


            lx.base=new JScrollPane(lx.disegno, 
                JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,               
                JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
            lx.base.setBackground(Color.white);
            lx.border.add(lx.base, BorderLayout.CENTER);

            lx.frame.pack();
            lx.frame.show();


            // Parse the input
            SAXParser saxParser = factory.newSAXParser();
            saxParser.parse( new File(argv[0]), handler);

        } catch (Throwable t) {
            t.printStackTrace();
        }
//        System.exit(0);
    }

    static private Writer  out;
    public void startDocument()
    throws SAXException
    {
    System.out.println("Start Document");
    }

    public void endDocument()
    throws SAXException
    {
    System.out.println("Numero notizie = "+nr);
    }

    public void startElement(String namespaceURI,
                             String lName, // local name
                             String qName, // qualified name
                             Attributes attrs)
    throws SAXException
    {
    String name = lName;
    if ("".equals(name)) name = qName;
      nomecomando = name;
    if(nomecomando.equals("item"))nr++;
    }

    public void endElement(String namespaceURI,
                           String sName, // simple name
                           String qName  // qualified name
                          )
    throws SAXException
    {
    String name = sName;
    if ("".equals(name)) name = qName;
      nomecomando=" ";
    }

 public void characters(char buf[], int offset, int len)
    throws SAXException
    {
        String s = new String(buf, offset, len);
        System.out.println(nomecomando+" "+s); 
        if(nomecomando.equals("title")&&nr>0)
          {
                lx.disegno.scrivinotizia(nr,s);}
    }


	class Display extends JPanel{
                int ystrip;
                String s;
                public void scrivinotizia(int y,String s1){ystrip=y;s=s1;
                 if(s!=null && s.length() != 0)titoli.add(s);repaint();}
		public void paintComponent(Graphics g){
            if(lx.o==null){
            lx.o = lx.frame.createImage(lx.disegno.size().width,lx.disegno.size().height);
            lx.og = lx.o.getGraphics();
                lx.og.setColor(Color.white);
                lx.og.fillRect(0,0,disegno.size().width,disegno.size().height);}
                lx.og.setColor(Color.black);
                if(titoli!=null) if(titoli.size()>0)for(int i=0;i < titoli.size();i++){
                 String s2=(String)titoli.get(i);
                lx.og.drawString(s2,5,i*20+20);
                System.out.println("notizia "+i +" "+s2);
                }
                g.drawImage(lx.o,0,0,this);

}
}
}
Ora potete considerare tutto il Web come un'enorme database e scrivere programmi che vi filtrano centinaia di siti di notizie o dati selezionando quelli piu' interessanti per voi:proprio quello che il Web semantico si propone di raggiungere.
INDIETRO a Imparate Java in una settimana
INDIETRO a Seminario su Java
Maintained by Giuseppe Zito: info@zitogiuseppe.com
Ultimo aggiornamento: