professionale 

Java: Arte e tecnica
Come si scrive un'applicazione RMI

Dario de Judicibus

 

 

Una delle caratteristiche di Java Φ quella di rendere semplice la scrittura di applicazioni distribuite in rete, cosa che solitamente provoca un vero e proprio "mal di stomaco" anche ai programmatori professionisti. Vediamo come attraverso un semplice esempio

Lo scorso numero abbiamo scritto che l’RMI Φ un’interfaccia Java che permette di invocare un metodo di un oggetto remoto. L’invocazione Φ del tutto equivalente a quella che possiamo fare dei metodi di un qualunque oggetto locale. Θ quindi possibile passare uno o pi∙ parametri al metodo in questione, cos“ come ricevere un’eventuale risposta e persino un’eccezione. Abbiamo inoltre affermato che l’intero meccanismo Φ molto semplice da usare, dato che scherma il programmatore dai dettagli relativi allo scambio e alla gestione dei dati coinvolti. Adesso vogliamo dimostralo presentando una piccola applicazione, scritta appositamente per la nostra testata, che si prefigge tre obiettivi: descrivere in modo dettagliato tutti i passi necessari per sviluppare un’applicazione basata su RMI; fornire un comodo scheletro di lavoro su cui potrete sviluppare le vostre applicazioni e, terzo obiettivo, far vedere uno dei possibili vantaggi di questo tipo di applicazioni rispetto, per esempio, a interfacce applet ╟classiche╚ verso applicazioni remote. Per far tutto questo procederemo in modo professionale: definiremo cioΦ prima le specifiche dell’applicazione, poi il disegno, e quindi il codice. Anche in questo caso vi consigliamo di stampare il codice presente nel CD-ROM prima di proseguire la lettura.

Le specifiche

Una caratteristica di Java Φ quella legata al concetto di proprietα. Una proprietα altro non Φ che una coppia chiave/valore; un insieme di coppie forma una lista di proprietα e la lista pu≥ essere salvata in un file, acquisendo cos“ un carattere di persistenza; pu≥ inoltre essere associata a un oggetto o a un componente del sistema. In Java esiste una particolare lista di proprietα chiamata Proprietα del Sistema che contiene una serie d’informazioni disponibili alle applicazioni e relative al sistema su cui girano. L’approccio Φ simile a quello usato per le cosiddette variabili di sistema o variabili d’ambiente che esistono in pratica un po’ in tutti i sistemi operativi. Al contrario di queste ultime, tuttavia, le proprietα del sistema sono le stesse in tutte le piattaforme su cui gira Java, anche se, ovviamente, esse prenderanno valori diversi nei differenti sistemi. La lista completa di tali proprietα Φ riportata nella tabella 1, con un esempio del tipo di valore che ciascuna proprietα pu≥ prendere, e un marcatore che indica se la proprietα in questione Φ accessibile via applet (~ ) o meno.

Per ragioni di sicurezza, infatti, Java non permette a un’applet di accedere ad alcune informazioni critiche del sistema su cui Φ in esecuzione. Questo evita che, per esempio, caricando una pagina via Internet, il gestore delle pagine acquisisca alcune informazioni sul vostro sistema a vostra insaputa utilizzando un’apposita applet. Ma se io volessi scrivere un’applicazione che volutamente esporta verso l’esterno determinate informazioni?

Proviamo a scrivere un’applicazione del genere, un programma cioΦ che mette a disposizione di un altro programma operante su un sistema remoto tutte le proprietα del sistema locale. Il programma in questione dovrα, a fronte di una richiesta remota, restituire il valore della proprietα di cui le sarα stato fornito il nome. Nel caso in cui la proprietα richiesta non esistesse verrα restituito un messaggio di errore. Sarα inoltre possibile richiedere a priori se la proprietα in questione Φ supportata o meno.

Il disegno

Per far questo dobbiamo prima di tutto scrivere una classe che, data una proprietα, ne ritorni il valore; la chiameremo classe SystemProperty. Scriveremo quindi un programma di collaudo della classe in questione che denomineremo ShowProperty. Una volta verificato il corretto funzionamento delle due classi, inizieremo a costruire quelle per l’accesso remoto. Avremo in tutto altre quattro classi. La prima, RemoteProperty, rappresenta la proprietα del sistema remoto, e non Φ in realtα una classe, ma l’interfaccia d’invocazione dell’oggetto remoto, cioΦ la definizione dei metodi che potranno essere invocati remotamente. La seconda, RemotePropertyImpl, rappresenta l’oggetto i cui metodi saranno invocati remotamente, ed Φ basata sull’interfaccia definita in precedenza. La terza, RemotePropertyServer, Φ l’applicazione remota che crea un oggetto della classe RemotePropertyImpl e lo registra sotto la propria URL. In pratica Φ come se il server affermasse ╟Mi chiamo in un certo modo. La URL attraverso la quale potete accedere ai miei servizi Φ questa, e l’oggetto in questione pu≥ essere esportato remotamente attraverso di me╚. La quarta classe, ShowRemoteProperty, Φ l’applicazione che richiede al sistema remoto una determinata proprietα e ne visualizza il valore sul sistema locale. Ricordiamo che qui, sia i concetti di locale e di remoto sia quelli di servente e di cliente sono del tutto intercambiabili. Le ultime due classi sono generate automaticamente con il comando rmic, e rappresentano rispettivamente la matrice (RemotePropertyImpl_Stub) che si occupa della serializzazione dei dati, e lo scheletro (RemotePropertyImpl_Skel) che interpreta la richiesta e restituisce il valore della proprietα al cliente.

Il codice

E veniamo ora al codice. Iniziamo con la classe SystemProperty, ovverosia con la classe il cui compito Φ restituire il valore di una proprietα di cui le sia stato fornito il nome. Θ una classe molto semplice: dato che non c’Φ alcun motivo di creare un oggetto per ottenere il valore in questione, scriveremo una classe statica, cioΦ una classe i cui metodi e variabili sono tutte statiche. L’acquisizione vera e propria del valore sarα ottenuta con l’istruzione

value = System.getProperty( propertyName ) ;

La classe verifica inoltre che la proprietα richiesta sia tra quelle supportate. Per semplicitα ci≥ avviene utilizzando un vettore statico. Tale approccio non Φ ottimale, in quanto il giorno in cui si dovessero aggiungere altre proprietα di sistema a quelle attuali, bisognerα aggiornare la lista in questione. Se volete, come esercizio potete provare a vedere come ottenere tale lista direttamente dal sistema.

La classe per il collaudo di SystemProperty Φ ancora pi∙ semplice. In pratica non fa altro che ricevere il nome della proprietα in ingresso e restituirne il valore in uscita utilizzando appunto la classe suddetta:

String propertyValue = SystemProperties.getValue( propertyName ) ;

Per esempio:

[I:\devel\Java\RMI] java ShowProperty os.name

Property 'os.name' has value 'Windows 95'.

A questo punto siamo pronti per sviluppare l’applicazione RMI. Per prima cosa definiamo l’interfaccia, come estensione di java.rmi.Remote. Questa conterrα un solo metodo:

public String getValue( String propertyName )
throws java.rmi.RemoteException ;

Da notare l’eccezione che va sempre riemessa qualunque sia il metodo e qualunque cosa faccia. Stiamo infatti effettuando comunque un’operazione via rete, per cui non si pu≥ mai sapere: la linea pu≥ cadere, essere intasata, o comunque i dati possono perdersi o rovinarsi per un qualunque motivo. Fatto questo ne scriviamo l’implementazione, definiamo cioΦ una classe RemotePropertyImpl che estende UnicastRemoteObject e implementa l’interfaccia RemoteProperty. Anche questa classe Φ molto semplice, essendo formata da un costruttore che non fa altro che richiamare il costruttore della superclasse, e dal metodo getValue che contiene solamente

return SystemProperties.getValue( propertyName ) ;

Per finire, le due classi pi∙ complicate. Prima il servente. Questi contiene un metodo statico principale (main) che per prima cosa crea e installa un gestore di sicurezza, in modo da evitare che siano esportate classi diverse da quelle previste dall’applicazione (vedi puntata precedente), quindi costruisce l’URL per mezzo della quale il servente Φ acceduto, e infine registra l’oggetto da invocare remotamente. L’URL base va fornita quando si lancia il servente, altrimenti si assume che cliente e servente siano sullo stesso sistema e che quindi questa sia nulla. A tale stringa il programma aggiunge l’identificativo dell’oggetto remoto, un nome cioΦ che identifica univocamente l’oggetto RemotePropertyImpl associato al servente di cui Φ stata fornita l’Url base, nel nostro caso ╟RemoreProperty╚. Questo in quanto lo stesso servente pu≥ gestire pi∙ oggetti. Attenzione per≥: se volete collaudare questo programma lanciando sia il cliente sia il servente sulla stessa macchina, dovrete assicurarvi comunque di essere in rete, cioΦ che il TCP/IP sia attivo. Se fate questo su una macchina connessa a una rete locale (LAN) via TCP/IP, non c’Φ problema. Se invece siete a casa sul vostro PC, dovrete collegarvi a Internet via modem per far funzionare il tutto. La registrazione avviene utilizzando la classe Naming, ovvero

RemotePropertyImpl obj = new RemotePropertyImpl( ) ;

Naming.rebind( serverUrl, obj );

Per quanto riguarda il cliente, all'inizio anch'esso installerα il gestore di sicurezza RMI, cioΦ

System.setSecurityManager( new RMISecurityManager( ) ) ;

quindi costruirα l’Url del servente e lo userα per agganciare l’oggetto remoto a esso associato. A questo punto il gioco Φ fatto: basterα utilizzare l’oggetto remoto come se fosse un oggetto locale e ricavare il valore della proprietα come avevamo fatto in precedenza con ShowProperty:

RemoteProperty ps = (RemoteProperty)Naming.lookup( serverUrl ) ;

String propertyValue = ps.getValue( propertyName ) ;

Compilazione ed esecuzione

A questo punto ci ritroviamo con sei sorgenti:

ShowRemoteProperty.java

RemotePropertyServer.java

RemotePropertyImpl.java

RemoteProperty.java

ShowProperty.java

SystemProperties.java

che compileremo lanciando javac su ognuno dei sei sorgenti, e quindi rmic su RemotePropertyImpl. Il risultato saranno otto file di tipo .class, ovverosia

ShowRemoteProperty.class

RemoteProperty.class

ShowProperty.class

SystemProperties.class

RemotePropertyServer.class

RemotePropertyImpl.class

RemotePropertyImpl_Stub.class

RemotePropertyImpl_Skel.class

Carichiamo sul sistema locale

l'applicazione cliente (ShowRemoteProperty.class)

l'interfaccia (RemoteProperty.class)

la matrice (RemotePropertyImpl_Stub.class)

mentre sul sistema remoto mettiamo:

l'applicazione servente (RemotePropertyServer.class)

l'interfaccia (RemoteProperty.class)

l'implementazione (RemotePropertyImpl.class)

lo scheletro (RemotePropertyImpl_Skel.class)

Adesso siamo pronti. Sul servente lanciamo il registro RMI in un processo a parte. Ad esempio, in Windows 95 eseguiremo

start rmiregistry

mentre in UNIX il comando da usare Φ

rmiregistry &

Sempre sul servente facciamo partire l'applicazione che gestirα le richieste del cliente, e cioΦ

start java RemotePropertyServer (Windows 95)

java RemotePropertyServer & (UNIX)

Spostiamoci ora sul cliente e lanciamo, per esempio

[F:\test\Java\RMI] java ShowRemoteProperty os.name

Property 'os.name' has value 'Windows 95'

Semplice, no?

(ddejudicibus@tecnet.it)

TABELLE

Proprietα

Descrizione

Esempio

~

Java.version

Attuale versione del linguaggio Java

11

s∞

Java.vendor

Stringa identificativa del produttore del linguaggio

Sun Microsystems Inc.

s“

Java.vendor.url

Url del produttore del linguaggio

http://www.sun.com/

s“

Java.home

Directory d’installazione di Java

d:\java

no

Java.class.version

Attuale versione del formato delle classi Java

45.3

s“

Java.class.path

Cammino di ricerca per le classi Java

.;d:\java\bin;

no

Os.name

Nome del sistema operativo

Windows 95

s“

Os.arch

Architettura del sistema operativo

x86

s“

Os.version

Attuale versione del sistema operativo

4.0

s“

File.separator

Separatore dei componenti del nome del file

\

s“

Path.separator

Separatore dei vari cammini di ricerca

;

s“

Line.separator

Separatore di lista

\n

s“

User.name

Nome dell'utente

pippo

no

User.home

Directory principale dell'utente

d:\java\bin\..

no

User.dir

Attuale directory di lavoro dell'utente

i:\devel\java\rmi

no

Tabella 1 - Proprietα del sistema

 

Letti per voi

Se cercate un buon libro che affronti la programmazione in Java in modo semplice ma abbastanza completo, vi consigliamo The Java Tutorial di Mary Campione e Kathy Walrath, edito dalla Addison-Wesley e facente parte della collana The Java Series. Il volume, di oltre 400 pagine, copre un po’ di tutto, dai princ“pi della programmazione orientata agli oggetti alle applet fino a descrive le principali classi base del Java Development Kit, dalle interfacce grafiche alla programmazione in rete con perfino un accenno all’integrazione di metodi nativi in programmi Java. La versione in nostro possesso Φ un po’ datata, ma Φ di recente uscita la seconda edizione che copre fino al JDK 1.2. L’aspetto interessante di questo libro Φ che le autrici propongono vari cammini alternativi di lettura ognuno indicato per esigenze di programmazione differenti. Per cui, per esempio, chi Φ interessato a scrivere solo applet, pu≥ saltare a piΦ pari il capitolo sui metodi nativi, mentre chi Φ pi∙ interessato alle applicazioni, pu≥ tranquillamente non leggere la sezione che parla di applet. Entrambe le autrici sono scrittrici professioniste di manuali (technical writer) e fanno parte del team JavaSoft di Sun. Il libro Φ di facile lettura, pieno di esempi, e corredato da un utile CD-ROM contenente l’ultima versione del JDK, moltissimi esempi di ottimo codice e diversi prodotti per lo sviluppo in Java.

Il codice ISBN Φ 0-201-31007-4.

 

Forse non tutti sanno che...

Se volete comparare due stringhe, non utilizzate mai l’operatore ==, ma i metodi di String equals() oppure compareTo(). Il motivo Φ che una variabile di tipo stringa Φ in effetti una referenza alla stringa vera e propria, e non il suo contenuto. Per cui, quando scrivete il codice

if ( compiler == "VisualCafΘ" )

{

System.println( "Symantec" ) ;

}

state comparando il contenuto di due puntatori, e non le due stringhe di caratteri. Ecco perchΘ se anche compiler contenesse la stringa "VisualCafΦ", il codice non stamperebbe la scritta "Symantec". In generale utilizzando l'operatore == state verificando che i due oggetti siano in realtα lo stesso oggetto, non che abbiano lo stesso contenuto.

Ma allora perchΘ il seguente pezzo di codice funziona?

1| String compiler = "VisualCafΘ" ;

2|

3| if ( compiler == "VisualCafΘ" )

4| {

5| System.println( "Symantec" ) ;

6| }

Il motivo Φ semplice. Per motivi di ottimizzazione, tutte le stringhe di tipo literal, cioΦ fissate a priori esplicitamente, che abbiano lo stesso contenuto, fanno riferimento allo stesso oggetto. In pratica il compilatore si accorge che ci sono due stringhe uguali, una alla riga 1 e una alla riga 3. Dato che si tratta di stringhe esplicite, e non di variabili, esse non possono essere modificate, per cui genera un solo oggetto il cui valore Φ "VisualCafΦ". A questo punto, a seguito dell’istruzione 1, alla variabile compiler viene assegnato il ╟puntatore╚ a tale oggetto, che poi Φ lo stesso della stringa alla riga 3, per cui la condizione di uguaglianza Φ soddisfatta. Attenzione per≥: il fatto che il secondo blocco di codice funzioni non vi autorizza a usarlo. Sarebbe un errore che si potrebbe pagare in seguito. Se infatti in futuro decideste di acquisire il valore di compiler dall’esterno, magari passato come parametro, e se fra tale assegnazione e la comparazione ci fosse molto codice, nel momento in cui il programma non dovesse pi∙ funzionare ci potreste perdere vari giorni prima di capire perchΘ. Cercate quindi di scrivere sempre un codice corretto e, nel caso delle stringhe, usate sempre gli appositi metodi e vedrete che non avrete problemi


Internet News Φ un mensile della Casa Editrice Tecniche Nuove S.p.A.
⌐ 1995/96/97. Tutti i diritti sono riservati.
Internet News non risponde di eventuali errori e omissioni.
Commenti a: inews@tecnet.it