L'abituale
chiosa... ;-)
Visto
che si è parlato di RMI mi piacerebbe schematizzarne la struttura
con UML, in modo da averne una visione di insieme...

Qui
vedete il Client che chiama un metodo su un oggetto remoto (RemoteObject),
almeno è quello che CREDE di fare (user view)... ;-)
Nella
realtà dell'implementazione il Client chiama il metodo su un oggetto
che IMPLEMENTA LA STESSA INTERFACCIA DELL'OGGETTO REMOTO (ma che si trova
nel suo stesso address space, cioè nella stessa JVM), l'oggetto
RemoteObject_Stub (una delle due classi create dal compilatore rmic).
Tale
stub comunica con uno skeleton (RemoteObject_Skel, l'altra classe creata
da rmic) via socket (si passano degli oggetti di tipo Operation attraverso
un ObjectStream) ed è proprio lo skeleton a chiamare il metodo sull'oggetto
"remoto" (per lo skeleton l'oggetto "remoto" è in realtà
locale, stanno sulla stessa JVM) e a ritrasmettere eventuali valori di
ritorno al chiamante (seguendo la stessa strada: l'ObjectStream di prima).
Chiaro no? ;-)
Un
altro caso in cui incontriamo un proxy è quando usiamo le AWT. Ogni
volta che creiamo un componente non "leggero" e lo rendiamo visibile viene
creato il cosiddetto peer (provate a guardare la documentazione del metodo
addNotify()). Il peer (o proxy) è l'oggetto NON java (quindi platform
dependent) che noi vediamo come oggetto java e che usiamo come tale quando
chiamiamo i suoi metodi. Ne esiste uno per ogni piattaforma su cui viene
fatto girare java. Un Button avrà come peer un WindowsButton (non
so se il nome è corretto ;-) sotto windows e un MotifButton (anche
in questo caso non sono sicuro del nome ;-) sotto Unix.
Rendere
"remoto" il memento
Ora torniamo
pure alla nostra applicazione...
Se
vi rammentate, nella puntata precedente avevo introdotto il concetto di
Storage,
un attore capace di... "gestire la memorizzazione da qualche parte (di
solito su disco), a lui NON interessa da dove arrivano gli stati da salvare",
tanto per citarmi. Questa volta vediamo l'esempio di un caretaker (l'attore
che salva gli state) remoto, cioè una classe che incapsula le chiamate
via RMI (trappando le eccezioni) e che quindi vi permette di trattare con
uno Storage locale (che voi VEDETE come locale) ma che ridirige ogni sua
funzionalità ad un VERO Storage istanziato su un'altra macchina.
La
situazione è analoga a quella vista per lo schema del RMI, abbiamo
un RemoteStorageClient che IMPLEMENTA la stessa interfaccia di uno Storage
reale (entrambi implementano infatti StorageI).
RemoteStorageClient
parla con RemoteStorageServer via RMI, ogni metodo dello Storage è
replicato per delega prima dallo RemoteStorageServer (che infatti possiede
un'istanza di StorageI) e poi anche dallo RemoteStorageClient, abbiamo
quindi un doppio salto prima di arrivare allo Storage effettivo.
Avrei
anche potuto saltare il doppio passaggio "client -> server -> storage"
e remotizzare direttamente uno storage, ma avrei perso la possibilità
(che ora invece sfrutto spesso ;-) di usare un RemoteStorageClient al posto
di uno Storage qualsiasi, senza dover cambiare il sorgente di chi usa lo
storage stesso (infatti avrei dovuto curare la cattura delle eccezioni
ad ogni chiamata di metodo).
Guardate
anche la gerarchia delle classi.
Come usare l'esempio
Scompattate
lo zip (mantenendo la gerarchia) e avrete
già tutto compilato e pronto da lanciare:
-
javac
*.java
-
rmic RemoteStorageServer
-
(per generare
stub e skeleton)
Nella
directory di nome "tmpStorage" verranno salvati gli state degli oggetti.
Alla prossima
puntata
Spero
di avervi interessato, come al solito vi esorto a farmi domande e commenti,
sia direttamente
che
su
JAVA-IT. |