DOCUMENTO DI SPECIFICA

Di Progetto

Monitor del carico macchina

Tesina Reti di Calcolatori

a cura di

Gabriele Tinti

 

versione 2.0

27 marzo 2000

Indice

 

 

  1. Introduzione
  2. Scelta dell’ambiente di Sviluppo
  3. Metodologia di sviluppo
  4. Specifica di Sviluppo
  5. Documentazione in UML

  1. Diagramma e specifica dei casi d'uso
  2. Diagramma e specifica delle classi
  3. Diagramma dei componenti
  4. Diagramma dei dispositivi

  1. Piano di test

 

 

Introduzione

In questo documento vengono riportate le fasi di progettazione della tesina che ha come obiettivo la realizzazione di un monitor del carico di una macchina basato su un sistema multi-piattaforma e machine-indipendent.

Il documento si sviluppa in cinque parti:

I diagrammi prodotti sono i seguenti:

 

 

 

 

 

Scelta dell’ambiente di Sviluppo

I requisiti del progetto hanno guidato verso la scelta di un ambiente multi-piattaforma, e quindi la scelta è caduta su Java, in quanto offre un supporto machine-indipendent ad alto livello. Pertanto questa scelta garantisce una serie di facilitazione riguardanti soprattutto la comunicazione in rete, la GUI, e la comunicazione tra processi.

Il problema di reperire alcune informazione come lo stato della Cpu, che è fortemente dipendente dall'architettura, non è possibile risolverlo con la versione attuale del JDK (Jdk 1.2.2), pertanto si è dovuto utilizzare rutine in codice nativo. La scelta in questa fase è caduta su JNI, che consente un supporto ancora legato a Java, per quanto riguarda l'interfacciamento tra classi Java e funzioni in C, e la gestione della memoria dinamica (garbace collector).

Infine per la comunicazione tra le varie macchine e l'utente che ne vuole conoscere lo stato si è pensato di appoggiarsi a RMI. La scelta è stata fatta soprattutto pensando a una possibile estensione dell'applicazione. Infatti se si volesse assegnare un task alla macchina più scarica, avrei già il supporto idoneo, e lo sforzo progettuale risulterebbe minimo.

 

Metodologia di sviluppo

La metodologia di sviluppo scelta è di tipo bottom-up . Ossia dalla progettazione di massima dell'applicazione si suddivide l'applicativo in alcuni moduli, e poi si passa alla loro realizzazione appunto in modo bottom-up. Questo sistema suggerisce anche uno sviluppo a prototipi, per meglio testare il lavoro che via via viene prodotto.

I nuclei fondamentali di sviluppo sono:

  1. La parte RMI di comunicazione dei processi
  2. La parte di reperimento delle informazioni sullo stato delle macchina. Questa parte è in codice nativo, e viene sviluppata ad hoc per ogni architettura.
  3. Il reperimento delle informazione e la presentazione all'utente finale.

Si cerca di adottare uno stile di progettazione ad oggetti puro, secondo l'impostazione Java, in modo da consentire uno sviluppo ulteriore del codice nel senso del mantenimento e della riusabilità, soprattutto per quanto riguarda le funzioni in codice nativo si limiterà la loro presenza in un unico gestore (classe), in modo che sia più facile sia la loro implementazione sia il loro grado di intrusione nella applicazione globale.

Un ultimo notazione riguardante lo sviluppo delle interfacce grafiche: in questo contesto non sembra di particolare rilevanza l'attenzione nei confronti delle interfacce grafiche, si preferice quindi concentrare l'attenzione sulla presentazione dello stato dei vari dispositivo a console, e presentare una semplice interfaccia solo per quanto riguarda la presentazione dei risultati all'utente finale.

 

 

Specifica di Sviluppo

In questa sezione si presenteranno i vari strumenti Java che verranno utilizzati in fase di implementazione.

RMI: il suo utilizzo è idoneo per due aspetti principale. Il primo è che offre un sistema molto efficiente di invocazione di metodi remoti grazie all'acquisizione di reference di Classi istanziate da applicazioni remote, inoltre è progettato per lavorare in ambiente internet e quindi offre già l'implementazione della parte di comunicazione del progetto. Inoltre non è necessario avere codice al lato client particolarmente complesso, al limite è sufficiente un browser abilitato all'esecuzione di applet. Il secondo è che permette di gestire in modo semplice molti server RMI, è sufficiente interrogare rmiregistry per prelevare lo stato della macchina registrata.

JNI: Jni in prima istanza mi consente di implementare codice nativo e invocarlo all'interno di una JVM, in seconda istanza mi consente di controllare all'interno di Java eventuali eccezione causate dal codice stesso, che altrimenti sarebbe difficile catturare utilizzando i metodi nativi (da jdk 1.0), inoltre la gestione della memoria dinamica viene ancora demandata al garbage collector, cosa non possibile quando si usano metodi nativi.

 

Specifica del codice

In questa sezione si daranno alcuni cenni di come è stato sviluppato il codice. L'interesse di non è rivolto nei confronti della sviluppo dettagliato di tutto il codice, ma piuttosto si vorranno evidenziare alcuni usi degli strumenti Java che si sono sviluppati.

Si suddividerà la sezione in 3 parti:

 

Sviluppo del client

AppletPrinc.java

 

Tralasciando la parte legata al GUI, che non sembra di interesse vediamo alcuni tratti interessanti del metodo jSend_actionPerformed

// Prepara l'oggetto con le richieste da inviare al server

OptionRequest opt = new OptionRequest();

opt.jHostSel = jHostSel.getText();

opt.jTutti = jTutti.isSelected();

// Costruzione dell'indirizzo per invocare la servlet MonitorService

try{

String strUrl1;

try{

//host dell'applet prelevato dall'html file

strUrl1=this.getCodeBase().toExternalForm();

} catch(Exception ex){…}

String strServlet="servlet/CaricoMacchina.MonitorService";

strUrl=strUrl1+strServlet;

System.out.println("Ok: Ho costruito l'url: "+strUrl);

URL anUrl = new URL(strUrl);

 

// apertura connessione

URLConnection con = anUrl.openConnection();

// Invio richiesta a servlet

ObjectOutputStream out = new ObjectOutputStream(con.getOutputStream());

// scrive l'oggetto sull' output stream e torna lo stream di input

// Spedire L'oggetto OptionRequest al servlet

out.writeObject(opt);

out.flush();

\\\\ObjectInputStream in = new ObjectInputStream(con.getInputStream());

//Leggere i dati inviati dalla servlet

//e li inserisce nella table

Response res;

try{

while(…){

res=(Response)in.readObject();

insertInTable(res);

} }

catch(IOException ex)

{…}

Il metodo insertInTable inserisce i dati nella tabella

 

Sviluppo della Servlet

MonitorService.java

 

La connessione al servlet dell'applet manda in esecuzione un'istanza di MonitorService. Viene invocato il metodo predefinito doPost() che è stato ridefinito e semplicemente lancia il metodo esegui():

public void service()

{

//lettura dei dati inviati dall'applet

// Acquisizione di tutti i reference registrati all'rmiregistry

// e costruzione di una lista serverList di macchine registrate.

serverList = Naming.list(hostRmiregistry);

// RICHIEDO il reference del ServerRMI chiamato serverList[i]

GestoreJavaRem macchina = (GestoreJavaRem) Naming.lookup(serverList[i]);

//Invocazione di un metodo su una macchina remota

Response res = macchina.getState();

//Spedisco l'oggetto REMOTO ricevuto all'applet

sendToApplet(res);

}//end service

 

Sviluppo di GestoreJava

GestoreJava.java

 

Crea un istanza di GestoreNative, dalla quale richiederà l'esecuzione di metodi remoti, e si registra al serverRMI.

//Registra se stessa al serverRMI:

gestoreJava = new GestoreJava();

//Si registra come gestore di oggetti i tipo Compute

Naming.rebind(name, gestoreJava);

 

Sviluppo di GestoreJavaLan

GestoreJavaLAN.java

 

Ha un comportamento analogo a GestoreJava, l'unica differenza è che i metodi di prelevamento dello stato non vengono presi da un'istanza di gesotoreNative, ma da un reference remoto di GesotoreJavaRem che rappresenta la macchina di una sotto lan collegata ad essa.

Genera inoltre un thread detto Figlio, che verifica lo stato di tutti gli host apparteneti ad una lan, e mantiene una lista degli Host attivi. La lista degli host è registrata su un dispositivo di memoria di massa (hosts.dat), in modo simile a Unix

 

Vediamo invece la parte di implementazione del thread Figlio.

Il metodo più significativo è il metodo run() che sospende il thread per un certo tempo dopodiché a verifica lo stato di tutti i gestori registrati sulla lan

 

//tempo di sospensione del thread

private long time=15000; //tempo di sleep prima di aggiornamento

 

 

//Istanzia GestoreJavaRem per ogni locale registrato

gestoreJavaLAN = new GestoreJavaLAN();

gestoreJavaLAN.macchinaLAN = macchina;

gestoreJavaLAN.hostLan = serverList[i];

//Registrazione a rmiRegistry

String name;

// nome del remoto da registrare al rmiregistry globale

name = "//"+hostRmi+"/Stato"+serverList[i];

try{

Naming.rebind(name, gestoreJavaLAN); //istanzia se stesso

System.out.println("GestoreJavaLAN bound");

}

catch (Exception e) {…}

//Gestione errori

} catch (Exception e) {…}

}/////// end ciclo prelevamento reference

//Aggiornamento ogni time millisecondi (15 sec)

try {this.sleep(time) ;}catch(InterruptedException e){};

}//end loop infinito

}//end run

}//end Figlio

 

Sviluppo metodi nativi

GestoreNative.java

GestoreNative semplicemente è una classe Java che carica una libreria e mette a disposizione metodi nativi

//Caricamento libreria

static{System.loadLibrary("nativeCpuMem");}

// Metodi nativi

private native float getCpuNative();

private native long getMemNative();

 

CaricoMacchina_GestoreNative.cpp

Questo file C++ implementa i metodi nativi C++ della classa Java GestoreNative mettendo a disposizione un'interfaccia.si è fatto uso di JNI.

Queste funzioni invocano due metodi che prelevano lo stato getCpuState e getMemState() e settano i risultati direttamente all'interno della classe:

float getCpuState();

long getMemState(long *,long *,long *);

Questo file è il medesimo sia per l'implementazione Win95 /NT che per Solaris /Linux

JNIEXPORT jfloat JNICALL Java_CaricoMacchina_GestoreNative_getCpuNative

(JNIEnv *env, jobject thisObj)

{

//Contiene il valore delo stato della cpu prelevato dalla funzione C getCpuState()

jfloat valCpu=0;

//Invoca il metodo getCpuState per prelevare lo statto della cpu

valCpu = getCpuState();

//Setta il valore della variabile GestoreNative.cpu

jclass clazz = env->GetObjectClass(thisObj);

jfieldID fid = env->GetFieldID(clazz, "cpu","F");

env->SetFloatField(thisObj,fid, valCpu);

return valCpu;

}

Esegui.cpp (Linux)

Utilizza due processi per prelevare lo stato

getCpuState();

Il processo padre: ps -A -o pcpu mentre il processo figlio: legge con una scanf su stdin

if((f=fork())==0)

{ /*figlio*/

//leggo dal padre i valori di cpu

while(gets(buf))

{

sscanf (buf,"%f",&val);

subtot=subtot+ val;

}

return subtot;

}

else

{/*padre*/

execl("/bin/ps","ps","-A","-o","pcpu",NULL);

}

}//end getCpustate()

Analogo il funzionamento di getMemState();

Esegui.cpp (Solaris)

La versione di esegui.cpp per solaris è leggermenta diversa in quanto il metodo procinfo usato in Linux non esiste in Solaris , si è fatto riferimento la routine di basso livello sysconf per prelevare lo stato della memoria mentre, per quanto riguarda lo stato del processore, il codice per Solaris non è stato modificato rispetto a Linux.

Sysconf(par) attraverso il paramentro par permette di prelevare molte informazioni di sistema, ed è risultata particolarmente utile con le opzioni di _SC_PHYS_PAGES, _SC_PAGESIZE, _SC_AVPHYS_PAGES:

long getMemState(long *total, long *used,long *free)

{

long sizePage;

sizePage = sysconf(_SC_PAGESIZE);

*total = sysconf(_SC_PHYS_PAGES)*sizePage;

*free = sysconf(_SC_AVPHYS_PAGES)*sizePage;

*used = *total - *free;

return *free;

}//end getMemState()

 

 

Esegui.cpp (Win9x)

 

Si è fatto uso delle Windows Api per prelevare lo stato.

Gestione Memoria attraverso l'invocazione di

GlobalMemoryStatus(p)

ottengo un ptr p che punta a una struttura di tipo MEMORYSTATUS

typedef struct _MEMORYSTATUS { // mst

DWORD dwLength; // sizeof(MEMORYSTATUS)

DWORD dwMemoryLoad; // percent of memory in use

DWORD dwTotalPhys; // bytes of physical memory

DWORD dwAvailPhys; // free physical memory bytes

DWORD dwTotalPageFile; // bytes of paging file

DWORD dwAvailPageFile; // free bytes of paging file

DWORD dwTotalVirtual; // user bytes of address space

DWORD dwAvailVirtual; // free user bytes

} MEMORYSTATUS, *LPMEMORYSTATUS

Per trovare la percentuale di utilizzo della cpu si è utilizzato il Performance Registry di Windows 9x. In particolare si è attivato all'interno di Windows un monitor dell'utilizzo della CPU, una volta attivato il monitor si preleva il risultato.

Il codice di getCpuState è semplicemente:

getCpuTime()

{DWORD cpuUsage;

//Se non attivo, ATTIVA il monitor della CPU

EnableDataCollection("KERNEL","CPUUsage");

// preleva il dato dell'uso della cpu

CollectPerfData("KERNEL","CPUUsage",&cpuUsage);

return (float)cpuUsage;

}

EnableDataCollection("KERNEL","CPUUsage") attiva il monitor connettendosi a

HKEY_DYN_DATA\PerfStats\StartStat\ KERNEL\CPUUsage.

CollectPerfData("KERNEL","CPUUsage",&cpuUsage) e preleva il valore del corrente uso della Cpu, connettendosi a:

HKEY_DYN_DATA\PerfStats\StatData\ KERNEL\CPUUsage

Il codice fa riferimento alle Microsoft Win32 API contenute in advapi32.lib

Esegui.cpp (Win NT)

Per quanto riguarda il prelevamento dello stato della Memoria, la versione non è stata modificata rispetto a Win 95, mentre per quanto riguarda il prelevamento dello stato della Cpu si è utilizzata la libreria PDH.dll, che mette a disposizione una comoda gestione dei registri di sistema.

Tralasciando i dettagli istanziando un contatore con il tipo di registro in formato stringa, si può accedere allo stato della Cpu.

 

// nome del contatore per la CPU

CHAR szCounterPath[45] = TEXT("\\Processor(0)\\% Processor Time");

// Partenza di un contatore per lo stato

pdhStatus = PdhAddCounter (hQuery, szCounterPath, 0, &hCounter);

// Funzione di congelamento dello stato

pdhStatus = PdhCollectQueryData(hQuery);

// Prelevamento del valore dello stato

pdhStatus = PdhGetFormattedCounterValue(hCounter,dwFormat,(LPDWORD)NULL, lpItemBuffer);

// Chiusura del contatore

pdhStatus = PdhCloseQuery (hQuery);

return (float)(lpItemBuffer->doubleValue);

 

 

Documentazione Uml

Vengono di seguito presentati i diagramma dei casi d'uso, delle classi, dei componenti e dei dispositivi in UML. Per ognuno di essi vi è una opportuna spiegazione.

 

Diagramma dei casi d'uso

Un utente richiede un servizio a un servitore MonitorService di un WebServer, che a sua volta preleva da una lista locale tutti i reference degli oggetti remoti GestoreJava (o GestoreJavaLan) delle macchine da analizzare, dopodiché contatta gli rmiregistry su ogni host e invoca i metodi per il prelievo dello stato, ritorna un risultato che viene rispedito all'utente sotto forma oggetto contenete i dati di risposta.

Da notare che MonitorService è un oggetto (Servlet) che risiede in un Server Web, ma può anche semplicemente essere una classe che viene generata all'interno dell'applet, e quindi è possibile demandare in locale il dialogo con i serverRmi registrati.

GestoreJavaLan è un oggetto che implementa le stesse funzionalità di MonitorService e si occupa di reperire le informazioni di una sottorete di macchine a lui connesse. L'idea di trasparenza no c'è a livello di sottorete (il gestore sa chi interrogare) in quanto si è pensato fosse ragionevole, la conoscenza di un server degli host apparteneti alla propria lan.. Esisterà una sola istanza di questa classe in ogni sottorete. Le macchine della sottorete implementaranno un agente di tipo GestoreJava.

In ogni host è attiva un'istanza di GestoreJava, che ha due comportamenti diversi: nel caso sia attiva al di fuori di una sottorete, essa invoca un servizio di registrazione a MonitorService, in modo da essere registrata nella sua lista locale e quindi essere raggiunta; nel caso risieda in una sottolan, non si registra a MonitorService, in quanto verrà interrogata attraverso GestoreJavaLAN. Ogni istanza di GestoreJava deve registrarsi al proprio rmiregistry, per rendere disponibile i propri servizi

Diagramma delle classi

Il diagramma delle classi presenta AppletPrinc che implementa il programma utente per fare una corretta richiesta di prelievo dello stato delle macchine.

MonitorService è una classe che riceve le richieste da AppletPrinc e implementa i seguenti metodi:

GestoreJava è un oggetto remoto che implementa UnicastRemoteObject. Invocando i suoi metodi vengono effettuare le analisi della macchina su cui esso è attivo. Da notare registratiAlServer() che permette all'oggetto remoto di registrarsi al WebServer, mentre fineSessine() permette di togliere la propria registrazione dall' WebServe .

GestoreFiglio è una classe che implementa metodi nativi. I metodi init() e stop() permettono di inizializzare o arrestare i processi che questa classe ha generato. Contiene anche due attribuit mem cpu che contengono gli ultimi valori registrati dalle precedenti letture dello stato.

Vediamo più in dettagglio le singole classi.

ApplePrinc:

E' una classe di java.applet, che mette a disposizioni alcuni controlli per la selezione delle macchine di cui si vuole lo stato. Per senmplicità si è scelto di richiedere o una rete o tutte le macchine che si sono registrate al rmiregisty principale. Mette a disposizione una tabella per la visualizzazione dei risultati, e un bottone per inviare la richiesta. Dopodiché si mette in attesa dei risultati da visualizzare.

OptionRequest:

E' una classe di java.io.Serilizable , che mette a disposizioni dei dati per inviare la corretta richeista a MonitorService:

Response:

Contiene tutte le informazione prelevate da un'architettura:

MonitorService:

e' una classe di javax.servlet, mette a disposizione funzionalità di connessione con l'applet e funzuionalità di connessione con il server che si sono registrati per il reperimento dello stato.

I principali metodi:

I principali attributi:

GestoreJava:

e' una classe di javax.rmi.server. Si registra al server rmiregistry e istanzia una classe di Gestore Native.

Metodi principali:

GestoreNative:

è una classe che contiene metodi nativi per il prelevare lo stato della macchina. Essa importa la libreria di sistema nativeCpuMem, che a seconda della piattaforma verrà cercata con i seguenti nomi:

 

Diagramma dei componenti

Il diagramma dei componenti mette in luce la presenza di NativeWin95 e NativeLinux/Sun. Questi due pacchetti rappresentano le librerie contenente il codice nativo che cambia a seconda dell'architettura.

NativeWin95 contiene soltanto alcune API per la lettura dello stato della macchina:

nativeCpuMem.dll

pdh.dll (Windows NT platform)

NativeLinux/Sun contiene invece due processi che attraverso una pipe si scambiano informazione sullo stato della macchina e restituiscono i valori calcolati (Diagramma dei componenti Linux/Sun).

libNativeCpuMem.so

Naturalmente in fase di implementazione si dovrà tenere conto della presenza dell'uno o dell'altro pacchetto e sarà necessario segnalare la necessità della presenza degli stessi.

Comunicazione rappresenta tutto le classi che implementano la comunicazione tra le varie entità presenti, in questo caso sono le socket e i la comunicazione implementata da RMI.

ServerRMI: principalmente è rappresentato dalla classe GestoreJava, GestoreNative, , rmireigistry, che implementano la comunicazione con il client e con lo stato della macchina.

 

Diagramma dei dispositivi

Vengono presentati i dispositivi che fanno parte del progetto.

GestoreJava risiede nella macchina soggetta a lettura dello stato, e ve n'è uno per ogni macchina.

Una versione più semplificata del progetto potrebbe vedere la scomparsa del ServerWeb come dispositivo, e la comparsa delle sue istanze all'interno dell'applet. Da notare che il diagramma delle classi non verrebbe modificato se non per il fatto che ServerWeb non erediterebbe più da Servet.

I dispositivi presenti sono:

UtenteWeb: è una macchina che si connette con un serveWeb per prelevare lo stato

RmiRegistry: è il gestore di nomi rmi che consente di prelevare i reference della classe che si è registrate per fornire lo stato su quella particolare macchina. Vi è un rmiregistry attivo su ogni macchina

ServerWeb: è un server che permette di scaricare l'applet e di invocare il MonitoService.

GestoreJava: è una macchina (possono essere molte) che si è registrata al proprio server rmi per fornire il prorio stato.

GestoreJavaLan: è una macchina come GestoreJava. Inoltre gestisce tutti le macchine della Lan di appartenenza che si sono registrate per fornire il proprio stato.

 

 

Piano di test

INTRODUZIONE

 

L'intenzione di questo semilavorato è descrivere il piano di test che permetta poi nel corso di tutta la fase di progettazione di verificare i vari requisiti definiti nel DSR (Documento di Specifica dei Requisiti). Per prima cosa è necessario scomporre il sistema complessivo in vari moduli; pertanto avremo due fasi di test: una prima per testare i singoli moduli e una seconda fase per testare i meccanismi di integrazione e interazione fra i singoli moduli fino a testare il sistema complessivo e arrivare a un test di accettazione del prodotto finale. Si può suddividere il piano di test nelle seguenti attività:

 

TEST DI INTEGRAZIONE

 

Prima di poter parlare di test di integrazione, è necessario suddividere il progetto in singoli moduli e specificare le relazione gerarchiche che sussistono tra i vari componenti. Di seguito viene riportato una tabella dei moduli. Dato il tipo di applicazione si è pensato di suddividere i moduli in coppie di agenti che hanno relazione tra loro. Avremo quindi un modulo Applet/Servlet che permette di testare tutte le funzionalità che legano le due entità nel sistema, e così via per tutti gli altri moduli.

 

Moduli sistema CaricoMacchina

Nome modulo

Componenti

Applet/Servlet

AppletPrinc MonitorService , optionRequest, Response

ConnessioneRmi (Servlet, gesstoreJava)

MonitorService, GestoreJava, optionRequest, Response,rmiRegistry

Prelevamento dello stato

GestoreJava, GestoreNative

Connessione Rmi LAN

GestoreJavaLan, GestoreJava, rmiRegistry

   

La tabella dà, quindi, un ordine logico e temporale, in virtù della metodologia di progettazione scelta, di quali moduli testare. Per testare anche solo alcune parti del sistema si rende necessario la creazione di moduli guida (driver) che simulano moduli che hanno il compito di chiamare le parti del sistema prese in considerazione in una particolare fase di test. Non sarebbero invece necessari moduli fittizi (stub) dal momento che ci muoviamo con un approccio bottom-up.

La prima fase di test di integrazione, consiste nel testare i componenti contenuti in ogni singolo modulo. Si passa poi al test del modulo, verificando la comunicazione dei componenti appartenenti allo stesso modulo.

Da notare che è stato possibile testare il codice in locale, su una macchina Windows 95 con scheda Ethernet, in quanto l'uso di un linguaggio come Java può garantire la portabilità del codice su altre architetture, mentre la possibilità di funzioni di loopback a consentito di testare sulla stessa macchina la parte distribuita del codice. Si è trovata invece difficoltà nel test del codice su diverse versione JDK. Alcune funzionalità della versione compilata con il JDK 1.1.6 sono risultate incompatibili con il JDK 1.2., ci si è dovuti quindi uniformare al Jdk 1.2, che è lo standard al quale viene garantita la compatibilità con le versioni future di Java. Per permettere l'utilizzo del applicazione anche in ambienti con Jdk 1.1.x si è creato un prototipo con funzioni ridotte.

In particolare sarà necessario che il server Web e le macchine che implementano GestoreJava supportino il JDK 1.2, mentre per quanto riguarda il client remoto (applet) la compatibilità è garantita dall'installazione di Java Plug In, che metta a disposizione almeno una versione JRE 1.1.8.

Per quanto riguarda il test del modulo "Prelevamente dello stato" si è dovuto fare riferimento a architetture specifiche, in quanto il codice è dipendente dall'architettura. Si è testato questo modulo in ambiente Linux Red hat 6.1, in ambiente Windows 98, e in ambiente Sun .Per fare questo test si utilizzato un driver molto semplice che permettesse semplicemente il caricamento della libreria e il test dello stato.

 

TEST DI SISTEMA

 

Questa fase si pone come naturale continuazione della fase di test di integrazione e ha il compito di completare il test del sistema, questa volta considerato nella sua globalità. In particolare si considererà proprietà quali il carico del sistema, la robustezza e l’affidabilità. A fronte di queste proprietà si può distinguere alcuni test specifici.

Il test di Sistema ha il principale scopo di verificare il corretto funzionamento dell'applicazione in ambiente distribuito, e quindi per il test si è scelto di utilizzare le macchine Sun e NT messe a disposizione in Lab2.

Non si è riusciti ad ottenere il permesso di fare test tra architetture diverse, cioè integrando macchine Sun e Windows NT, in quando il la rete del laboratorio non lo consente, ma il supporto che offre Java ne garantisce comunque il funzionamento, anche perché la comunicazione tra un'applicazione e un'altra avviene tramite RMI, e quindi è del tutto indipendente dall'architettura.

I test presi in considerazione nella fase di Test di Sistema sono: il test di stress e il test di robustezza.

 

Test di Stress

L’obiettivo del test di stress è quello di verificare il comportamento dell’applicazione in condizioni di sovraccarico, ossia in quelle condizioni di carico maggiore del normale carico di lavoro in cui il sistema per un qualsiasi motivo si può venire a trovare. Questo test risulterà importante perché da un lato fornirà quali sono i limiti strutturali della nostra applicazione e dall’altro ci permetterà di gestire al meglio tali situazioni probabilmente rare ma comunque da gestire.

Il carico del sistema può essere considerato a due diversi livelli: un primo livello riguarda il carico a cui verrà sottoposta la singola macchina, e quindi si vorrà testare il funzionamento in una situazione critica, un secondo livello sarà il verificare il funzionamento dell’applicazione in un ambiente distribuito, cioè in un ambiente con supporto di rete ove è previsto che l’applicativo debba girare.

Si è cercato di simulare un po' di traffico in rete e di fare più chiamate al servlet da parte dell'applet, il sistema si comporta abbastanza bene, anche se facendo chiamate ripetute dello stato di una macchina si umenta il suo carico computazionale, e quindi il tempo della Cpu viene falsato.

 

Test di Robustezza

Con questo test si vorrà testare il programma da un punto di vista delle risposte a dati in input non corretti. Data la piccola quantità di dati che l'utente gestisce di fatto si ricevono soprattutto informazione piuttosto che produrle, questo test non offre particolare interesse.

E' invece fondamentale verificare il comportamento della sistema in caso di eventi eccezionali, quali la caduta di un server, e quindi offrire un comportamento semplice ma robusto per evitare che l'intero sistema si blocchi.

Il test si snoda in tre parti sisgnificative:

Si è comunque considerato di gestire tutte questi casi anche se in modo semplificato

Nel primo caso il sistema va riavviato, in quanto il serverWeb non è in grado di ricstruire la lista dei gestori che si erano registrati.

Nel secondo caso il ServerWeb se ne accorge e cancella dalla sua lista il gestore, al successivo riavvio del gestore, tornerà ad aggiornare la lista.

Il terzo caso invece viene gestito come il secondo, è demandato all'amministratore dell'host le opportune operazioni di recovery.

E' stato concordato di non implementare politiche di ripristino particolari, ma semplicemente gestire in modo semplice le eventuali eccezioni, intervenendo nella maggior parte dei casi con messaggi per l'amministratore demandando la scelta di politiche appropriate.

Per questo test si è lavorato questo modo: si sono provati uno alla volta tutti i punti, e si è verificato il comportamento del sistema. Da notare che tutte le applicazioni attive sono ridondanti di messaggi sullo stdout, in quanto si vuole monitorare in tutte le fasi il corretto funzionamento del sistema globale.