Università degli Studi di Cagliari

UNIFIED MODELING LANGUAGE

E

REMOTE METHOD INVOCATION

Andrea Manconi

Giacomo Vacca

Indice


1.0 Introduzione.

2.0 Unified Modeling Language.

3.0 Remote Method Invocation.

4.0 Potete consultarci all'indirizzo...

Introduzione


Questo lavoro è stato realizzato nell'anno accademico 97/98 come tesina per il corso di Ingegneriadel Software tenuto presso l'Università degli Studi di Cagliari, dal professore Ing. G. Armano.

Se dalla lettura di questo lavoro si vorranno fare delle osservazioni ci potete scrivere al nostroindirizzo di posta elettronica.

per tornare all'indice

Unified Modeling Language


2.1 Introduzione.

L'Unified Modeling Language è un linguaggio di modellazione per progetti object oriented. L'UML è nato nei laboratori della Rational dal lavoro comune di Booch, Rumbaugh e Jacobson che prima del 1995 ricoprivano, ognuno singolarmente, la più ampia fetta di mercato nell'analisi dei progetti object oriented. Tutti e tre lavoravano in direzioni parallele da cui alla fine del 1995 la necessità di unirsi per portare a termine il progetto di unificazione che prese il nome di Unified Modeling Language.

Il suo utilizzo è particolarmente utile perché in fase di progettazione consente una visione globale a diversi livelli di astrazione dei vari aspetti del progetto: dalle funzionalità cui ilsoftware finale deve adempiere, ai legami tra le diverse classi, alle relazioni tra i moduli software e le macchine hardware in cui questi devono funzionare.

La fase di progettazione è utilissima perché permette di apportare modifiche e correzioni in maniera diretta. Tutti sappiamo che quando abbiamo a che fare con programmi che utilizzano molte classi, la modifica di una di queste può comportare la modifica del codice delle altre alle qualiè collegata; senza avere di fronte una "piantina" delle classi (class diagram), cioè una sorta dimappa che indichi come abbiamo collegato le varie classi tra loro e come interagiscono, dovremmocompiere uno sforzo di memoria per ricordarci tutti i legami esistenti tra le classi per poi ritrovarci quasi sicuramente degli errori in fase di compilazione che aumenteranno la nostra sete omicida verso il compilatore. Un buon utilizzo di UML invece evita questi problemi; se siamoin grado di sviluppare il progetto in maniera ottimale sul tool della Rational, possiamo apportare le modifiche sul class diagram avendo una visione diretta e globale delle classi. Abbiamo detto precedentemente che UML permette varie viste del progetto, va aggiunto che una modifica ad una data vista comporta una automatica correzione alle viste dei livelli inferiori.

Oltre che in fase di progettazione, uno dei maggiori vantaggi offerti dall'UML è senz'altro il Reverse Engineering che permette di ricavare il modello UML a partire dal codice. L'utilità di questa funzione è evidente in fase di manutenzione; tutti sappiamo che spesso si attuano modifiche al codice senza apportare le relative correzioni alla documentazione e questo a lungo andare comporta che chi debba fare una manutenzione su un software che ha avuto varie modifiche debba lavorare con una documentazione vecchia, inadeguata. Il Reverse Engineering permette, una volta che sono state apportate delle modifiche al codice, di ricreare a partire da questo il modello UML e quindi di avere una documentazione consistente e più semplice da capire.

per tornare all'indice

2.2 Descrizione del tool Rational Rose.

L'UML presenta tre finestre principali alla sua apertura. La prima che analizziamo è la finestra delle viste (in alto a sinistra). Questa finestra presenta le varie viste esistenti:

		1. Use case view
2. Logical view
3. Component view
4. Deployment view

L'ordine in cui sono poste è lo stesso in cui debbono essere analizzate.Cliccando due volte su ognuna di queste viste possiamo esploderle e vedere come sono composte alloro interno.

a. Use case view.

Per prima cosa appare qui come in tutte le viste un main che rappresenta il diagramma principale per quella determinata vista.

Subito dopo il main possiamo trovare i vari attori rappresentati da omini stilizzati:

					
E gli use case che invece vengono rappresentati tramite ellissi:

					
Ogni case è caratterizzato da una scritta che indica il proprio comportamento.Un case può essere meglio analizzato con degli interaction diagrams (che appartengono alla vista degli use case); se questi sono stati creati li possiamo vedere sulla finestra centrale cliccandodue volte sullo use case.

Per creare gli use case diagram, che come tutti i diagrammi vengono mostrati nella finestra centrale, possiamo interagire con la barra degli strumenti che viene posta automaticamente all'atto dell'installazione del software tra la finestra che indica la mappa deidiagrammi e quella che invece li mostra. Per rappresentare un actor basta posizionarsi con il mouse sull'omino stilizzato sulla barra in questione cliccare una sola volta e quindi posizionarsi con il mouse sulla finestra dei diagrammi e cliccare una seconda volta in modo da rilasciare l'actor. Se esistono degli altri attori viene mostrata una finestra con i nomi degli altri attori che possiamo eventualmente scegliere anche per il nuovo, altrimenti basta digitare un nuovo nome che verrà accettato. Una volta che abbiamo rappresentato i vari attori, ci rimane da creare gli use case per evidenziare le azioni che questi compiono. Possiamo disporre gli use case sul diagramma nello stesso modo in cui abbiamo disposto gli actor, cioè interagendo con labarra degli strumenti. Dato un nome allo use case (basta semplicemente digitarlo una volta che è stato posizionato) dobbiamo creare le association cioè le relazioni che esistono tra un'actor eun particolare use case. Se clicchiamo due volte su un actor, use case o association compare la finestra delle specification.

Nel caso delle association la finestra prende il nome di Association Specifications nameAssociation, in questa finestra possiamo dare un nome alla association, specificare lo stereotipo in modo da poter trattarle non solo come semplici association ma anche come extendes ouses; possiamo inoltre specificare i ruoli e la documentazione relativa. La documentazione di qualunque oggetto dei diversi diagrammi viene sempre mostrata nella finestra in basso a sinistrache rimane vuota o disattivata quando non è stata scritta alcuna documentazione. Per gli actorinvece la finestra delle specification differisce per l'assenza dei ruoli. Lo stereotipo specificato in questa finestra è Actor per default. Nelle specification degli use case invece è possibile vedere gli interaction diagrams associati semplicemente cliccando il bottone "Diagrams".

b. Logical view.

Stesso discorso per quanto riguarda il main. Oltre questo possiamo avere dei package e delle classi. Esplodendo i package possiamo ritrovare la stessa gerarchia di main, package e altre classi; in generale comunque esplodendo le classi viene mostrata per ognuna l'elenco dei propri slots e dei metodi. Ancora cliccando due volte sugli slots e sui metodi abbiamo la possibilità di accedere alle specifications di questi.

I package vengono rappresentati tramite delle cartelle:

					
Anche in questo caso per creare i class diagram si deve interagire con la barra degli strumenti che sarà diversa dalla precedente per il semplice fatto che non abbiamo a che fare conactor e use case ma con classi. Per creare un package, che ricordiamo contiene al suo interno delle classi, e altri package annidati, basta selezionare questi dalla barra degli strumenti cliccando una sola volta e poi posizionare il cursore dove vogliamo mettere il package nella finestra dei diagrammi e quindi cliccare un'altra volta per rilasciare il package. Possiamo dareil nome al package semplicemente digitandolo (appena il package è stato rilasciato), oppure posizionandoci sopra e cliccando due volte in modo che compaia la finestra delle Class Specification in cui è presente una voce per il nome, così come avveniva per gli actor, use case e association nella use case view.

E' possibile inoltre mettere dei commenti e delle note.
Per mettere dei commenti si deve scegliere il simbolo:

					
Il bottone si trova sulla barra degli strumenti. Una volta selezionato il bottone si ci deve posizionare dove si vuole inserire il commento e scrivere.

Per le note invece si deve selezionare sempre dalla barra degli strumenti il simbolo sotto indicato e quindi inserire la nota:

							
Una volta inserita la nota la si deve collegare ad una classe, a un package o a un qualunque oggetto del class diagram; per fare questo occore selezionare dalla barra degli strumenti (b.d.s.) il simbolo

- - - - - - - - - - - - - -
denominato Anchor Note to Item.

La cosa più importante in un class diagram è quella di inserire una classe. Per inserire una classe basta selezionare dalla b.d.s. il simbolo corrispondente:

class
					
Notiamo che il simbolo che rappresenta la classe è suddiviso in tre parti. Nella parte più in alto si specifica il nome, mentre più interessanti sono gli altri due. Al centro è possibile specificare gli slots, basta posizionarsi sulla classe interessata e cliccare una volta il tasto destro del mouse; comparirà una finestra in cui sarà possibile scegliere la voce New Attribute, e una volta selezionata questa si può introdurre un nuovo attributo con la sintassi:

Name : type = initVal
Cioè è possibile specificare a fianco al nome il tipo dell'attributo ed eventualmente il valore iniziale. Per default gli slots vengono dichiarati privati indicando un lucchetto accanto al nomedell'attributo. E' possibile scegliere il livello di protezione cliccando due volte il tasto sinistro del mouse una volta posizionati sul simbolo private; comparirà una finestra con i simboli relativi ai diversi modificatori che potremo selezionare cliccandoci sopra con il mouse.

Allo stesso modo in cui si sono inseriti gli slots si possono introdurre anche le operazioni che vengono indicate nella parte inferiore della classe, basta posizionarsi sulla classe che ci interessa e quindi come nel caso precedente cliccare il tasto destro del mouse e scegliere la voce New Operation. La sintassi con cui vengono dichiarati i metodi è la seguente

< < stereotype > > opname(argList):return
Cioè oltre al nome dell'operazione è possibile specificare sia lo stereotipo, sia gli argomenti dell'operazione che il valore restituito. E' importante specificare tutte le voci della sintassi non solo per la comprensione delle operazioni ma anche per generare il codice corretto a partire dal diagramma. Se non si specificano correttamente argomenti e valori restituiti, così come il tipo e il grado di protezione per gli attributi, sorgeranno dei problemi nella fase di generazione del codice.
(La scelta dipende comunque dal livello di astrazione che si desidera.)

Se si vuole scegliere una association per collegare due classi dalla b. d. s. si deve scegliere il bottone:

					
e collegare le due classi. Mentre per le generalizzazioni il tasto corrispondente è il seguente:

					

c. Component view.

Esplodendo la component view compare come al solito il main, e di seguito i vari componenti rappresentati in questo modo:

					
Per disporre un component nel diagram si deve selezionare il simbolo dalla b. d. s., posizionarsi sul diagram nel punto dove lo si vuole porre e digitare il nome.

I legami tra i vari component prendono il nome di dependency e vengono selezionati cosi come le association per i class diagram. Il loro simbolo è :

- - - - - - - - - ->
I componenti rappresentano moduli software.

d. Deployment view.

I deployment view mostrano i vari processor (e i component associati) e i device .I processor vengono rappresentati in questo modo:

					
Mentre i device vengono rappresentati in quest'altro modo:

					
Il legame tra processor e device in questo caso è chiamato Connection e il suo simbolo rappresentato da una linea continua si trova sempre nella b. d. s. Per legare i processor e i device tra di loro basta selezionare la Connection dalla b. d. s. e quindi collegare i due "oggetti" del diagramma di cui si vuole la connessione.

E' possibile, come detto in precedenza specificare i process associati al processor; per fare questo basta aprire la finestra delle specification cliccando due volte con il mouse sopra il processor e quindi selezionare la voce Detail e inserire i process. E' possibile indicare anche un livello di scheduling tra quattro possibili:

   		Preemptive			NonPreemptive		Manual		Executive
I processor rappresentano unità HW in grado di gestire ed elaborare dati. I device rappresentanoinvece entità HW senza capacità di elaborazioni dati.

per tornare all'indice

2.3 Descrizione del progetto.

Lo scopo di questa tesina è stato quello di analizzare e progettare un sistema di raccolta e memorizzazione di dati atmosferice. Si considerano diversi dispositivi sistuati in diverselocalità della Regione Sardegna che devono comunicare tramite una stazione di controllo. I dettaglidella comunicazione tra i dispositivi e la stazione di controllo rimangono nascosti. E' inveceimportante sapere che le comunicazioni tra il centro di controllo e i dispositivi sono realizzatetramite oggetti locali denominati STUB. Le specifiche del progetto prevedono che la stazionedi controllo sia un'istanza della classe WeatherStation, mentre gli stub che colloquiano coni dispositivi remoti devono essere istanze delle seguenti classi:

	- AirThermometer			Termometro dell'aria	- GroundThermometer			Termometro del suolo	- Anemometer				Misuratore della velocità del vento	- Barometer				Misuratore di pressione atmosferica	- RainGauge				Indicatore di precipitazioni atmosf.
La stazione di controllo comunica con gli oggetti locali (stub) che, in fase di inizializzazione , vengono associati una volta per tutte con i dispositivi remoti. Una volta effettuato il collegamento non occorre preoccuparsi del fatto che si sta colloquiando con dispositivi remoti.

Il centro di controllo sarà avvertito dagli oggetti "stub" ogni volta che è necessario effettuare la lettura dei dati. I dati dovranno essere registrati ad opera della stazione di controllo in un file denominato LogFile.

Il progetto è stato ampliato da noi sviluppatori per potere sfruttare e mostrare le funzionalità del tool U. M. L. . Si è supposto che i dati, una volta immagazzinati nel LogFile, potessero essere analizzati da un centro di elaborazione in grado di effettuare delle elaborazioni.Le elaborazioni sviluppate da questo centro di calcolo denominato ElaborationCenter sono dei semplici calcoli dei valori max, min e della media.

I dati elaborati devono essere registrati in un file denominato AverageFile. Si occupa della registrazione del file lo stesso ElaborationCenter. Una volta che i dati sono stati elaborati abbiamo supposto la possibilità che degli utenti potessero accedere a questo file e leggerlo. L'accesso ai dati elaborati è gestito da un manager denominato AverageFileManager.

per tornare all'indice

2.4 Analisi del progetto con U. M. L.

L'analisi del progetto con U. M. L. prevede l'analisi delle singole viste
	        Use case view.	       	Logical view.		Component view.		Deployment view.

Ogni vista prevede un livello di astrazione più o meno marcato; in questo modo si può analizzare lo use case diagrams senza dovere tenere conto del livello di implementazione. E' un errore comune quello di analizzare questa vista avendo in mente già quali devono essere le classie i legami tra queste; di questi passi si deve invece tenere conto nella vista successiva che haproprio il compito di nascondere questa fase allo use case view.

Analizziamo quindi di seguito le diverse viste.

Use case view.

La vista degli use case ha lo scopo di mostrare quali devono essere le funzionalità del sistema.Questa vista viene presentata al cliente che ha commissionato il progetto come contratto. Infatti, capita spesso che il cliente non abbia bene in mente quali devono essere le potenzialitàdel sistema o non possegga le conoscenze tecniche richieste per comprendere livelli meno astrattidi progetto, e si può rischiare che ad una data richiesta non corrispondano le aspettative finalidel cliente. Per ovviare a questi problemi, alla richiesta del progetto si analizza la use case view e lo si propone al cliente come contratto.

Passiamo ora alla fase propria di analisi di questa vista; incominciamo a determinare chi sono gli attori e gli use case. Ricordiamo che un attore è qualcosa in grado di compiere un'azione o su cui viene compiuta un'azione, ad esempio un attore può essere un sistema Unix (in grado di compiere delle azioni) o un file (su cui viene compiuta l'azione di scrittura e di lettura). Unouse case invece è un'azione compiuta da un attore. Un modo per individuare gli attori e gli use case in un progetto è quello di descrivere tutto ciò che deve fare il sistema e quindi individuare i soggetti e i verbi al suo interno. Gli attori saranno individuati dai soggetti mentre gli use case corrisponderanno ai verbi.

La descrizione per il nostro sistema può essere fatta nel modo seguente, dove in neretto vengono posti i soggetti e i verbi:

WeatherStation deve attivare lo Stub e una volta che questo è stato attivato dovrà inviare i datia WeatherStation. I dati inviati saranno registrati da WeatherStation su un file chiamato LogFile.Un ElaborationCenter avrà quindi il compito di utilizzare i dati del LogFile per calcolare alcunivalori statistici e quindi registrare il tutto su un file chiamato AverageFile. A questo punto unUser può accedere tramite un gestore chiamato AverageFileManager all'AverageFile.

Quindi gli attori che possiamo individuare sono WeatherStation, Stub, ElaborationCenter, LogFile,AverageFileManager, AverageFile e User. Le azioni compiute da questi attori sono anch'esse evidenziate in neretto e sono le azioni di attivare gli stub, memorizzare i nuovi dati,elaborare i dati immagazzinati, memorizzare i dati elaborati, concedere l'accesso ai dati.

Le azioni individuate portano a includere nel diagramma i seguenti use case:

Stub - WS communication : rappresenta l'azione di comunicazione tra stub e WeatherStation.

UpdateLogFile : è l'azione di aggiornamento del LogFile, ogni volta che vengono fatte delle nuove misure.

UpdateAverage : è l'azione di aggiornamento dell'AverageFile.

AverageFileAccess : azione che gestisce l'accesso dell'utente all'AverageFile.

Resta da determinare le associazioni tra gli attori e gli use case che ormai sono ovvie.Le associazioni rappresentano i legami tra i vari attori e use case nel diagramma; risulta chiaro quindi che WeatherStation e Stub sono legati tra loro tramite lo use case STUB-WS communication, e a sua volta WS sarà in relazione con LogFile tramite lo use case UpdateLogFile.

All'azione di UpdateAverageFile contribuiscono tre attori che quindi saranno collegati tramite associazioni a questo use case; questi sono il LogFile, da cui vengono letti i dati da elaborare, l'ElaborationCenter, che elabora questi ultimi, e l'AverageFile, dove vengono registrati i file elaborati.

Infine all'AverageFileAccess sono associati ancora tre attori: lo User che fa la richiesta di accesso, l'AverageFileManager che valuta la richiesta e l'AverageFile che contiene i dati da mostrare.

Interaction diagrams.

Sono stati inoltre analizzati gli interaction diagrams per tre dei quattro use case del sistema,per la precisione per Stub-WS communications, UpdateAverage e AverageFileAccess.

Analizziamo per primo l'interaction relativo a Stub-WS communication. Di questo è stato creato per primo il sequence diagram, mentre il collaboration viene creato a partire dal sequence automaticamente, sotto richiesta, dal Rose. Il nome dato ai due diagrammi è transmission. Questo diagramma deve mostrare come in funzione del tempo interagiscono Stub e WS sempre astraendo dalla fase di implementazione. Per come abbiamo visto le cose WeatherStation attiva una sola volta lo stub, che da quel momento deciderà quando trasmettere i dati che possiede, in base alle sue specifiche di temporizzazione.

L'altro interaction che andiamo ad analizzare è UpdateAverage. Anche di questo, come di tutti gliinteraction, abbiamo prodotto sia il sequence che il collaboration diagram. Questo interaction si occupa di analizzare la fase di aggiornamento dell'AverageFile; per fare questo l'ElaborationCenter deve accedere al LogFile ed elaborare i dati. L'elaborazione è rappresentatada una self-delegation sull'ElaborationCenter, questo perché è lo stesso ElaborationCenter che sioccupa di elaborare i dati. Una volta fatto questo i dati vengono registrati sull'AverageFile ad opera dello stesso centro di elaborazione.

Analizziamo ora l'ultimo interaction diagram che abbiamo chiamato AverageFileAccess e che analizza lo use case di accesso all'AverageFile da parte di un utente. L'accesso al file è gestito dall'AverageFileManager, che deve verificare tramite una password la possibilità di un utente di accedere al file; se la password fornita dall'utente non è valida l'accesso verrà negato. Anche in questo caso è presente una self-delegation sull'AverageFileManager per la verifica della password, in quanto la verifica è una operazione di cui si occupa l'AverageFileManager stesso. Una volta che l'utente è stato accettato è sempre l'AverageFileManager che deve fornire i dati all'utente, e per fare questo deve accedere all'AverageFile.

Logical view.

Nella logical view vengono mostrate le classi ed i legami esistenti tra esse. Dalle specifiche del progetto sappiamo che la stazione di controllo deve essere un'istanza della classe WeatherStation mentre iAirTherm, iGroundTherm, iAnemom, iBarom e iRainGauge sono sottoclassi di Stub.

Con l'UML è possibile organizzare le classi in package; l'utilizzo dei package è consigliato quando si ha a che fare con un gran numero di classi, tali che la loro rappresentazione in un solo main sarebbe scomoda da vedere. Viene consigliato di utilizzare i package quando il diagramma di classe non può essere contenuto in un foglio di formato A4.

Nel nostro caso abbiamo deciso di utilizzare due package; un package che contiene le classi legate alla WeatherStation, o per meglio dire le classi che si occupano di gestire il prelievo dei dati, e quindi le interfacce, e le classi che si occupano della elaborazione degli stessi dati, e un altro package dove sono contenute le classi che contengono le operazioni per il prelievo dei dati dai dispositivi remoti.

In realtà questo verrà spiegato meglio nella seconda parte della tesina, ma è bene sapere che poiché nei diagrammi di classe dobbiamo rappresentare i legami tra tutte le classi, bisogna tenere conto anche del fatto che il nostro progetto prevede diverse macchine che comunicano tra loro e quindi abbiamo il compito di dovere rappresentare anche il protocollo di comunicazione e quindi le interfacce di comunicazione. Siamo arrivati alla soluzione che le operazioni si trovinoin remoto e vengono richiamate in locale; questo significa semplicemente che su una macchina saranno presenti implementazione ed interfaccia mentre nell'altra macchina (stazione di controllo)sarà presente solo l'interfaccia e quindi la possibilità di richiamare operazioni presenti nellamacchina remota. Fatta questa precisazione doverosa, ma che non fa parte di questa sezione (e verrà di fatto ampiamente sviluppata nella seconda parte della tesina), possiamo passare ad analizzare le varie classi.

Analizziamo per primo il package denominato Remote. Al suo interno è presente una superclasse chiamata Sensor, le cui sottoclassi sono AirTherm, GroudTherm, Anemom, Barom, RainGauge, e l'interfaccia della classe WeatherStation che abbiamo chiamato intWS. L'interfaccia è necessariaperché dalle specifiche del progetto sappiamo che il dispositivo deve trasmettere a WeatherStation una nuova misura; per fare questo occorre far si che WeatherStation abbia un metodo esportabile dal dispositivo per potere leggere la misura, e quindi deve essere presente un'interfaccia. Possiamo vedere che Sensor è una superclasse dal modo in cui sono collegate le sottoclassi, cioe tramite una relazione di generalizzazione. La classe Sensor ha quattro slots privati che sono :

status: questo attributo è stato introdotto per evidenziare lo stato del dispositivo, che può essere attivo o disattivo. Un dispositivo infatti si deve attivare per effettuare le misure a intervalli di tempo determinati, mentre tra due misure successive il dispositivo deve essere disattivo. Dovendo rappresentare uno stato è stata dichiarata di tipo boolean.

code: è il codice del dispositivo. Questo dalle specifiche deve essere unico per ogni dispositivo. Un codice può essere una combinazione di caratteri e numeri, e quindi è stata dichiarata di tipo String.

place: indica la località di appartenenza del dispositivo come ad esempio Olbia, Oristano o Cagliari. E' stata dichiarata di tipo String.

TimeInterval: rappresenta l'intervallo di tempo tra due misure successive del dispositivo. E' stata dichiarata di tipo int.

Le operazioni sono due:

GetStatus: è un'operazione che restituisce lo stato del dispositivo, e poiché lo stato è dichiarato di tipo boolean, anche il tipo restituito dall'operazione sarà boolean.

GetDate: è l'operazione che restituisce la data del giorno in cui è stata effettuata la misura. E' stata dichiarata di tipo Date (dove Date è un oggetto che rappresenta la data in JAVA).

Le sottoclassi della classe Sensor, elencate in precedenza, hanno tutte uno slot di tipo float che rappresenta il dato misurato, e un'operazione startDevice che viene richiamata da WeatherStation per inizializzare il dispositivo. E' stata dichiarata di tipo void.

Il package descritto (Remote) contiene operazioni che si trovano sulla macchina remota, e saranno queste operazioni ad essere richiamate dalla WeatherStation per avere le informazioni sui dati misurati. Come accennato precedentemente, nasce la necessità di un collegamento tra le macchine, quindi la macchina locale (dove risiede WeatherStation) dovrà essere in grado di utilizzare tali operazioni, per cui avrà bisogno di un'interfaccia che le descriva. A questo punto passiamo all'analisi del secondo package che abbiamo battezzato Local.

Partiamo dall'analisi della classe WeatherStation; descriviamo slots e operazioni:

LogFile: è un attributo. E' il file dove vengono registrate le misure pervenute dai dispositivi remoti. LogFile è un'istanza della classe FileOutputStream, definita in JAVA.

fileOpened: è un attributo che ha lo scopo di verificare se in fase di scrittura il file è già stato aperto oppure deve essere ancora aperto. E' una variabile di attributo. E' stata dichiarata di tipo boolean.

transmission: è l'operazione che permette a WeatherStation di prelevare il dato misurato una volta che è stato attivato un dispositivo. L'operazione è esportabile perché deve essere richiamata dal dispositivo che effettua la misura. Il tipo restituito è Measurement.

La classe WeatherStation dovrà essere in grado di richiamare le operazioni che si trovano nella macchina remota, quindi dovrà essere collegata ad una interfaccia. Le classi dell'interfaccia sono implementate dalle classi del package remote e saranno perciò caratterizzate da uno stereotipo < < interface > > .

Analizziamo ora la molteplicità per WeatherStation. Sappiamo che una stazione di controllo può ricevere dati da diversi dispositivi disposti sul territorio regionale, quindi abbiamo un'unica WeatherStation mentre ci saranno in generale n Stub collegati: la molteplicità sarà di 1 a n.

All'interno dello stesso package sono presenti anche le classi che permettono le operazioni di creare l'AverageFile e di gestire l'accesso a questo da parte degli utenti.

Analizziamo la classe ElaborationCenter: questa è la classe che si occupa proprio di creare l'AverageFile. Questa classe è in collegamento con WeatherStation e sarà un collegamento 1 a n poiché abbiamo supposto che possono esistere vari centri di elaborazione che possono accedere ai dati ed elaborarli. Analizziamo ora i metodi e gli slots:

code: è sempre il codice del dispositivo che abbiamo detto deve essere unico. E' stato dichiarato di tipo String.

place: rappresenta il luogo dove si trova il dispositivo ed è stato dichiarato di tipo String.

LogFile: indica il file in lettura che contiene i dati misurati. Poiché stavolta il file è in lettura è stato dichiarato di tipo FileInputStream, una classe di JAVA che contiene le operazioni di lettura.

AverageFile: rappresenta il file in cui vengono registrati dati elaborati. Essendo un file in scrittura è stato dichiarato di tipo FileOutputStream.

Elaboration: è l'operazione che effettua il calcolo dei valori massimi, minimi, medi e della deviazione standard per le misurazione di ogni dispositivo remoto. Rende un oggetto di tipoStorage.

Elaborate: è l'operazione che viene richiesta dall'esterno ed attiva la classe ElaborationCenter.

Abbiamo parlato di registrare i dati in un file chiamato LogFile. Il problema è che noi non vogliamo registrare un semplice dato di tipo float che rappresenta la misura effettuata ma vogliamo registrare degli oggetti che contengano tutte le informazioni utili per l'identificazione di quella misura come il codice del dispositivo che ha fatto la misura e la località. E' nata quindi la necessità di creare un'altra classe chiamata Measurement. Gli oggetti di questa classe istanziati nella classe WeatherStation permettono di registrare tutte le informazioni necessarie con la tecnica della serializzazione. Analizziamo ora questa classe.

code, place: indicano il codice e la località di appartenenza del dispositivo. Sono di tipo String.

info: indica il valore della misura da registrare nel file. E' stato dichiarato di tipo float.

status: è lo stato del dispositivo (attivo/ non attivo), il suo tipo è boolean.

type: rappresenta il tipo di dispositivo: termometro, barometro etc. E' stato dichiaratodi tipo int.

Come anticipato le informazioni che contiene questa classe sono informazioni utili per il riconoscimento dei dati in fase di lettura. La lettura avviene all'interno della classe ElaborationCenter, quindi anche in questa classe occorrerà istanziare degli oggetti di tipo Measurement che permettano la lettura delle informazioni registrate nel file. Le conseguenze sono due:

1) la classe Measurement è collegata sia alla classe WeatherStation, sia alla classe    ElaborationCenter,2) la classe Measurement deve contenere delle operazioni che restituiscano le informazioni    lette dal file.
Quindi le operazioni della classe Measurement sono :

getCode: restituisce, una volta che l'oggetto è stato letto dal file, il codice dello stesso. Il tipo restituito è String.

getPlace: restituisce la località del dispositivo remoto che ha fatto la misura. Il tipo restituito è String.

getData: restituisce il valore misurato. Il tipo restituito è float.

getStatus: restituisce lo stato dall'oggetto letto. Il tipo restituito è boolean.

getType: restituisce il tipo del dispositivo. Il tipo restituito è int.

Queste operazioni servono per effettuare i controlli sull'identità del dispositivo.

Il problema della creazione della classe Measurement per la registrazione del file LogFile, si è presentato anche per la registrazione dell'AverageFile. La classe in questo caso è stata chiamataStorage e le informazioni che permette di registrare sono relative non più al dato letto ma al dato elaborato e quindi consentirà di registrare la media e i valori max, min e deviazione standard, oltre al codice, località e tipo di dispositivo.

code: il codice. Tipo String.

place: la località. Tipo String.

Average: rappresenta la media elaborata dal centro di elaborazione. Il tipo è float.

Max, Min: sono i valori max e min elaborati sempre dal centro di elaborazione. Sono sempre di tipo float.

Stv: rappresenta la deviazione standard. Il tipo è float.

type: il tipo del dispositivo. Tipo int.

Quando i dati devono essere letti, perché ad esempio li si vuole fornire all'utente finale, occorrono delle operazioni che come nel caso di risorse, restituiscano i dati letti tramite l'oggetto.

getCode: restituisce il codice dall'oggetto letto. Il tipo restituito è String.

getPlace: restituisce la località dall'oggetto letto. Il tipo restituito è String.

getAverage: restituisce il valore della media. Il tipo restituito è float.

getMax: restituisce il valore max. Il tipo restituito è float.

getMin: restituisce il valore min. Il tipo restituito è float.

getDeviance: restituisce il valore della deviazione standard. Il tipo restituito è float.

getType: restituisce il tipo del dispositivo. Il valore restituito è int.

show: mostra i dati contenuti.

Si vedrà che la lettura dell'AverageFile sarà associata ad una classe chiamata AverageFileManager, quindi la classe Storage sarà collegata sia ad ElaborationCenter sia ad AverageFileManager. Il collegamento sarà sempre di 1 a 1.

Analizziamo ora la classe AverageFileManager che contiene le operazioni per la gestione dell'accesso all'AverageFile. La molteplicità abbiamo assunto essere di 1 a 1 tra l'AverageFileManager e l'ElaborationCenter, mentre abbiamo posto la molteplicità di 1 a n tra l'AverageFileManager e la classe User (classe che rappresenta l'utente che vuole accedere al file). In questo modo stiamo supponendo una struttura tale per cui esistano diversi centri di elaborazione che producono diversi file, e ad ognuno di questi file è associato un manager che gestisce l'accesso di più utenti.

Passiamo ora in rassegna anche gli slots e le operazioni di questa classe:

password: indica la password che un utente deve fornire per potere accedere all'AverageFile. Abbiamo assunto l'esistenza di una singola password comune per tutti gli utenti. E' stata dichiarata di tipo String.

AverageFile: indica l'AverageFile. Essendo in fase di lettura è stato dichiarato di tipoFileInputStream.

verifyPassword: è l'operazione che verifica la validità della password. Il tipo restituito è boolean.

GetData: è l'operazione che permette la lettura dei dati del file all'utente.

L'ultima classe rimasta da analizzare è la classe User che come detto in precedenza deverappresentare l'utente finale. La classe User non contiene operazioni perché è l'AverageFileManager che si occupa di fornire i dati elaborati dell'AverageFile, l'unico slot che contiene è il nome dello user che rappresenta un identificatore dello stesso ed è stato dichiarato di tipo String.

    

Component view.

La component view come già stato detto mostra il legame tra i diversi component del sistema, dove i component rappresentano i moduli software del sistema. E' importante sottolineare che ogni classe è associata a un component che può essere associato a un linguaggio. Questo permette la generazione del codice con uno specifico linguaggio. Naturalmente la fase di generazione del codice produce solo la dichiarazione delle classi, degli slots e dei metodi, specificando tutto ciò che è stato specificato nella fase di progetto come tipo degli attributi, valore restituito dai metodi e livello di protezione.

Per associare una classe ad un component si deve cliccare due volte sul component desiderato, a questo punto appare la finestra delle specification e scelta la voce Realize vengono mostrate tutte le classi che sono state create; per assegnare una classe ad un component si ci deve posizionare con il mouse su quella classe e cliccare il tasto destro del mouse, a questo punto appare una finestra dove è presente la voce Assign, cliccando qua sopra si assegna la classe al component, classe che verrà contrassegnata da una v rossa.

Passiamo ad analizzare i vari component :

WeatherStation: al component WeatherStation sono state assegnate le classi WeatherStation e Measurement. Questo component è stato posto in relazione diretta con il component Stub che rappresenta l'interfaccia.

Stub: come detto questo component raggruppa le classi relative all'interfaccia e dal grafico si nota la relazione denominata REALIZE (quella tratteggiata) che si utilizza per evidenziare l'implementazione delle classi. Le classi associate a questo component sono iAirTherm,iGroundTherm, iAnemom, iBarom e iRainGauge.

RemDevice: questo component invece non rappresenta le interfacce ma i dispositivi remoti reali. E' in relazione diretta con Stub per quanto detto nel paragrafo precedente. Le classi associate sono AirTherm, GroundTherm, Anemom, Barom, RainGauge.

DataManager: a questo component sono associate le classi che si occupano di elaborare i dati, registrarli e renderli disponibili per un utente. Le classi assegnate sono ElaborationCenter, Measurement, Storage e AverageFileManager.

User: questo component deve rappresentare l'utente finale quindi l'unica classe che gli è stata assegnata è la classe User ed è in relazione con il component DataManager in quanto è solo con le classi di questo che interagisce.

Deployment view.

In questa vista vengono specificati componenti hardware che devono far parte del nostra sistema. Si divide tra processor e device. Un processor è un componente hardware in grado di eseguire programmi, come ad esempio un PC, mentre un device è un componente hardware che svolge un ruolo passivo, come un hard disk, cioè un componente hardware che non è in grado di elaborare. Se analizziamo cosa serve al nostro sistema perchè possa funzionare correttamente ci rendiamo conto che possono essere utili due hard disk differenti dove registrare il LogFile e l'AverageFile. Prendiamo due H.D. differenti perchè non vogliamo che i due tipi di dati si trovino sulla stessa memoria di massa, preferiamo pensare di avere delle strutture dedicate per le due cose.

I processor presenti nel sistema, cioè le differenti macchine che servono per il perfetto funzionamento del dispositivo sono tre :

Local: con Local indichiamo il processor che contiene i processes WeatherStation e AverageFileManager. Sulla macchina denominata Local verranno quindi richiamate le operazioni remote (che si trovano su un processor chiamato Remote), e inoltre verranno effettuate le operazioni di elaborazione dei dati e della loro registrazione sui device LogFile e AverageFile (sono stati gli stessi nomi dei file per denotare i device per avere una vista comprensiva del diagramma). Risulta quindi che Local sarà in relazione sia con il device LogFile sia con AverageFile.

Remote: rappresenta il processor dove si trovano le implementazioni delle operazioni interfacciate con Local. Sarà quindi in relazione anch'esso con Local.

User: indica il processor dove si trova l'utente e quindi sarà collegato a Local.

Una vista globale del deployment view diagram porta quindi ad individuare tre macchine, che nel nostro caso sono sistemi Windows, dove devono essere installate le varie parti del software che verrà prodotto alla fine della parte di analisi. Abbiamo una macchina, Remote, che fornisce le operazioni per la determinazione delle misure e quindi l'esportazione di queste nellamacchina Local, nella quale oltre che a registrare queste informazioni in un file chiamato LogFile in una apposita memoria di massa, che abbiamo indicato con lo stesso nome, elabora i dati e crea un file con i dati elaborati in un'altra memoria di massa indicata sempre con lo stesso nome del file, AverageFile. A questo punto esiste un'altra macchina che è quella dell'utente dove viene richiesto di potere accedere ai dati elaborati.

per tornare all'indice

2.5 Diagrammi.

Si consiglia di scaricare il file .mdl per potere esaminare i diagrammi, vista l'impossibilitàdi visionarli al meglio a causa delle dimensioni di alcuni diagrammi

 per scaricare il file .mdl

Use Case View: Main

Logical View: Main

Logical View: Remote

Logical View: Local

Component View: Main

Collaboration Diagram: AverageFileAccess

Collaboration Diagram: Transmission

Collaboration Diagram: UpdateAverage

Sequence Diagram: AverageFileAccess

Sequence Diagram: Transmission

Sequence Diagram: UpdateAverage

Deployment Diagram:

 per tornare all'indice

2.6 Conclusioni.

Da un seminario tenuto al CRS4 (Centro di Ricerca, Sviluppo e Studi Superiori in Sardegna), dalla dottoressa Maria Grazia Setzu abbiamo constatato l'utilità di questo tool per la progettazione disistemi object oriented, soprattutto quando le classi sono numerose. L'utilità non si ha solo in fase di progettazione ma anche per la documentazione fornita, utilissima durante la fase di manutenzione soprattutto se chi se ne occupa non ha preso parte al progetto.

per tornare all'indice

2.7 Bibliografia.

Tutorial U. M. L.: disponibile all'interno del tool e nel sito della Rational.

Filmati sull'uso dell'U. M. L.: disponibili nel sito della Rational.

Altri riferimenti

Fowler, U. M. L. Distilled , McGraw Hill
Sito della Rational:

www.rational.com/ROSE

da questo sito è possibile scaricare anche la versione evaluation (durata di 30 giorni) di Rational Rose 98.

Contattando la Object Way s. p .a. al numero verde

167-051060

è possibile ricevere gratuitamente un cd-rom con la versione evaluation del Rational Rose 98.

per tornare all'indice

Remote Method Invocation


3.1 Introduzione al Remote Method Invocation.

L'R. M. I. è una tecnica di JAVA con cui un utente può fare riferimento ad oggetti che si trovano su una macchina remota. Si supponga di avere un programma che riceva dei dati in ingresso e poi mostri sul monitor un'elaborazione degli stessi, e supponiamo ancora che il programma nella macchina locale non disponga di un metodo per l'elaborazione ma sia in grado di leggere i dati, di mostrarli ma non di elaborarli. Se lavoriamo con applicazioni object oriented, è possibile che il metodo per l'elaborazione viva ad esempio su un'altra macchina con cui lavora il sistema; a questo punto invece di avere gli stessi metodi per le due applicazioni sulle diverse macchine, è possibile usufruire delle potenzialità dell'RMI e usare il metodo remoto come se questo fosse locale.

Facciamo una distinzione tra client e server; con server si intende l'applicazione che rende disponibile per l'invocazione i metodi, mentre il client è l'applicazione che li utilizza.

Un'importante limitazione dell'RMI è che si può lavorare solo quando entrambi client e server sono applicazioni JAVA. Da questo punto di vista possiamo dire che è più generale l'architettura CORBA, essendo indipendente dal linguaggio di applicazione.

Grande importanza nell'RMI ricoprono le interfacce. Un'interfaccia è una classe con la dichiarazione dei metodi che possono essere esportati; si parla di dichiarazione perché i metodi vengono dichiarati ma non implementati. Un'interfaccia ha quindi lo scopo di mostrare quali sono i metodi invocabili remotamente. Si noti che in un'interfaccia non siamo costretti a dichiarare tutti i metodi di una classe, ma solo quelli che devono essere esportati. L'interfaccia deve essere associata alla classe di cui rende invocabili i metodi e perciò ogni oggetto remoto implementa l'interfaccia remota corrispondente. Ad esempio supponiamo di avere un'interfaccia A e la classe che la implementa, implA:

	// dichiarazione dell'interfaccia.	import java.rmi.*;	public interface A extends Remote  {		float metodo_uno( ) throws RemoteException;		boolean metodo_due( ) throws RemoteException;	}// definizione dell'oggetto remoto che implementa l'interfaccia A.    import java.rmi.*;      import java.rmi.registry.*;   import java.rmi.server.*;   public class implA extends UnicastRemoteObject implements A {   public implA () throws RemoteException { } // costruttore   public float metodo_uno( ) throws RemoteException {          ...............................         // implementazione del metodo   }   public boolean metodo_due( ) throws RemoteException {          ................................         //implementazione del metodo}public static void main (String args[]){          ………………………………………………         //main}

Elenchiamo i diversi passi per creare un'applicazione RMI:

Creare una interfaccia che estende l'interfaccia java.rmi.Remote. Questa interfaccia definisce i metodi esportabili che l'oggetto remoto implementa. Ogni metodo in questa interfaccia deve dichiarare nella clausola throws, java.rmi.RemoteException, che è la superclasse di più specifiche classi RMI per la gestione delle eccezioni. Si ha la necessità di fare questo per gestire le eccezioni che potrebbero presentarsi durante l'invocazione dei metodi.

Definire una sottoclasse di java.rmi.server.UnicastRemoteObject che implementa l'interfaccia Remote.

Scrivere un programma che crea un'istanza dell'oggetto remoto. A questo punto si deve esportare l'oggetto rendendolo disponibile tramite un registro di servizio. Questo può essere fatto sia con rmiregistry (un programma di JAVA) sia usando la classe LocateRegistry, che a nostro avviso è più utile perché permette di creare ed esportare un registro all'interno del programma, invece di dovere richiamare l'rmiregistry dal prompt del DOS, ogni volta che si testa l'applicazione.

Si faccia riferimento alla dichiarazione del main precedente:
	public static void main (String args[]) {	implA  Aobj;				try { 		// crea ed esporta un registro sull'host locale sulla porta specificata.				 LocateRegistry.createRegistry(1099);		// istanzia l'oggetto				Aobj = new implA( );		// lega l'oggetto appena creato al registro tramite un nome che servirà  		//per riferimento. Un oggetto remoto può essere collegato a qualsiasi 		// nome ne registro, ma per evitare conflitti tra i nomi è conveniente  		// utilizzare il nome del package che si esporta nel registro.				Naming.bind(“rmi:///implA”, Aobj);				} catch (Exception e) {					e.printStackTrace( )              }}il registro alla porta 1099 è quello utilizzato per i servizi.

Dopo aver compilato il programma server con javac, si deve usare il compilatore rmic (rmi compiler) per generare uno stub ed uno skeleton (questi verranno discussi più avanti) per l'oggetto remoto. Con l'RMI il client ed il server non comunicano direttamente. Per la comunicazione occorrono lo stub e lo skeleton creati a partire dalla classe che implementa l'oggetto remoto tramite l'rmic.

Una volta scritto il programma del lato client, occorre scrivere il programma del lato server che faccia riferimento ad un oggetto remoto sul lato server. L'applicazione si differenzia da una che non utilizza oggetti remoti solo quando occorre fare la ricerca dell'oggetto remoto. Questa si fa utilizzando la classe Naming; vediamo come

	import java.rmi.*;	public class sideClient {	public sideClient( ) { }       // costruttore	public static void main (String args[]){  	A    remoteObject;  	try  		{	     // fa il lookup dell'oggetto, cioè cerca l'oggetto con riferimento implA nel registro.	 remoteObject=(A)Naming.lookup(“rmi://nomeMacchinaServer/implA”);     // vengono utilizzati i due metodi resi esportabili.	 remoteObject.metodo_uno( );	 remoteObject.metodo_due( );	 }catch (Exception e) { e.printStackTrace( );}   }
per tornare all'indice

3.2 Architettura.

Il sistema RMI è costituito da tre strati: stub/skeleton layer, remote reference layer e transport layer.

Lo stub/skeleton layer è l'interfaccia tra l'applicazione e il resto del sistema RMI , il suo compito è quello di trasmettere i dati al reference remote layer tramite MARSHALL STREAMS. Marshall streams utilizza il meccanismo della serializzazione degli oggetti con il quale è in grado di trasmettere gli oggetti.

Quando un oggetto è passato a un metodo o è restituito da un metodo, ciò che viene realmente trasmesso è il riferimento all'oggetto. La macchina remota non può leggere cosa c'è nella memoria della macchina locale. Un riferimento valido per una macchina non ha significato per un'altra. Esistono due metodi per aggirare il problema. Si può passare uno speciale riferimento remoto all'oggetto, un riferimento che punta alla memoria della macchina remota, o si puòpassare una copia dell'oggetto. Quando la macchina remota passa un oggetto alla macchina locale, passa un riferimento remoto. Tuttavia quando la macchina locale passa un oggetto alla macchina remota, fa una copia dell'oggetto e invia la copia. La copia viene inviata dalla macchina locale alla macchina remota.

Per copiare un oggetto bisogna convertire l'oggetto in un flusso di bytes; questo è particolarmente difficile perché gli oggetti possono includere altri oggetti, che vanno anch'essi copiati durante la copia dell'oggetto principale.

La serializzazione degli oggetti è un meccanismo per convertire oggetti in flussi di bytes che possono essere passati ad un'altra macchina, la quale ricostruirà l'oggetto a partire dal flusso di bytes.

Lo stub e lo skeleton si occupano delle stesse cose, ma mentre il primo lo fa per il client il secondo lo fa per il server. Un client quando invoca un metodo remoto, lo fa localmente allo stub. Lo stub è un'implementazione di un'interfaccia remota dell'oggetto remoto ed è lui che spedisce l'invocazione richiesta all'oggetto server tramite il remote reference layer. Il reference remote layer è responsabile di capire a che tipo di oggetto si fa riferimento, cioè deve essere in grado di capire se il server è un singolo oggetto oppure è un oggetto replicato e quindi richiede comunicazioni multiple. Il transport layer è invece responsabile di settare la connessione e tenere una traccia degli oggetti remoti da inviare. Quindi la chiamata passa allo skeleton che la passa alla implementazione dell'oggetto remoto che fornisce il metodo.

per tornare all'indice

3.3 Caricamento dinamico delle classi.

I file stub e skeleton, vengono prodotti dal compilatore rmic nella macchina server, ma una copia del file stub deve essere presente nella macchina client per il corretto funzionamento del sistema. Esistono due alternative, o si copia il file a mano oppure si utilizza l'RMIClassLoader. L'RMIClassLoader è una classe utilizzata per prelevare una classe specifica e tutte quelle utilizzate all'interno di questa. Con questa soluzione sarà quindi il client che si collegherà al server e preleverà le classi interessate. Il caricatore di classe segue un percorso ben definito per prelevare la classe: per primo cerca il file di classe nelle locazioni definite nel CLASSPATH, quindi nell'URL definito dalla proprietà java.rmi.server.codebase. A questo punto per caricare una classe dinamicamente basta aggiungere le seguenti righe di codice al client:

	System.setSecurityManager(new RMISecurityManager( ));	System.getProperties( ).put(“java.rmi.server.codebase”,http://pcdida2/classes”);
La prima riga serve per attivare un gestore di sicurezza, questo perché la classe viene caricata tramite la rete. Per quanto riguarda la seconda riga, invece, l'RMIClassLoader cerca la classe nel CLASSPATH, nell'URL codificato dell'oggetto, e quindi non trovandolo il posto in cui cerca l'URL è la proprietà java.rmi.server.codebase. Il file di classe si deve chiaramente trovare nella directory specificata nell'indirizzo.

per tornare all'indice

3.4 Descrizione dei file .class e .java.

Nel nostro sistema abbiamo due lati, il lato server e il lato client; questo paragrafo vuole essere una semplice guida per la comprensione dei file al momento dell'installazione del sistema. Ricordiamo che abbiamo a che fare con un sistema ad oggetti distribuiti in cui il lato server può trovarsi su più macchine, cioè ognuno degli oggetti AirTherm, GroundTherm, Anemom, Barom e RainGauge, può trovarsi su una macchina diversa. Segue la necessità che su ogni macchina siano presenti le interfacce corrette per il funzionamento del sistema.

I file che saranno presenti al momento dell'installazione sulla macchina client saranno:

	WS.java: implementazione lato client	intWS.java: interfaccia di WeatherStation	        Stub.java: interfaccia di Sensor	iAirTherm.java: interfaccia di AirTherm.java		iGroundTherm.java: interfaccia di GroundTherm.java	iAnemom.java: interfaccia di Anemom.java	iBarom.java: interfaccia di Barom.java	iRainGauge.java: interfaccia di RainGauge.java	AverageFileManager.java: implementazione di average file manager	User.java: implementazione della classe User			ElaborationCenter.java: implementazione della classe ElaborationCenter
Al momento della compilazione

c:\client\>javac WS.java
saranno presenti i file .class di tutti i file sopraelencati, esclusi AverageFileManager e User, cioè il compilatore non si limita a compilare solo il file .java a cui ci riferiamo ma tutti i file riferiti al suo interno.

Nella macchina server (per semplicità ci riferiamo solo ad AirTherm) i file presenti saranno:

	AirTherm.java: implementazione della classe AirTherm	iAirTherm.java: interfaccia della classe AirTherm	Sensor.java: implementazione della classe Sensor	Stub.java: interfaccia della classe Sensor	intWS.java: interfaccia di WeatherStation	Anche dal lato server dobbiamo inizialmente compilare il file AirTherm

c:\server>javac AirTherm.java
A questo punto avremmo sia i file .java che .class perché il compilatore provvederà a compilare tutti le classi riferite all'interno di AirTherm. A questo punto bisogna creare i file stub con l'rmic (rmi compile):

c:\server>rmic AirTherm
I file che verranno creati sono

					AirTherm_Stub.class					AirTherm_Skel.class
Sarà l'RMIClassLoader a caricare il file _Stub anche sul lato client.

per tornare all'indice

3.5 Listati

Classe AirTherm.java

  ///////////////////////////////////////////////Implementazione del termometro dell'aria/////////////////////////////////////////////import java.rmi.*;import java.rmi.server.*;import java.rmi.registry.LocateRegistry;import java.util.*;public class AirTherm extends Sensor implements iAirTherm, Runnable {  private Random rand=new Random();  private Thread notifier=null;  private intWS myclient;  float Temp;  int type=1;   public AirTherm() throws RemoteException{   //costruttore	code="002";	place="Oristano";	TimeInterval=10000;	Temp=18;  }  // statDevice viene richiamato dal centro di controllo attraverso l'RMIpublic synchronyzed void startDevice(intWS client) throws RemoteException{	myclient=client;	if (notifier==null) {		notifier=new Thread(this);		notifier.start();	}}  // run viene attivato al momento dell'istanziazione di un oggetto di tipo AirTherm// e gestisce la vita e la sospensione del threadpublic void run() {	while(true){  	// interrompe per TimeIntervall millisecondi l'esecuzione	try{ Thread.currntThread().sleep(TimeInterval);}	catch(InterruptedException e){}  			// aggiorna casualmente la temperatura	float newTemp=Temp+(rand.nextFloat()%4);	if (newTemp!=Temp){	  Temp=newTemp;	  Measurement tempMeasure=new Measurement(code,place,Temp,Status,type);	  try{	   System.out.println("---------Trasmissione nuova temperatura-------");	   myclient.transmission(tempMeasure);     	  //effettua una chiamata al metodo remoto transmission, fornito dal centro 	  //di controllo	 } catch(RemoteExceptione){      }    }  }}			      	public static void main(String args[]){ try{   System.out.println("------------------Attivazione--Airtherm-----------------------");   LocateRegistry.createRegistry(1099);  // associa i metodi esportabili ad un nome nel registro. 1099 è la porta prevista per // comunicazioni RMI   iAirTherm server=new AirTherm();   Naming.rebind("///AirTherm",server);   //viene effettuato il collegamento   (bind)   System.out.println("-----------------------Pronto per RMI----------------------------"); }catch(Exceptione ){e.printStackTrace();}}			}

Classe iAirTherm.java

  ////////////////////////////////////////Interfaccia della classe AirTherm//////////////////////////////////////import java.rmi.*;public interface iAirTherm Extends Remote {    // interfaccia del metodo esportabile per AirTherm void startDevice(intWS client) throws RemoteException;}

Classe Anemom.java

  //////////////////////////////////////Implementazione dell'anemometro////////////////////////////////////import java.rmi.*;import java.rmi.server.*;import java.rmi.registry.LocateRegistry;import java.util.*;public class Anemom extends Sensor implements iAnemom, Runnable {  private Random rand=new Random();  private Thread notifier=null;  private intWS myclient;  float Speed;  int type=4;   public Anemom() throws RemoteException{      //costruttore	code="004";	place="Sassari";	TimeInterval=10000;	Speed=80;  }  // statDevice viene richiamato dal centro di controllo attraverso l'RMIpublic synchronyzed void startDevice(intWS client) throws RemoteException{	myclient=client;	if (notifier==null) {		notifier=new Thread(this);		notifier.start();	}}  // run viene attivato al momento dell'istanziazione di un oggetto di tipo Anemom// e gestisce la vita e la sospensione del threadpublic void run() {	while(true){  	// interrompe per TimeIntervall millisecondi l'esecuzione	   try{ Thread.currntThread().sleep(TimeInterval);}	   catch(InterruptedException e){}  			   // aggiorna casualmente la velocità	   float newSpeed=Speed+(rand.nextFloat()%4);	   if (newSpeed!=Speed){		Speed=newSpeed;		Measurement	tempMeasure=new Measurement(code,place,Speed,Status,type);		try{	       System.out.println("---------Trasmissione nuova velocità del vento-------");	       myclient.transmission(speedMeasure);     		//effettua una chiamata al metodo remoto transmission, fornito dal centro 		//di controllo      	} catch(RemoteExceptione){		 }        }  }}			      	public static void main(String args[]){ try{   System.out.println("------------------Attivazione--Anemom-----------------------");   LocateRegistry.createRegistry(1099);  // associa i metodi esportabili ad un nome nel registro. 1099 è la porta prevista per // comunicazioni RMI   iAnemom server=new Anemom();   Naming.rebind("///Anemom",server);   //viene effettuato il collegamento	   System.out.println("-------------------------Pronto per RMI----------------------------"); }catch(Exceptione ){e.printStackTrace();}}			}

Classe iAnemom.java

  ////////////////////////////////////////Interfaccia della classe Anemom//////////////////////////////////////import java.rmi.*;public interface iAnemom Extends Remote {    // interfaccia del metodo esportabile di Anemom void startDevice(intWS client) throws RemoteException;}

Classe Barom.java

  ////////////////////////////////////Implementazione del barometro//////////////////////////////////import java.rmi.*;import java.rmi.server.*;import java.rmi.registry.LocateRegistry;import java.util.*;public class Barom extends Sensor implements iBarom, Runnable {  private Random rand=new Random();  private Thread notifier=null;  private intWS myclient;  float Press;    // dato di interesse per la memorizzazione  int type=2;     // dato identificativo per il dispositivo   public Barom() throws RemoteException{      //costruttore	code="001";	place="Olbia";	TimeInterval=10000;	Press=120;  }  // statDevice viene richiamato dal centro di controllo attraverso l'RMIpublic synchronyzed void startDevice(intWS client) throws RemoteException{	myclient=client;	if (notifier==null) {		notifier=new Thread(this);		notifier.start();	}}  // run viene attivato al momento dell'istanziazione di un oggetto di tipo Anemom// e gestisce la vita e la sospensione del threadpublic void run() {	while(true){  	// interrompe per TimeIntervall millisecondi l'esecuzione	 try{ Thread.currntThread().sleep(TimeInterval);}	 catch(InterruptedException e){}  			 // generazione casuale di una nuova pressione	 float newPress=Press+(rand.nextFloat()%4);	 if (newPress!=Press){	   Press=newPress;	   Measurement tempMeasure=new Measurement(code,place,Press,Status,type);	   try{	 	System.out.println("---------Trasmissione nuova pressione---------");		myclient.transmission(pressMeasure);     		//effettua una chiamata al metodo remoto transmission, fornito dal centro 	 	//di controllo         } catch(RemoteExceptione){	 }      }  }}			      	public static void main(String args[]){ try{  System.out.println("------------------Attivazione--Barom-----------------------");  LocateRegistry.createRegistry(1099);  // associa i metodi esportabili ad un nome nel registro. 1099 è la porta prevista per // comunicazioni RMI  iBarom server=new Barom();  Naming.rebind("///Barom",server);   //viene effettuato il collegamento	  System.out.println("-------------------------Pronto per RMI----------------------------"); }catch(Exceptione ){e.printStackTrace();}}			}

Classe iBarom.java

  /////////////////////////////////////Interfaccia della classe Barom///////////////////////////////////import java.rmi.*;public interface iBarom Extends Remote {    // interfaccia del metodo esportabile di Barom void startDevice(intWS client) throws RemoteException;}

Classe GroundTherm.java

  ////////////////////////////////////////////////Implementazione del temometro del terreno//////////////////////////////////////////////import java.rmi.*;import java.rmi.server.*;import java.rmi.registry.LocateRegistry;import java.util.*;public class GroundTherm extends Sensor implements iGroundTherm, Runnable {  private Random rand=new Random();  private Thread notifier=null;  private intWS myclient;  float Temp;  // dato di interesse per la memorizzazione  int type=3;  // dato identificativo per il dispositivo   public Barom() throws RemoteException{      //costruttore	code="003";	place="Sassari";	TimeInterval=10000;	Temp=40;  }  // statDevice viene richiamato dal centro di controllo attraverso l'RMIpublic synchronyzed void startDevice(intWS client) throws RemoteException{	myclient=client;	if (notifier==null) {		notifier=new Thread(this);		notifier.start();	}}  // run viene attivato al momento dell'istanziazione di un oggetto di tipo Anemom// e gestisce la vita e la sospensione del threadpublic void run() {	while(true){  	// interrompe per TimeIntervall millisecondi l'esecuzione	 try{ Thread.currntThread().sleep(TimeInterval);}	 catch(InterruptedException e){}  			// generazione casuale di una nuova pressione	float newTemp=temp+(rand.nextFloat()%4);	 if (newTemp!=Temp){	  Temp=newTemp;        Measurement tempMeasure=new Measurement(code,place,Temp,Status,type);	 try{	  System.out.println("---------Trasmissione nuova temperatura---------");	  myclient.transmission(tempMeasure);     	 //effettua una chiamata al metodo remoto transmission, fornito dal centro 		       //di controllo	} catch(RemoteExceptione){    }   }  }}			      	public static void main(String args[]){ try{  System.out.println("------------------Attivazione--GroundTherm-----------------------");  LocateRegistry.createRegistry(1099);  // associa i metodi esportabili ad un nome nel registro. 1099 è la porta prevista per // comunicazioni RMI  iGroundTherm server=new GroundTherm();  Naming.rebind("///GroundTherm",server);   //viene effettuato il collegamento	  System.out.println("-------------------------Pronto per RMI----------------------------"); }catch(Exceptione ){e.printStackTrace();}}			}

Classe iGroundTherm.java

  ///////////////////////////////////////////Interfaccia della classe GroundTherm/////////////////////////////////////////import java.rmi.*;public interface iGroundTherm Extends Remote {    // interfaccia del metodo esportabile di GroundTherm void startDevice(intWS client) throws RemoteException;}

Classe RainGauge.java

  ///////////////////////////////////////////////////////Implementazione del misuratore di precipitazioni/////////////////////////////////////////////////////import java.rmi.*;import java.rmi.server.*;import java.rmi.registry.LocateRegistry;import java.util.*;public class RainGauge extends Sensor implements iRainGauge, Runnable {  private Random rand=new Random();  private Thread notifier=null;  private intWS myclient;  float Rain;  int type=3;   public RainGauge() throws RemoteException{      //costruttore	code="005";	place="Nuoro";	TimeInterval=10000;	Rain=10;  }  // statDevice viene richiamato dal centro di controllo attraverso l'RMIpublic synchronyzed void startDevice(intWS client) throws RemoteException{	myclient=client;	if (notifier==null) {		notifier=new Thread(this);		notifier.start();	}}  // run viene attivato al momento dell'istanziazione di un oggetto di tipo RainGauge// e gestisce la vita e la sospensione del threadpublic void run() {	while(true){  	// interrompe per TimeIntervall millisecondi l'esecuzione	 try{ Thread.currentThread().sleep(TimeInterval);}	  catch(InterruptedException e){}  			  // aggiorna casualmente la precipitazione	  float newRain=Rain+(rand.nextFloat()%4);	  if (newRain!=Rain){	   Rain=newRain;	   Measurement tempMeasure=new Measurement(code,place,Rain,Status,type);	   try{	    System.out.println("---------Trasmissione nuova precipitazione-------");	    myclient.transmission(RainMeasure);     	   //effettua una chiamata al metodo remoto transmission, fornito dal centro          //di controllo	  } catch(RemoteExceptione){	}     }  }}			      	public static void main(String args[]){ try{  System.out.println("------------------Attivazione--RainGauge-----------------------");  LocateRegistry.createRegistry(1099);  // associa i metodi esportabili ad un nome nel registro. 1099 è la porta prevista per // comunicazioni RMI iRainGauge server=new RainGauge(); Naming.rebind("///RainGauge",server);   //viene effettuato il collegamento (bind) System.out.println("-------------------------Pronto per RMI----------------------------"); }catch(Exceptione ){e.printStackTrace();}}			}

Classe iRainGauge.java

  /////////////////////////////////////////Interfaccia della classe RainGauge///////////////////////////////////////import java.rmi.*;public interface iRainGauge Extends Remote {    // interfaccia del metodo esportabile di RainGauge void startDevice(intWS client) throws RemoteException;}

Classe AverageFileManager.java

  //////////////////////////////////////////////////////////////Implementazione del gestore del file dei dati elaborati////////////////////////////////////////////////////////////import java.io.*;import java.lang.*;class AverageFileManager{    private String password;  private FileInputStream AverageFile;  AverageFileManager()         // costruttore: definisce la password	{password="123"	;}  public boolean verifyPassword(String str)   {return str.equals(password);}    // getData restituisce un oggetto di tipo storage, contenente tutti  // i dati richiesti  public Storage getData(String place, String code, int devType, boolean b[]) {	Storage stor=new Storage();	System.out.println("  ");	try{  	   //apre in lettura un file in grado di gestire la serializzazione di un oggetto         FileInputStream fis=new FileInputStream("AverageFile");	   ObjectInputStream in=new ObjectInputStream(fis);	   System.out.println("File aperto in lettura...");	   while(true) {				stor=(Storage)in.readObject();  	 // verifica che l'oggetto letto sia quello richiesto	        		if ((code.equals(stor.getCode()))&&(place.equals(stor.getPlace())))			{return stor;}	   }	} catch(IOException e) {System.out.println(e);}	  catch(Exception d) {System.out.println("---------------Eccezione----------------");}	return null;   }}

Classe ElaborationCenter.java

  ////////////////////////////////////////////////////////////////////Implementazione del centro di elaborazione dati metereologici//////////////////////////////////////////////////////////////////import java.io.*;import java.lang.*;import java.util.*;public class ElaborationCenter{ private String code; private String place; private FileInputStream LogFile; private FileOutputStream AverageFile; Stack codeStack=new Stack(); public ElaborationCenter() {     // costruttore	code=" ";	place=" "; }  // Elaboration effettua tutti i calcoli richiesti // e rende un oggetto storage che li contiene private Storage Elaboration(String placein, String codein) {	int i=0;	float sum=0;	float max=0;	float min=10000;	float temp=0;	float av=0;	float stv=0;	int devtype=1;	try{  	// apre in lettura il file dei dati immagazzinati	 FileInputStream fis=new FileInputStream("LogFile");	 ObjectInputStream in=new ObjectInputStream(fis);	 Measurement measure=new Measurement();	 while(true) {		measure=(measurement)in.readObject();  	 // effettua i calcoli per il dispositivo richiesto				if((codein.equals(measure.getCode()))&&(placein.equals(measure.getPlace()))) {			temp=measure.getData();			sum=sum+(float)(temp);			i++;			if (temp > max)	max=temp;			if (temp < min) min=temp;			devtype=measure.getType();		}	 }	} catch(IOException e){}	  catch(Exception d) {System.out.println("------------------Eccezione"---------------");}	av=(sum/i);	Storage stor=new Storage(placein,codein,av,max,min,stv,devtype);	return stor;      }  // Elaborate gestisce i dati elaborati e li immagazzina nel file AverageFilepublic void Elaborate() {	float av,ma,mi,dev=0;	int type;	String placex,codex;	try{		FileInputStream fis=new FileInputStream("LogFile");		ObjectInputStream in=new ObjectInputStream(fis);		Measurement measure=new Measurement();		while (true) {			measure=(Measurement)in.readObject();			if (codeStack.contains(measure.getCode())==false) {				codeStack.push(measure.getCode());				codeStack.push(measure.getPlace());			}		}	}catch (IOExceptione) { }	 catch (Exception d) {System.out.println("---------------Eccezione----------------");}	try{  		// apertura del file in scrittura		FileOutputStream fos1=new FileOutputStream("AverageFile");		ObjectOutputStream out1=new ObjectOutputStream(fos1);				while (codeStack.empty()==false) {			placex=(String)codeStack.pop();			codex=(String)codeStack.pop();			Storage stor=Elaboration(placex,codex);			out1.writeObject(stor);			out1.flush();		}		out.close();	}catch (IOException e) { }	catch (Exception d) {System.out.println("-------------------Eccezione----------------");}     }}	

Classe Measurement.java

  /////////////////////////////////////////////////////////////////////////Classe Measurement utilizzata per immagazzinare i dati nel LogFile///////////////////////////////////////////////////////////////////////import java.io.*;import java.util.Date;public class Measurement implements Serializable {	String code, place;	float info;	boolean status;	int type;  // costruttori	public Measurement() {}	public Measurement (String code, String place, float info, boolean status, int type) {		this.code=code;		this.place=place;		this.info=info;		this.status=status;		this.type=type;	}		public String getCode() {return code;}		public float getData() {return info;}	public int getType() {return type;}	public String getPlace() {return place;}	public boolean getStatus() {return status;}}

Classe Sensor.java

  //////////////////////////////////////////Implementazione della classe Sensor////////////////////////////////////////import java.util.Date;import java.rmi;import java.rmi.registry.*;import java.rmi.server.*;public class Sensor extends UnicastRemoteObject implements Stub {	protected boolean Status;	protected String palce, code;	protected int TimeInterval;  	// definizione dei costruttori	public Sensor() throws RemoteException{}	public Sensor(boolean Status, String code, String place, int TimeInterval) throws									RemoteException{		this.Status=Status;		this.code=code;		this.place=place;		this.TimeInterval=TimeInterval;	}  	// restituisce lo stato del dispositivo	public boolean getStatus() throws RemoteException {return Status;}  	// resituisce il codice del dispositivo	public String getCode() throws RemoteException {return code;}  	// resituisce l'ora corrente	public Date getDate() throws RemoteException {return new Date();}}

Classe Stub.java

  ////////////////////////////////////////Implementazione della classe Stub//////////////////////////////////////import java.rmi.*;import java.util.Date;public interface Stub extends Remote {	public boolean getStatus() throws RemoteException;	public Date getDate() throws RemoteException;}

Classe Storage.java

  ///////////////////////////////////////////////////////////////////////////Classe Storage utilizzata per immaggazzinare i dati nell'AverageFile/////////////////////////////////////////////////////////////////////////import java.io.*;public class Storage implements Serializable {	private String place, code;	private float av;	private float max;	private float min;	private float stv;	private int type;  	// costruttori    public Storage() {	av=max=min=stv=type=0;    }    public Storage (String place,String code,float av,float max,float min,float stv,int type){	this.place=place;	this.code=code;	this.av=av;	this.max=max;	this.min=min;	this.stv=stv;	this.type=type;    }   public String getCode() {return code;}   public String getPlace() {return place;}   public float getAverage() {return av;}	   public float getMax() {return max;}   public float getMin() {return min;}   public float getDeviance() {return stv;}   public int getType() {return type;}   public void show(boolean b[]) {	System.out.println("	Code:"+ code);	if (b[0]) System.out.println("	Max:"+ max);	if (b[1]) System.out.println("	Min:"+ min);	if (b[2]) System.out.println("	Average:"+ av);	if (b[3]) System.out.println("	Standard Deviation:"+stv);   }}

Classe WeatherStation.java

  ///////////////////////////////////////////////////Implementazione della classe Weather Station/////////////////////////////////////////////////import java.awt.*;import java.net.URL;import java.rmi.*;import java.rmi.server.*;import java.io.*;import java.util.Date;public class WeatherStation implements intWS{		boolean fileOpened=false;	FileOutputStream fos=null;	ObjectOutputStream out=null;  	// costruttore	public WeatherStation() {		try{			FileOutputStream fos=new FileOutputStream("LogFile");			ObjectOutputStream out=new ObjectOutputStream(fos);		} catch(IOException e ){System.out.println(e);}	}	public void transmission(Measurement newMeasure){		try{  		// il file deve essere aperto una sola volta in scrittura. Per verificare questo	// viene valutata una variabile fileOpened inizialmente posta a false.		 if (fileOpened==false){				fos=new FileOutputStream("LogFile");				out=new ObjectOutputStream(fos);				fileOpened=true;			}		  System.out.println(newMeasure.getData());  		  // scrive l'oggetto nel file		  out.writeObject(new Measure);		  Date date=new Date();		  System.out.println("--Oggetto scritto alle ore "+			 (int)(date.getHours()+2":"+date.getMinutes()+"."date.getSeconds());		}catch(IOException e){System.out.println(e);}		catch(Exception d) {System.out.println(d);}	}	public static void main (String args[]){			WeatherStation WS=new WeatherStation();	try{	 UnicastRemoteObject.exportObject(WS);  				  // caricamento dinamico delle classi	 System.setSecurityManager(new RMISecurityManager());	 System.getProperties().put("java.rmi.server.codebase","http://pcdida1/users/Andrea/");	 System.getProperties().put("java.rmi.server.codebase","http://pcdida2/users/Giacomo/");	 String serverName1="//pcdida1/Barom";	 String serverName2="//pcdida2/AirTherm";  	  	  // ricerca dei metodi sulle macchine server	  // per complettezza si dovrebbero inserire anche le altre misure	  // questo non è stato fatto per indisponibilità di macchine in laboratorio	  iBarom server1=(iBarom) Naming.lookup(serverName1);          iAirTherm server2=(iAirTherm) Naming.lookup(serverName2);	  server1.startDevice(WS);        server2.startDevice(WS);	}catch(Exception){e.printStackTrace();			  return;			 }	}}	  		

Classe intWS.java

  //////////////////////////////////////////Interfaccia del centro di controllo////////////////////////////////////////import java.rmi.*;public interface intWS extends Remote{  	//interfaccia del metodo esportabile di WeatherStation	void transmission(Measurement newMeasure) throws RemoteException;}

Classe User.java

  ///////////////////////////////////////Implementazione dell classe User/////////////////////////////////////import java.awt.*;import java.awt.event.*;public class User extends Frame {	private Button bottone1, bottone2;	private TextField password,palcetxt,codetxt,maxtxt,outmax;	private TextField outpass,outmin,outave,outdev;	private CheckboxGroup group1;	private Checkbox MAX,MIN,AVER,DEV,AT,GT,PR,WS,RG;	private Label code,place,select,select2,g,h,max,title;	private Label outmaxtxt,outmintxt,outavetxt,outdevtxt,outpasstxt;	private int LarghFin=700;	private int AltFin=300;	private boolean passwordChecked=false;	public User() {      // costruttore				setTitle("USER");		setLayout(new FlowLayout());		setFont(new Font("DialogInput",Font.BOLD,14));		setBackground(Color.lightGray);		setResizable(false);		DialogLayout DiaLay=new DialogLayout(this,700,300);		bottone1=new Button("Check Password");		bottone2=new Button("Show Data");		password=new TextField(" ");		password.setColumns(8);		password.setEchoCharacter('*');		outpass=new TextField(" ");		outpass.setColumns(30);		placetxt=new TextField(" ");		placetxt.setColumns(15);		outmax=new TextField(" ");		outmax.setColumns(8);		outmin=new TextField(" ");		outmin.setColumns(8);		outave=new TextField(" ");		outave.setColumns(8);		outdev=new TextField(" ");		outdev.setColumns(8);		MAX=new Checkbox("Max", false);		MIN=new Checkbox("Min", false);		AVER=new Checkbox("Average", false);		DEV=new Checkbox("Std Deviation", false);		g=new Label("		");		h=new Label("			");		code=new Label("Code",Label.LEFT);		place=new Label("Place",Label:LEFT);		select=new Label("Select info type:",Label.LEFT);		select2=new Label("Select device type:", Label.LEFT);		outmaxtxt=new Label("Max:",Label.LEFT);		outmintxt=new Label("Min:",Label.LEFT);		outavetxt=new Label("Average:",Label.LEFT);		outdevtxt=new Label("Std Deviance:",Label.LEFT);		outpasstxt=new Label("Messages:",Label.LEFT);		title=new Label("INGEGNERIA DEL SOFTWARE",Label.CENTER);		title.setFont(new Font("DialogInput",Font.BOLD,20));		title.setForeground(Color.yellow);		AT=new Checkbox("Air Temp",group1,false);		GT=new Checkbox("Ground Temp",group1,false);		PR=new Checkbox("Pressure",group1,false);		WS=new Checkbox("Wind Speed",group1,false);		RG=new Checkbox("Rain",group1,false);		codetxt=new TextField(" ");		codetxt.setColumns(6);		add(bottone1);		add(password);		add(bottone2);		add(place);		add(placetxt);		add(code);		add(codetxt);		add(select);		add(MAX);		add(MIN);		add(AVER);		add(DEV);		add(g);		add(select2);		add(AT);		add(GT);		add(PR);		add(WS);		add(RG);		add(outmaxtxt);		add(outmax);		add(outmintxt);		add(outmin);		add(outavetxt);		add(outave);		add(outdevtxt);		add(outdev);		add(outpasstxt);		add(outpass);		add(h);		add(title);		pack();		resize(LarghFin,AltFin);		show();	}	public boolean handleEvent(Event evt) {				switch(evt.id) {			case evt.WINDOW_DESTROY:				dispose();				System.exit(0);				return true;			}		if ("Check Password".equals(evt.arg)) {			AverageFileManager AFM=new AverageFileManager();			String passwd=password.getText();			if (AFM.verifyPassword(passwd)==true) {				passwordChecked=true;				outpass.setText("Password accepte");			}else {outpass.setText("Password not accepted");}		}		if ("Show data".equals(evt.arg)) {			if (passwordChecked==false) {outpass.setText("Insert password!");}			else {				ElaborationCenter EC=new ElaborationCenter();				EC.Elaborate();				String codereq,placereq;				AverageFileManager AFM=new AverageFileManager();				codereq=codetxt.getText();				placereq=placetxt.getText();								boolean[] Selected=new boolean[4];				Selected[0]=MAX.getState();				Selected[1]=MIN.getState();				Selected[2]=AVER.getState();				Selected[3]=DEV.getState();				int type=0;								if (AT.getState()) type=1;				if (GT.getState()) type=2;				if (PR.getState()) type=3;				if (WS.getState()) type=4;				if (RG.getState()) type=5;						Storage stor=AFM.getdata(placereq,codereq,type,Selected);						if (stor==null) {					outpass.setText("Unknow device!!");					outmax.setText(" ");					outmin.setText(" ");					outave.setText(" ");					oudev.setText(" ");				} else {					if (Selected[0]) {						Float MAX=new Float(stor.getMax());						outmax.setText(MAX.toString());					} else outmax.setText(" ");					if (Selected[1]) {						Float MIN=new Float(stor.getMin());						outmin.setText(MIN.toString());					} else outmin.setText(" ");					if (Selected[2]) {						Float AVE=new Float(stor.getAverage());						outave.setText(AVE.toString());					} else outave.setText(" ");					if (Selected[3]) {						Float DEV=new Float(stor.getDeviance());						outdev.setText(DEV.toString());					} else outdev.setText(" ");						outpass.setText(" ");								}		}	}		return false; }		public static void main(String args[]) {		User userwin=new User();	}}										
per tornare all'indice

3.6 Conclusioni.

La tecnica dell'RMI è una tecnica che pone il progettista ad un livello per cui non deve preoccuparsi dei problemi veri e propri della comunicazione tra le macchine, così come avviene con altre tecniche, quindi è come se lavorassimo sempre in locale. A questa lancia spezzata in favore dell'RMI si affianca però il problema che questa tecnica può essere utilizzata soltanto quando applicazioni del lato server e del lato client, sono entrambe implementazioni JAVA. Altre tecniche come CORBA, invece, pure essendo più complicate da utilizzare, permettono di potere fare cooperare applicazioni scritte in linguaggi diversi, come ad esempio C++ e JAVA.

per tornare all'indice

3.7 Bibliografia.

		     JAVA EXAMPLES IN A NUTSHELL: D. Flanagan. O'Reilly

JAVA TUTORIAL: WWW.JAVASOFT.COM

per tornare all'indice

4 Potete consultarci all'indirizzo:

gruppo11@tiffany.diee.unica.it

per tornare all'indice