Università degli Studi di Napoli Federico II Facoltà di Scienze MM.FF.NN. Corso di Laurea in Informatica Tesi Sperimentale di Laurea Triennale Progettazione ed implementazione di un sistema di rappresentazione grafica di dati complessi Relatori Candidato Prof. Guido Russo Ing. Giovanni Battista Barone Dott.ssa Vania Boccia Anno accademico 2009/2010 Gianluca Coda Matricola: 566/557 Indice generale 1 Introduzione.......................................................................................................................... 3 2 Modalità per la rappresentazione dei dati – relazioni fra entità, grafici semplici e grafici complessi.....................................................................................................................................6 2.1 Tipi Di Relazioni Visualizzate Nei Diagrammi E Nei Grafici......................................6 2.2 Composizione Di Relazioni E Diagrammi Complessi................................................ 10 2.3 Rappresentazione Di Code E Flussi............................................................................ 11 3 Obiettivi e requisiti del sistema realizzato......................................................................... 14 3.1 Il Precedente Sistema Di Visualizzazione Del Centro CNAF.....................................17 3.2 Requisiti Funzionali Del Generatore Di Grafici .........................................................17 3.3 Requisiti Non Funzionali.............................................................................................20 3.4 Scelta Dei Tool - Confronto Tra Gnuplot E JFreeChart.............................................. 20 4 Specifica dei requisiti..........................................................................................................24 4.1 Use Case...................................................................................................................... 24 4.2 Tabelle Di Cockburn....................................................................................................25 5 Architettura del sistema di visualizzazione realizzato .......................................................42 5.1 Scomposizione Funzionale In Moduli.........................................................................42 5.1.1 Modulo di accesso dei dati job.......................................................................... 43 5.1.2 Modulo di trasformazioni dei file Job acquisiti ................................................44 5.1.3 Generazione di pagine JSP................................................................................ 48 5.2 Diagrammi UML Di Progettazione............................................................................. 53 5.2.1 Component Diagram..........................................................................................53 5.2.2 Class Diagram....................................................................................................54 5.2.3 Sequence Diagram............................................................................................. 56 6 Sviluppo dell'applicazione.................................................................................................. 57 6.1 Dal Diagramma Delle Classi Al Codice Java..............................................................57 6.2 Organizzazione Dei File Di Sistema Contenenti I Dati Dei Grafici...........................58 6.3 Libreria Di Visualizzazione JFreeChart ..................................................................... 59 6.4 Tipi Di Grafici............................................................................................................. 59 6.5 L'esportazione Verso Il Portale....................................................................................64 6.5.1 I formati immagini statici.................................................................................. 64 6.5.2 I formati immagini interattivi............................................................................ 66 6.5.3 Le scelte effettuate............................................................................................. 67 7 Conclusioni e futuri sviluppi...............................................................................................68 8 Bibliografia......................................................................................................................... 69 9 Appendici – Installazione e funzionamento del sistema.................................................... 70 9.1 Messa In Esercizio.......................................................................................................70 9.2 Guida D'uso................................................................................................................. 71 9.3 Codice Sorgente.......................................................................................................... 78 Gianluca Coda 566/557 Pagina 2 di 131 1 Introduzione L'attività del tirocinio di tesi è stata svolta nell'ambito di un progetto in corso fra l'Università di Napoli Federico II e il CNAF (Centro Nazionale per la Ricerca e sviluppo per le tecnologie informatiche e telematiche) di Bologna. L'attività della tesi ha riguardato lo studio, la progettazione e la realizzazione di un generatore di grafici rappresentanti le attività dei sistemi di attesa (code) e dei job di una Grid. La prima fase dello stage è stata dedicata all'individuazione dei requisiti del generatore di grafici. Tali requisiti sono stati ricercati con la chiara intenzione di superare alcuni problemi esistenti in una precedente versione del visualizzatore e con le motivazioni di estendere le funzionalità e le modalità di utilizzazione (suggerite in parte anche dai gestori del sistema Grid). Per individuare tipi di visualizzazioni adeguate ai dati da rappresentare si è intrapreso uno studio che è partito dall'analisi (astratta) di alcune relazioni parte-totalità (ad esempio relazione fra una parte di un flusso e il flusso totale), individuando successivamente il corrispondente tipo di grafico più adeguato al tipo di relazione da rappresentare. L'analisi ha portato ad individuare l'adeguatezza di un particolare diagramma per la rappresentazione di genesi di popolazioni di attese (Virtual Organization, Gruppi e Code) in varie modalità di visualizzazione temporale (per esempio rispetto ad un giorno, una settimana, un mese etc). I requisti del generatore di grafici in parte funzionali (come quelli appena riportati) e in parte non funzionali (come quelli relativi al tipo di codice di sviluppo) sono stati presi come input, in una fase del tirocinio, dove si è progettato il visualizzatore, in particolare sviluppando i diagrammi dei casi di uso, di classi e di sequenza per la realizzazione del codice del programma. Il generatore di grafici è stato realizzato secondo un'architettura riportata schematicamente nella figura 1.1. Gianluca Coda 566/557 Pagina 3 di 131 Figura 1.1 - Schema di architettura e moduli del generatore grafico. Dopo l'introduzione contenuta nel capitolo 1, nel capitolo 2 si è effettuato uno studio per individuare, per ogni tipo di relazione parte-totalità, il tipo più adeguato di diagramma da adottare per la visualizzazione. In tale capitolo si è individuata anche una particolare rappresentazione grafica, la quale possiede caratteristiche di chiarezza e di espressività per le attività complesse relative al monitoraggio delle code in un Grid. Nel capitolo 3 sono stati individuati i requisiti funzionali e non funzionali del generatore, in parte forniti da alcuni utenti (stakeholders) del Grid. Altri requisiti sono stati individuati dal tirocinante. Nello stesso capitolo si è riportato anche uno studio dei tool di base da adoperare per lo sviluppo del visualizzatore grafico. Nel capitolo 4 della tesi sono stati descritti i requisiti funzionali e non funzionali individuati durante la fase iniziale del lavoro di tirocinio. Tali requisiti hanno costituito l'input per l'attività di progettazione (contenuta nel capitolo 5) la quale è stata sviluppata applicando metodi dell'ingegneria del software, utilizzando quindi gli standard diagrammi UML dei casi d'uso, delle classi e delle sequenze. Nella tesi è contenuta una descrizione del programma del generatore grafico realizzato (capitolo 6), nella quale viene riportato il Gianluca Coda 566/557 Pagina 4 di 131 ruolo svolto da tutti i moduli software che compongono il sistema, in particolare è stato descritto il modulo che rende possibile la visualizzazione dei grafici su WEB. In tale parte di tesi sono stati forniti esempi di di funzionamento del visualizzatore grafico. Nel capitolo 7 sono state riportate le conclusioni ed elencati i futuri sviluppi del sistema, mentre la bibliografia è presente nel capitolo 8. Infine, in appendice è stato riportato il codice Java sviluppato relativo ai principali moduli del sistema. Per le finalità di utilizzazione del sistema realizzato, è stata redatta una guida di installazione e di utilizzazione del sistema di visualizzazione realizzato. Gianluca Coda 566/557 Pagina 5 di 131 2 Modalità per la rappresentazione dei dati – relazioni fra entità, grafici semplici e grafici complessi In questo capitolo, partendo dall'analisi di alcune relazioni di parte e totalità, si è individuato una corrispondenza tra un insieme di relazioni parte/totalità presenti nel dominio dei dati da visualizzare e un insieme di grafici più adeguati per la loro rappresentazione. Nello stesso capitolo sono state individuate alcune specifiche modalità di rappresentazione per dati complessi (diagrammi genetici) che meglio esprimono le relazioni di entità del dominio della rappresentazione delle attese, oggetto di studio di questo tirocinio. 2.1 Tipi di relazioni visualizzate nei diagrammi e nei grafici I sistemi di visualizzazione di grafici hanno nelle loro funzionalità di base quelle di disegnare valori di proprietà o di caratteristiche fisiche di oggetti, o insiemi, nello spazio e nel tempo. La maggior parte dei layout esistenti nei sistemi software predisposti per questo scopo (ad esempio Excel, MatLab) assumono come rappresentazione di una totalità, uno spazio-3d, un piano, un cerchio etc, e le parti in tali spazi geometrici. Esistono diverse ricerche dove sono state studiate le relazioni fra parti e totalità. Winston, Chaffin e Hermann (1987) hanno proposto le relazioni chiamate WCH (acronimo dei nomi degli ideatori). Abbiamo preso come riferimento queste relazioni per individuare un insieme di relazioni di base a partire dalle quali costruire grafici più complessi. Riportiamo un insieme di tipologie di relazioni parti e totalità, di interesse al dominio Gianluca Coda 566/557 Pagina 6 di 131 dei sistemi di attese, e i corrispondenti grafici. Relazioni componente/oggetto - sono relazioni tra oggetti composti (totalità) e le loro parti, dove le parti sono separabili ed hanno una loro funzionalità, per esempio manico/tazza, tastiera/computer, tasti/tastiera, etc (diagramma dei componenti UML). Le relazioni componente/oggetto vengono in genere visualizzate in un spazio bidimensionale attraverso icone o simboli (che rappresentano le componenti o parti della totalità) che (quasi sempre) non rispecchiano le reali distanze (neppure in scala) fra le componenti. Nel dominio delle Grid un esempio di tale relazione componente oggetto e il diagramma corrispondente è il seguente: Figura 2.1.1 – Esempio di relazione parte-totalità di tipo componente-oggetto (elementi e servizi della European DataGrid). Relazioni membro/collezione – sono relazioni che stabiliscono un'appartenenza di un elemento ad una collezione. I membri non hanno un ruolo funzionale nella collezione e possono essere pertanto separati. Costituiscono un esempio di tali relazioni albero/foresta, cella di memoria/MemoriaRam, etc. Anche queste relazioni vengono rappresentate in forma iconica come nel caso precedente. Gianluca Coda 566/557 Pagina 7 di 131 Figura 2.1.2 - Esempio di relazione parte-totalità di tipo membro-collezione. Relazioni porzione/totalità – stabiliscono una relazione fra una parte o porzione della totalità costituita dallo stesso materiale della totalità e la totalità stessa. Esempi di tali relazioni sono fetta/torta, tipo_persona/popolazione. Rientrano in questo gruppo code/flussi_totali tipici per la rappresentazione di attività di calcolo di un computer eseguite e quelle ancora da eseguire in un certo intervallo temporale. Le relazioni porzione/totalità sono rappresentate attraverso i tipici diagramma a torta. Figura 2.1.3 – Esempio di relazione parte-totalità di tipo porzione-totalità Gianluca Coda 566/557 Pagina 8 di 131 Relazioni materiale/oggetto - sono relazioni tra un oggetto o una componente di esso e il tipo di materiale di cui è costituito. In questo caso il materiale non può essere separato dall’oggetto, non una ha funzionalità e non è omogeneo. Per esempio acciaio/telaio_bici. In questa categoria appartiene una classe di relazione tipo_regione/regione. I diagrammi tipici che vengono adoperati per la visualizzazione di queste relazioni, sono quelli delle mappe tematiche dove aree di un territorio come case, strade, linee ferroviarie etc, vengono visualizzate con una trama diversa in un diagramma. I rapporti spaziali fra parti e totalità in questi diagrammi sono mantenuti attraverso opportune scale e sistemi di riferimento. Figura 2.1.4 - Esempio di relazione parte-totalità di tipo materiale-oggetto. Relazioni caratteristica/attività – sono relazioni che descrivono una caratteristica di un'attività, evento o fenomeno complesso. Le caratteristiche sono rappresentate spesso nel tempo, hanno un ruolo funzionale, ma non sono separabili dall'attività. Rientrano in questa categoria i diagrammi che descrivono un'attività complessa attraverso un insieme di caratteristiche (temperature, densità, fasi etc) che variano nel tempo. Gianluca Coda 566/557 Pagina 9 di 131 Figura 2.1.5 -Esempio di relazione parte-totalità di tipo caratteristica-attività 2.2 Composizione di relazioni e diagrammi complessi Le relazioni presentate costituiscono una serie di relazioni base a partire dalle quali si costruiscono più complesse relazioni. Ad esempio una relazione materiale-oggetto che varia nel tempo (è il caso di una mappa tematica GIS che varia nel tempo), oppure come la sovrapposizione di relazioni caratteristiche di una relazione caratteristica -attività. Queste relazioni complesse creano alcune difficoltà nella costruzione dei relativi diagrammi. Per per la costruzione di diagrammi in cui ci sono sovrapposizione di relazioni di caratteristiche/attività come ad esempio gli stati di più job (più caratteristiche) in esecuzione in un centro di calcolo (attività del centro), occorre ad risolvere il problema di quanti job al massimo rappresentare e come distinguere graficamente i vari job al fine di ottenere grafici di migliore lettura per gli utenti. Un esempio di diagramma di caratteristiche sovrapposte è rappresentato nella seguente figura. Gianluca Coda 566/557 Pagina 10 di 131 Figura 2.2.1 – Esempio di diagramma con caratteristiche sovrapposte. Alcuni diagrammi complessi utilizzati in questa tesi sono stati quelli di genesi di popolazioni o alberi genetici (diretti o inversi). I quali risultano essere costituiti da una composizione di relazioni porzioni-totalità, dove ogni porzione (omogenea alla totalità) può a sua volta essere composto da altre porzioni (sempre contenenti elementi dello stesso tipo della totalità). 2.3 Rappresentazione di code e flussi Le code e flussi sono entità che sono presenti in varie scenari di attività umane o di sistemi industriali o informatici. Esistono diversi sistemi di visualizzazione di code e di flussi, utilizzati a tal punto da costituire una sorta di metafora per diversi scenari anche molto differenti tra loro. Tipici sistemi (e metafore) di gestione di code sono: •i clienti in una banca o un ufficio postale in attesa di un'operazione da eseguire; •le persone in coda in attesa di un taxi; Gianluca Coda 566/557 Pagina 11 di 131 •i veicoli in attesa di un ingresso ad un casella stradale; •gli aerei in attesa di decollo o di atterraggi; •sistemi di elaborazione; •sistemi di comunicazione / trasmissione dati; •sistemi di lavorazione industriale. Tutti questi scenari hanno in comune: A)una popolazione in ingresso di potenziali clienti; B)delle tipologie di clienti che si qualificano per tipo di servizio; C)un insieme di code; D)un insieme di servitori di servizi associate alle code. B e C differiscono l'uno dall'altro poiché presentano differenti strutture. Ad esempio un ufficio postale può avere una gerarchia di servizi (si veda figura 2.3.1) e diversi servitori ossia differenti sportelli che compiono il servizio. Figura 2.3.1 - Ufficio postale - gerarchia di servizi. Gianluca Coda 566/557 Pagina 12 di 131 Le rappresentazioni di flussi e di strutture di attese possono essere rappresentati come un albero genetico costituiti da un insieme di popolazioni Pi, un insieme di relazioni di tipo insieme sott'insieme R: R(Px,Py) con x≠y e un insieme di caratteristiche Cn associate ad ogni Pi. La rappresentazione di tali alberi genetici è realizzata attraverso diagrammi del tipo riportato in figura 2.3.2. Figura 2.3.2 – Rappresentazione di code come gerarchie di popolazione o alberi genetici Tali diagrammi sono dello stesso tipo proposti in questa tesi per la rappresentazioni dei job di un sistema Grid, ma possono essere utilizzati in ogni sistema di attesa, specificando di volta in volta la popolazione Px (inserita nella tassonomia) e una caratteristiche Cn (in funzione di qualche variabile) associate a tale popolazione. Gianluca Coda 566/557 Pagina 13 di 131 3 Obiettivi e requisiti del sistema realizzato Abbiamo già riportato che l'attività del tirocinio di tesi è stata svolta nell'ambito di un progetto in corso fra l'Università di Napoli Federico II e il CNAF (Centro Nazionale per la Ricerca e sviluppo per le tecnologie informatiche e telematiche) di Bologna. Il CNAF si occupa della gestione e dello sviluppo a livello nazionale dei principali servizi informatici e telematici di supporto all'INFN. Tale centro partecipa da alcuni anni allo sviluppo, implementazione e gestione di infrastrutture di “Griglia” a livello italiano (GRID.it) e internazionali (EGEE e LHC Computing GRID) - per i quali è curato anche parte della progettazione e dello sviluppo. Lo sviluppo del generatore di grafici realizzato in questo tirocinio rientra tra le attività del CNAF. Esso costituirà il sistema di visualizzazione dei grafici per rappresentare i flussi dei dati presenti nel Grid su indicato. Tra gli obiettivi del sistema vi è anche quello di rappresentare i dati di monitoraggio in tempo reale e secondo serie storiche, adottando diverse strategie di aggregazione sia per i dati che per le entità del Grid. Le principali entità del sistema Grid possono essere espresse/rappresentate tramite il seguente diagramma di classi UML. Figura 3.1 – Esempio di parti della Grid e sue relazioni. Gianluca Coda 566/557 Pagina 14 di 131 Dall'immagine si possono individuare le relazioni fra le entità della Grid di cui ne sono state monitorate le attività. Nella Grid sono presenti una o più Code. Ogni Coda è identificata da una stringa e può usufruire di determinate risorse per lo svolgimento di un compito. Ad ogni Coda sono associati gruppi di utenti, in maniera diretta o tramite aggregazioni (astratte) denominate VO (Virtual Organization). Ogni VO è anch'essa identificata per mezzo di una stringa. Un Utente rappresenta il soggetto che ha la possibilità di interagire con la griglia sfruttandone le capacità di calcolo. Tale processo viene attivato mediante sottomissione dei Job alla Grid. Un Job indica una sequenza di compiti (di istruzioni di calcolo) che impegnano le risorse della Grid e possono, nel tempo, trovarsi in diversi stati (in attesa, sospesi etc) - quest'ultimi hanno una denominazione specifica a seconda dello scheduler (politica di pianificazione) adottato dalla Grid. Nell'ambito della sperimentazione del lavoro di tesi, gli scheduler considerati sono di due tipi: PBF e LSF. Nel PBF i job possono avere i seguenti stati: Nome Stato Descrizione S (suspended) Indica lo stato in cui è un job sospeso dal server dove rimarrà in questa condizione fino a che non ci saranno risorse libere per l'esecuzione Q (queued) Questo stato indica i job in coda, quest'ultimi possono essere in seguito eseguiti o assegnati ad atre risorse W (waiting) Indica un job in attesa di esecuzione. In questo stato sono inclusi anche i job la cui richiesta è fallita per qualche motivo R (running) Indica un Job in esecuzione E (exited) Indica un job terminato con successo e costituisce lo stato successivo all'esecuzione T (transiction) Indica un job in transazione per una nuova locazione Tabella 3.1 Gianluca Coda 566/557 Pagina 15 di 131 Nello scheduler LSF invece, gli stati sono: Nome Stato Descrizione PSUSP Indica un job sospeso dal proprietario oppure un job sospeso dallo Scheduler mentre era nello stato PEND SSUSP Indica un job sospeso dal sistema PEND Indica un job in coda POST_DONE Post processo senza errori POST_EXIT Post processo con errori WAIT Indica lo stato in cui si trova un job in attesa di essere eseguito EXITED Indica un job che è terminato con uno stato di errore DONE Indica un job terminato con successo UNKWN Stato sconosciuto, costuisce una conseguenza della perdita di collegamento tra il demone Mbatchd ed il sbattchd dell'host ZOMBI Indica lo stato in cui un job non è più eseguibile in quanto ucciso; solitamente si effettua un'operazione di kill su un job in stato UNKWN. Un'altra possibilità per la quale un job si trovi in questo stato, è dovuta all'indisponibilità dell'host in cui avrebbe dovuto essere eseguito il job; in questo caso il job verrà ucciso ed il suo stato successivo sarà EXITED RUN Indica lo stato di un job che in esecuzione Tabella 3.2 Gianluca Coda 566/557 Pagina 16 di 131 3.1 Il precedente sistema di visualizzazione del centro CNAF Il sistema di visualizzazione delle attività del Grid (d'ora in avanti indicato con SPVG) esistente, presentava manchevolezze e inadeguatezze di funzionalità che riportiamo nei seguenti punti: A)SPVG si limita a fornire delle immagini in formato compresso (png) che sono prodotte da uno script interpretabile da Gnuplot, il quale fornisce una rappresentazione ad istogrammi che non riesce a rappresentare i flussi di dati con una risoluzione accettabile; B)SPVG presenta una seconda tipologia di grafici, relativi ad altri aspetti delle attività della Grid, realizzati in Flash, che per questa data tecnologia software si prevedono problemi di incompatibilità di supporto dei futuri browser Internet; C)SPVG aveva la funzionalità di effettuare zoom (aumento di dettaglio) sia per intervallo di tempo, sia per tipo di attività; D)Non è esiste una funzionalità in SPVG per la visualizzazione di un albero genetico delle popolazioni, per cui non è possibile conosce l'appartenenza di un certo sottogruppo al gruppo che lo ha generato; E)Il punto D implica anche che SPVG prevede solo un livello di gerarchia fra gruppi cioè solo i livelli VO-code; F)SPVG prevede solo una corrispondenza di uno ad uno fra VO e code. 3.2 Requisiti funzionali del generatore di grafici In questo paragrafo riportiamo i requisiti funzionali per il generatore di grafici forniti dai coordinatori di Grid.it, in parte determinati attraverso interviste del tirocinante a quest'ultimi. Gianluca Coda 566/557 Pagina 17 di 131 Dall'analisi è scaturito che uno degli obiettivi del sistema è quello di generare dei grafici di monitoraggio a partire dai dati provenienti dallo scheduler del sistema Grid, che superassero tutte le anomalie ed inadempienze individuate (si veda paragrafo precedente) in particolare implementando una funzionalità di visualizzazione dei dati mediante una rappresentazione ad alberi genetici diretti (si veda figura 2.3.2) e inversi. Altro requisito, inoltre, è quello di fornire a richiesta una rappresentazione globale dei job in esecuzione. Tra i requisiti funzionali si è individuato quello della visualizzazione in Web dell'attività del Grid. Nella tabella 3.2.1 si riporta l'elenco dei requisiti funzionali individuati: Requisito funzionale Descrizione Visualizzazione grafica dei dati storici Possibilità di visualizzare mediante rappresentazione grafica i dati di monitoraggio della Grid nei seguenti intervalli temporali: •giornaliero; •settimanale; •mensile; •annuale. Visualizzazione tabellare monitoraggio della Grid del Possibilità di visualizzare in forma tabellare i dati di monitoraggio della Grid in tempo reale. Navigazione basata su alberi genetici Gianluca Coda 566/557 Menu strutturati rappresentati mediante alberi genetici. In particolare per la visualizzazione grafica dei dati storici, sono stati richiesti i seguenti menu: •albero genetico - rispecchia la struttura della Grid, a 3 livelli di profondità. Le voci di primo livello si riferiscono alle Code, quelle di secondo livello alle VO ed infine quelle di terzo livello ai Gruppi. Le Pagina 18 di 131 informazioni accessibili tramite questo menu sono una rappresentazione grafica dei dati forniti dal Parser. Non vengono forniti dal Parser i dati del livello VO per il quale si costruisce una rappresentazione dei dati concatenando i dati dei suoi sottolivelli (Gruppi). •albero genetico inverso- questo menu ha 2 livelli di profondità. Le voci di primo livello si riferiscono alle VO, mentre le voci di secondo livello alle Code. Al livello delle VO si ha una rappresentazione dei dati forniti dal Parser, mentre per il livello delle Code si costruisce una rappresentazione mediante concatenazione dei dati del livello superiore (VO). Per la visualizzazione tabellare di monitoraggio è stato richiesto il seguente menu: •albero genetico ridotto- ha 2 livelli di profondità e rispecchia la struttura della Grid. Dove al primo livello si ha una rappresentazione delle Code ed al secondo livello una rappresentazione delle VO. Interfaccia via web Possibilità di utilizzare l'interfaccia utente attraverso il Web. Salvataggio grafici Possibilità di archiviazione delle rappresentazioni grafiche dei dati in locale. Interattività dei grafici Gianluca Coda 566/557 Possibilità di effettuare zoom su porzioni arbitrarie dei grafici. Tabella 3.2.1 Pagina 19 di 131 3.3 Requisiti non funzionali I requisiti non funzionali per l'implementazione del generatore dei grafici che riguardano la tipologia dei file in ingresso, non costituiscono dei vincoli per gli strumenti di sviluppo adoperati, essendo i formati dei file del sistema di provenienza compatibili con la maggior parte dei sistemi. E' stato richiesto che l'interfaccia di visualizzazione del generatore fosse compatibile con i sistemi operativi Windows e Linux. Tra i requisiti non funzionali del nuovo visualizzatore è emerso quello relativo alla possibilità di esportare i grafici in vari formati e di offrire un certo grado di interattività. Inoltre si è scelto di sviluppare il sistema grafico di visualizzazione in ambiente Java, al fine di evitare eventuali problemi di installazione di software di terze parti e di non incorrere ad altre eventuali incompatibilità con qualche tipologia di browser. Si è ritenuto infine che la scelta Java, come piattaforma software di sviluppo, potesse risolvere eventuali problemi di integrazione e di compatibilità con gli altri moduli collegati al funzionamento di monitoraggio della Grid. Infine, tra i requisiti non funzionali è stato scelto di generare i layout di interfaccia in Inglese. 3.4 Scelta dei tool - confronto tra Gnuplot e JFreeChart Dopo aver individuato i requisiti del generatore di grafici è emersa la necessità di scegliere un sistema o una libreria grafica come di sviluppo del visualizzatore. La scelta è ricaduta su due tool, molto usati per questi tipi di applicazioni: Gnuplot e JfreeChart. Il primo si è posto all'attenzione perché era il tool usato per la generazione dei grafici del precedente sistema (SPVG), il secondo, invece, preso in considerazione Gianluca Coda 566/557 Pagina 20 di 131 per una migliore adeguatezza alla metodologia di sviluppo basata su JAVA. Si è quindi passati ad una comparazione fra questi due strumenti di sviluppo. Gnuplot, inizialmente sviluppato intorno al 1986, rivolto alla rappresentazione di dati scientifici con i seguenti formati (Latex, PostScript, fig, metafont), oggi è in grado di essere utilizzato da vari linguaggi di programmazione, tra cui: AWK, C, C++, Java, Python, Perl e Visual Basic. Gnuplot è un programma open source, che consente di realizzare grafici sia in 2D che in 3D, accettando in ingresso vari formati di rappresentazione di dati. Disponibile per vari sistemi operativi, è utilizzato attraverso comandi o script non troppo complessi, inviati al terminale di visualizzazione. Consente di ottenere diversi output: a video, stampanti, esportazione nei più comuni formati di immagini, etc. Esempi di grafici ottenuti da Gnuplot sono del tipo: Figura 3.4.1 – Esempi di grafici Gnuplot. Gianluca Coda 566/557 Pagina 21 di 131 JFreeChart è una libreria Java, anch'essa Open Source, che può essere integrata sia in applicazioni Java locali che in applicazioni web. Possiede una gran varietà di tipi di rappresentazione di dati, dai più comuni grafici a “torta”, a “istogrammi” sino a grafici complessi come “time series chart” o “Gantt”. La scelta di utilizzare JFreeChart, rispetto a Gnuplot, oltre alla compatibilità di base Java, è motivata dalle seguenti osservazioni: –possibilità di generare grafici complessi sia 2D che 3D; –permette il salvataggio in vari formati quali JPEG, PNG, PDF, SVG (tramite Batik); –possibilità di visualizzare, mediante puntamento mouse sul grafico, valori dei diagrammi (conosciuta anche come una funzionalità di tipo tooltip) ; –permette zoom interattivo Esistono due entità che si possono definire in JFreeChart: i Dataset e le Series. I Dataset sono collezioni di Series, queste ultime costituite da strutture che immagazzinano i dati da rappresentare. Esistono vari tipi di Dataset e Series in base alla natura dei dati da monitorare. Sono disponibili Dataset che consentono l'importazione di dati da Database e file di testo. Un altro aspetto interessante di JFreeChart è la gestione degli eventi che consente di realizzare grafici interattivi. A tal fine è possibile utilizzare tooltip per la gestione degli eventi legati all'uso del mouse. I principali passi da seguire per la generazione di un grafico con JFreeChart sono: • creazione di un Dataset in base al tipo di grafico; • popolamento del Dataset per mezzo di oggetti Series in base alla natura dei dati; • creazione di un oggetto JFreeChart con il Dataset precedentemente creato. Alcune personalizzazioni del grafico possono prevedere: • l'inserimento di attributi del grafico tramite una delle classi del package plot; Gianluca Coda 566/557 Pagina 22 di 131 • un rendering del grafico tramite una delle classi del package renderer; • l'inserimento di assi del grafico tramite una delle classi del package axis. Si riportano di seguito alcuni esempi di grafici ottenuti da JFreeChart: Figura 3.4.2 – Esempio di grafici con JFreeChart. Gianluca Coda 566/557 Pagina 23 di 131 4 Specifica dei requisiti Per il superamento delle inadeguatezze A), B), C), D) e E) (individuate in 3.1) dell'esistente visualizzatore sono state proposte alcune nuove funzionalità e soluzioni implementative, che riporteremo in questo capitolo. Rispetto ai requisiti stabiliti dai responsabili del Grid, sono stati inseriti alcune funzionalità di visualizzazione aggiuntive come input alla progettazione del visualizzatore (requisiti funzionali). Esse sono state scelte: –per migliorare la leggibilità da parte dell'utente nel raggruppare grafici di interesse di tutte le entità relative alle attese/code del Grid; –per visualizzare particolari relazioni fra code/attese e Job utilizzando un albero genetico; –per consentire di effettuare uno zoom su particolare intervalli di tempo o su uno specifico dato di interesse non ben evidenziato dal scala corrente attivata; –per consentire di avere più rappresentazioni grafiche (caratteristiche) per uno stesso insieme di dati; –per consentire di esportare in diversi formati i grafici generati. Riportiamo gli use case in formato UML dei requisiti appena elencati. 4.1 Use Case Figura 4.1.1 – Alcuni casi d'uso del Sistema di Visualizzazione. Gianluca Coda 566/557 Pagina 24 di 131 Dal caso d'uso riportato in figura 4.1.1 si evincono le principali funzionalità del sistema. Il caso d'uso “visualizza monitoraggio” consente ad un generico utente di conoscere in tempo reale i dati di esercizio della Grid. “visualizza storici” consente, invece, di osservare l'andamento dei dati di esercizio della Grid nel tempo. In questo caso, l'utente ha la possibilità di scegliere vari tipi di rappresentazione dei dati (“modifica tipo menu”) e di salvare i grafici in vari formati (“salva storico”). Per quanto riguarda il caso d'uso “visualizza storici” si riporta un use case diagram che offre un maggior livello di dettaglio: Figura 4.1.2 – Casi d'uso – Funzioni di monitoraggio. 4.2 Tabelle di Cockburn Use case#1 visualizza storici Obiettivo del Use case L'utente desidera visualizzare gli storici di esercizio della Grid Precondizioni Nessuna Condizione d'uscita con successo L'utente ha correttamente visualizzato le informazioni di suo interesse Gianluca Coda 566/557 Pagina 25 di 131 Condizione d'uscita con insuccesso L'utente non ha ottenute le informazioni richieste Attori partecipanti Utente (Attore primario) Trigger (azione di avvio) L'utente accede all'interfaccia web Descrizione Step n° Utente 1 Accede all'interfaccia web Estensione 1: interfaccia irraggiungibile Sistema 2 Mostra il menu di navigazione basato sull'albero genetico della Grid 2.1 In caso di irraggiungibi lità dell'interfacc ia web, mostra un errore esplicativo Use case#2 visualizza grafici giornalieri Obiettivo del Use case L'utente desidera visualizzare gli storici giornalieri di esercizio della Grid Precondizioni L'utente ha effettuato l'accesso all'interfaccia di visualizzazione degli storici della Grid (Use case#1) Condizione d'uscita con successo L'utente ha correttamente visualizzato gli storici giornalieri di suo interesse Condizione d'uscita con insuccesso L'utente non ha ottenute le informazioni richieste Gianluca Coda 566/557 Pagina 26 di 131 Attori partecipanti Utente (Attore primario) Trigger (azione di avvio) L'utente seleziona dal menu principale la voce “giornalieri” Descrizione Step n° Utente 1 Seleziona dal menu principale la voce “giornalieri” Sistema 2 Mostra l'elenco della struttura di genesi della Grid e il grafico dei dati giornalieri totali Estensione 1: interfaccia irraggiungibile 2.1 In caso di irraggiungibi lità dell'interfacc ia web, mostra un errore esplicativo Estensione 2: Caricamento grafico non riuscito 2.1 Il sistema non è in grado di caricare il grafico (ad esempio: plugin applet Java mancante sul brower dell'utente), quindi mostra un Gianluca Coda 566/557 Pagina 27 di 131 errore esplicativo Superordinates •Use case#1 (visualizzazione grafica) Use case#3 visualizza grafici settimanali Obiettivo del Use case L'utente desidera visualizzare gli storici settimanali di esercizio della Grid Precondizioni L'utente ha effettuato l'accesso all'interfaccia di visualizzazione degli storici della Grid (Use case#1) Condizione d'uscita con successo L'utente ha correttamente visualizzato gli storici settimanali di suo interesse Condizione d'uscita con insuccesso L'utente non ha ottenute le informazioni richieste Attori partecipanti Utente (Attore primario) Trigger (azione di avvio) L'utente seleziona dal menu principale la voce “settimanali” Descrizione Step n° Utente 1 Seleziona dal menu principale la voce “settimanali” Estensione 1: Gianluca Coda 566/557 Sistema 2 Mostra l'elenco della struttura di genesi della Grid e il grafico dei dati settimanali totali 2.1 In caso di Pagina 28 di 131 interfaccia irraggiungibile Estensione 2: Caricamento grafico non riuscito irraggiungibi lità dell'interfacc ia web, mostra un errore esplicativo 2.1 Il sistema non è in grado di caricare il grafico (ad esempio: plugin applet Java mancante sul brower dell'utente), quindi mostra un errore esplicativo Superordinates •Use case#1 (visualizzazione grafica) Use case#4 visualizza grafici mensili Obiettivo del Use case L'utente desidera visualizzare gli storici mensili di esercizio della Grid Precondizioni L'utente ha effettuato l'accesso all'interfaccia di visualizzazione degli storici della Grid (Use case#1) Condizione d'uscita con successo L'utente ha correttamente visualizzato gli storici mensili di suo interesse Condizione d'uscita con insuccesso L'utente non ha ottenute le informazioni richieste Gianluca Coda 566/557 Pagina 29 di 131 Attori partecipanti Utente (Attore primario) Trigger (azione di avvio) L'utente seleziona dal menu principale la voce “mensili” Descrizione Step n° Utente 1 Seleziona dal menu principale la voce “mensili” Sistema 2 Mostra l'elenco della struttura di genesi della Grid e il grafico dei dati mensili totali Estensione 1: interfaccia irraggiungibile 2.1 In caso di irraggiungibi lità dell'interfacc ia web, mostra un errore esplicativo Estensione 2: Caricamento grafico non riuscito 2.1 Il sistema non è in grado di caricare il grafico (ad esempio: plugin applet Java mancante sul brower dell'utente), quindi mostra un errore esplicativo Gianluca Coda 566/557 Pagina 30 di 131 Superordinates •Use case#1 (visualizzazione grafica) Use case#5 visualizza grafici annuali Obiettivo del Use case L'utente desidera visualizzare gli storici annuali di esercizio della Grid Precondizioni L'utente ha effettuato l'accesso all'interfaccia di visualizzazione degli storici della Grid (Use case#1) Condizione d'uscita con successo L'utente ha correttamente visualizzato gli storici annuali di suo interesse Condizione d'uscita con insuccesso L'utente non ha ottenute le informazioni richieste Attori partecipanti Utente (Attore primario) Trigger (azione di avvio) L'utente seleziona dal menu principale la voce “annuali” Descrizione Step n° Utente 1 Seleziona dal menu principale la voce “annuali” Estensione 1: interfaccia irraggiungibile Gianluca Coda 566/557 Sistema 2 Mostra l'elenco della struttura di genesi della Grid e il grafico dei dati annuali totali 2.1 In caso di irraggiungibi lità dell'interfacc ia web, mostra un Pagina 31 di 131 errore esplicativo 2.1 Estensione 2: Caricamento grafico non riuscito Il sistema non è in grado di caricare il grafico (ad esempio: plugin applet Java mancante sul brower dell'utente), quindi mostra un errore esplicativo Superordinates •Use case#1 (visualizzazione grafica) Use case#6 Cambia tipo menu Obiettivo del Use case L'utente desidera cambiare il tipo di menu Extend •Use case#1 (visualizzazione grafica) Precondizioni L'utente ha effettuato l'accesso all'interfaccia di visualizzazione degli storici della Grid (Use case#1) Condizione d'uscita con successo L'utente ha correttamente cambiato il tipo di menu Condizione d'uscita con insuccesso - Attori partecipanti Utente (Attore primario) Trigger (azione di avvio) L'utente seleziona inverso” Descrizione Step n° Gianluca Coda 566/557 Utente la voce “albero Sistema Pagina 32 di 131 1 Seleziona la voce “albero inverso” 2 Mostra il menu strutturato secondo l'albero genetico inverso Use case#7 Salvataggio immagine Obiettivo del Use case L'utente desidera salvare l'immagine del grafico attualmente visualizzato Extend •Use case#1 (visualizzazione grafica) Precondizioni L'utente ha effettuato l'accesso all'interfaccia di visualizzazione degli storici della Grid (Use case#1) Condizione d'uscita con successo L'utente ha correttamente salvato il grafico Condizione d'uscita con insuccesso L'utente non riesce a salvarel'immagine Attori partecipanti Utente (Attore primario) Trigger (azione di avvio) L'utente seleziona una voce del menu per visualizzare uno storico Descrizione Step n° Utente 1 Seleziona una voce del menu per visualizzare uno storico 2 Gianluca Coda 566/557 Sistema Mostra il grafico relativo allo storico selezionato dall'utente Pagina 33 di 131 con i bottoni per il salvataggio dell'immagin e in vari formati 3 Preme su uno dei bottoni per salvare l'immagine nel formato desiderato 4 5 Estensione1: Caricamento grafico non riuscito Use case#8 Gianluca Coda 566/557 Mostra il grafico nel formato scelto L'utente salva l'immagine 2.1 Il sistema non è in grado di caricare il grafico (ad esempio: plugin applet Java mancante sul brower dell'utente), quindi mostra un errore esplicativo Visualizza grafico coda Pagina 34 di 131 Obiettivo del Use case L'utente desidera visualizzare il grafico relativo ad una coda attiva sulla Grid Extend •Use case#1 (visualizzazione grafica) Precondizioni L'utente ha effettuato l'accesso all'interfaccia di visualizzazione degli storici della Grid (Use case#1) e selezionato una delle voci relative agli intervalli temporali (giornalieri, settimanali, mensili, annuali) Condizione d'uscita con successo L'utente ha correttamente visualizzato il grafico relativo alla coda ed intervallo temporale selezionati Condizione d'uscita con insuccesso L'utente non è riuscito a visualizzare il grafico atteso Attori partecipanti Utente (Attore primario) Trigger (azione di avvio) L'utente seleziona dal menu principale il nome della coda desiderata Descrizione Step n° Utente 1 Seleziona dal menu principale il nome della coda desiderata Estensione 1: Caricamento grafico non riuscito Gianluca Coda 566/557 Sistema 2 Mostra il grafico relativo alla coda ed all'intervallo temporale selezionati 2.1 Il sistema non è in grado di caricare il grafico (ad esempio: plugin applet Java Pagina 35 di 131 mancante sul brower dell'utente), quindi mostra un errore esplicativo Use case#9 Visualizza grafico VO Obiettivo del Use case L'utente desidera visualizzare il grafico relativo ad una VO attiva sulla Grid Extend •Use case#1 (visualizzazione grafica) Precondizioni L'utente ha effettuato l'accesso all'interfaccia di visualizzazione degli storici della Grid (Use case#1), selezionato una delle voci relative agli intervalli temporali (giornalieri, settimanali, mensili, annuali) e della coda di suo interesse Condizione d'uscita con successo L'utente ha correttamente visualizzato il grafico relativo alla VO ed intervallo temporale selezionati Condizione d'uscita con insuccesso L'utente non è riuscito a visualizzare il grafico atteso Attori partecipanti Utente (Attore primario) Trigger (azione di avvio) L'utente seleziona dal menu principale il nome della VO desiderata Descrizione Step n° Utente 1 Seleziona dal menu principale il nome della VO desiderata 2 Gianluca Coda 566/557 Sistema Mostra il grafico Pagina 36 di 131 relativo alla VO ed all'intervallo temporale selezionati Estensione 1: Caricamento grafico non riuscito 2.1 Il sistema non è in grado di caricare il grafico (ad esempio: plugin applet Java mancante sul brower dell'utente), quindi mostra un errore esplicativo Use case#10 Visualizza grafico other Obiettivo del Use case L'utente desidera visualizzare il grafico relativo ai gruppi, non appartenenti a nessuna VO, attivi sulla Grid Extend •Use case#1 (visualizzazione grafica) Precondizioni L'utente ha effettuato l'accesso all'interfaccia di visualizzazione degli storici della Grid (Use case#1), selezionato una delle voci relative agli intervalli temporali (giornalieri, settimanali, mensili, annuali) e della coda di suo interesse Condizione d'uscita con successo L'utente ha correttamente visualizzato il grafico relativo ai gruppi non appartenenti Gianluca Coda 566/557 Pagina 37 di 131 a nessuna VO ed intervallo temporale selezionati Condizione d'uscita con insuccesso L'utente non è riuscito a visualizzare il grafico atteso Attori partecipanti Utente (Attore primario) Trigger (azione di avvio) L'utente seleziona dal menu principale il nome della coda desiderata Descrizione Step n° Utente 1 Seleziona dal menu principale la voce “other” Estensione 1: Caricamento grafico non riuscito Gianluca Coda 566/557 Sistema 2 Mostra il grafico relativo a tutti i gruppi non appartenenti a nessuna VO relativament e ad una specifica coda ed all'intervallo temporale selezionati 2.1 Il sistema non è in grado di caricare il grafico (ad esempio: plugin applet Java mancante sul brower dell'utente), quindi Pagina 38 di 131 mostra un errore esplicativo Use case#11 Visualizza grafico gruppo Obiettivo del Use case L'utente desidera visualizzare il grafico relativo ad un gruppo attivo sulla Grid Extend •Use case#1 (visualizzazione grafica) Precondizioni L'utente ha effettuato l'accesso all'interfaccia di visualizzazione degli storici della Grid (Use case#1), selezionato una delle voci relative agli intervalli temporali (giornalieri, settimanali, mensili, annuali), della coda e della VO di suo interesse Condizione d'uscita con successo L'utente ha correttamente visualizzato il grafico relativo al gruppo ed intervallo temporale selezionati Condizione d'uscita con insuccesso L'utente non è riuscito a visualizzare il grafico atteso Attori partecipanti Utente (Attore primario) Trigger (azione di avvio) L'utente seleziona dal menu principale il nome della coda desiderata Descrizione Step n° Utente 1 Seleziona dal menu principale il nome del gruppo desiderato 2 Gianluca Coda 566/557 Sistema Mostra il grafico relativo al gruppo ed Pagina 39 di 131 all'intervallo temporale selezionati Estensione 1: Caricamento grafico non riuscito 2.1 Il sistema non è in grado di caricare il grafico (ad esempio: plugin applet Java mancante sul brower dell'utente), quindi mostra un errore esplicativo Use case#12 Visualizza monitoraggio Obiettivo del Use case L'utente desidera visualizzare i dati relativi ai job in esecuzione in quel momento sulla Grid Precondizioni Nessuna Condizione d'uscita con successo L'utente ha correttamente visualizzato le informazioni di suo interesse Condizione d'uscita con insuccesso L'utente non ha ottenuto le informazioni richieste Attori partecipanti Utente (Attore primario) Trigger (azione di avvio) L'utente accede all'interfaccia web Descrizione Step n° Utente 1 Accede all'interfaccia web Gianluca Coda 566/557 Sistema Pagina 40 di 131 2 3 Mostra il menu di navigazione basato sull'albero genetico della Grid L'utente seleziona una voce dal menu principale 4 Estensione 1: interfaccia irraggiungibile Gianluca Coda 566/557 2.1 In caso di irraggiungibi lità dell'interfacc ia web, mostra un errore esplicativo Pagina 41 di 131 5 Architettura del sistema di visualizzazione realizzato 5.1 Scomposizione funzionale in moduli Il sistema di visualizzazione è stato realizzato scomponendo le funzionalità individuate in sottofunzionalità e associando quest'ultime a moduli software separati (si veda a tale riguardo lo schema di architettura riportato in figura 5.1.1). Figura 5.1.1 – Schema dell'architettura del Sistema di Visualizzazione. Gianluca Coda 566/557 Pagina 42 di 131 5.1.1 Modulo di accesso dei dati job Nei file ASCII, forniti dall'output del PARSER (un programma responsabile della produzione dei file job in un stabilito formato), sono presenti organizzati in colonne (si veda tabella 5.1.1.1). Il contenuto di tali file ha la seguente struttura: Data Etichetta 1 … Etichetta n Valore data 1 X1,1 X1,.. X1,n … ... ... Valore data m Xm,1 Xm,... Xm,n Tabella 5.1.1.1 dove Xi,j, con i che va da 1 ad m e j che va da 1 a n, si riferisce alla j-esima etichetta al tempo i-esimo. Sul primo rigo sono presenti i nomi delle colonne che, ad eccezione della prima, rappresentano gli “stati” che i job assumono. Nella prima colonna vi sono le date cui si riferiscono i dati delle colonne successive. Il modulo di accesso dei dati job ha la responsabilità di effettuare, ad intervalli di tempo regolari, il caricamento in memoria dei dati riguardanti i job. In particolare il modulo è costituito da un insieme di thread che periodicamente leggono i file ASCII. Ogni thread del modulo in esame è responsabile delle letture di tutti i file di dati di uno specifico intervallo temporale. Attualmente sono previsti i seguenti intervalli temporali: •giornaliero •settimanale •mensile •annuale Gianluca Coda 566/557 Pagina 43 di 131 5.1.2 Modulo di trasformazioni dei file Job acquisiti Per ogni entità della Grid viene prodotto un file contenente le informazioni dello stato dell'entità. Nasce quindi, in molti casi, l'esigenza di integrare in una unica struttura dati (un unico file di archivio) tutti i dati. Il processo di trasformazione dei file job acquisiti è affidato a due specifici processi (thread): •ConcatenatoreGruppi; •ConcatenatoreVO. Lo scopo dei thread è leggere i file di loro interesse e, come suggerisce il nome, concatenarne i dati. I file ottenuti dal processo di concatenazione rispettano la struttura precedentemente descritta nella tabella 5.1.1.1. In particolare il processo di concatenazione avviene ordinando in una sequenza etichettata tutte le colonne presente nei singoli file. Nelle immagini a seguire si riporta il processo di concatenazione di tre file (File A, File B e File C). L'unico vincolo nel formato dei file per una corretta concatenazione riguarda il numero di righe e di colonne che deve essere lo stesso per tutti i file. In tale contesto, tale vincolo non costituisce un problema in quanto le informazioni di monitoraggio della Grid sono standardizzate sia dal punto di vista temporale (la colonna Data per ogni file è uguale) che di tipi di informazioni (tutti i file hanno ugual numero di colonne). Gianluca Coda 566/557 Pagina 44 di 131 Figura 5.1.2.1 – Esempio di funzionamento del modulo di concatenazione. I nuovi nomi delle etichette sono generate per mezzo di concatenazione dei nomi dei file e delle relative etichette (<NuovaEtichetta>= <NomeFile>_<Etichetta>). Questa operazione è fondamentale per assegnare in maniera univoca i nomi delle nuove etichette. Nella figura a seguire è riportato un esempio che crea il nuovo nome della colonna mediante concatenazione appena descritta. Gianluca Coda 566/557 Pagina 45 di 131 Figura 5.1.2.2 – Schema concatenazione dei nomi delle etichette. Con tale scelta di rappresentazione è possibile ottenere senza ambiguità digrammi di visualizzazione del tipo riportato nella figura che segue. Figura 5.1.2.3 – Esempio di rappresentazione ad istogrammi. L'utilità di rappresentare i dati in tal modo immagazzinati, consiste nel fatto che a partire da tale rappresentazione si può generare un grafico più dettagliato rispetto a quello totale. Consideriamo l'andamento di un grafico che indica il numero di persone presenti in Gianluca Coda 566/557 Pagina 46 di 131 una sala cinematografica durante cinque giorni di proiezione. Una prima rappresentazione di tale scenario può essere del tipo riportato in figura 5.2.1.4. a) (di colore arancio). Se si è interessati a conoscere la distribuzione del numero di uomini e donne si può rappresentare la situazione distinguendo i dati per ogni categoria di persone (grafici di colore blu e rosso). Con la scelta appena riportata è possibile ricostruire il grafico totale delle persone (figura 5.2.1.4. b) riportando anche la distinzione degli insiemi delle parti (uomini e donne). La tecnica adottata in questa tesi è stata quella di ottenere la rappresentazione della totalità (ad esempio delle persone) aggregando i dati relativi delle parti (uomini e donne nella figura 5.2.1.4. b). Figura 5.2.1.4 – Rappresentazioni di gruppi in scenari complessi. Gianluca Coda 566/557 Pagina 47 di 131 5.1.3 Generazione di pagine JSP Per sito dinamico si intende un sito che pur mantenendo le stesse funzionalità (ad esempio una modalità di presentazione dei dati di un'interfaccia) può fornire un contenuto di dati variabili che è determinato in genere da un aggiornamento di un data base di riferimento che subisce un aggiornamento. Per implementare il tipo di dinamicità appena descritto, sono stati creati dei sistemi per automatizzare i processi di comunicazione tra un richiedente che possiede una modalità di presentazione (client) e un sistema che fornisce la risposta alla richiesta (server). Le tecnologie Server Side CGI (Common Gateway Interface) non sono prese da noi in considerazione per i loro riconosciuti svantaggi come ad esempio un eccessivo peso sul processore ed attività computazionali di produzione di output a carico del sistema. Entrambi gli svantaggi appena elencati rappresentavano un problema che non poteva essere sottovalutato sopratutto per applicazioni Web-Oriented che possono incorrere in un elevato numero di interrogazioni. La Sun Microsystem con la creazione delle Servlet è riuscita a risolvere i problemi delle applicazioni CGI. Tale tecnologia è stata quella da noi adottata nel sistema di visualizzazione realizzato. Ne riportiamo la descrizione delle entità di base di questa tecnologia. Le Servlet sono delle classi realizzate in Java dotate di una definita struttura e sono richiamate al solo scopo di generare i contenuti dinamicamente. Uno dei principali vantaggi delle Servlet è che il carico di lavoro che prima era affidato al processore ora passa al Servelt Engine, programma caricato in memoria che si occupa della gestione dei processi generati dalle richieste degli utenti. Un particolare Servlet Engine è Tomcat, denominato anche Servlet Container o Servlet/JSP. Tomcat può essere inteso anche come un Web Server, come ad esempio Apache, perché riesce sia ad interpretare le richieste inviate dai client, sia a provvede alla generazione dinamica dei dati. Gianluca Coda 566/557 Pagina 48 di 131 Rispetto ad Apache, Tomcat offre alcune caratteristiche aggiuntive che descriveremo più avanti in questo paragrafo. La politica di gestione degli input/output di Tomcat non è molto differente da quella adottata da un qualsiasi Web Server. Dopo l'inserimento e l'acquisizione del url, vengono visualizzate alcune strutture che esistono già al momento dell'attivazione di Tomcat. Queste strutture prendono il nome di Contesto, le quali sono controllate da oggetti che sono attivi sia nel loro ambito di azione che per gli utenti che vi accedono. Il Web Server assegna ad ogni utente un Contenitore che è chiamato Sessione che contiene le risorse disponibili per uno specifico utente. Tramite questa Sessione l'utente ha la possibilità di inviare le richieste per la generazione dinamica dell'output. Anche le richieste possono essere intese come dei Contenitori che hanno un ciclo di vita che ha una durata che va dall'inizio della richiesta sino al tempo del suo soddisfacimento. Per quanto riguarda i metodi di gestione degli input/output invece Tomcat è caratterizzato dall'istanza di un particolare oggetto chiamato Request (in sigla Req) che viene lanciato nel momento in cui si inserisce l'url. In conseguenza dell'istanza di Req viene istanziato, dal Servlet Engine, un altro oggetto chiamato Response( in sigla Res). Entrambi gli oggetti saranno inviati al Contesto (inteso come oggetto) per soddisfare la richiesta del client. Infine, un ulteriore oggetto è istanziato da Tomcat, di nome Ses. Il ruolo di tale oggetto è di identificare in maniera univoca l'utente e di verificare che la sua Sessione sia attiva. Gli esiti di tali operazioni possono essere: •la Servlet ha esaudito la richiesta e delega Tomcat per la consegna dell'output al client; •la risorsa necessaria per soddisfare la richiesta non è disponibile presso il Contesto utilizzato quindi Tomcat ha il compito di riavviare la procedura per cercare la risorsa in altri Contesti; •la richiesta può essere soddisfatta nel Contesto, ma tramite un'altra risorsa si crea un Gianluca Coda 566/557 Pagina 49 di 131 Forward verso la giusta risorsa. La scelta della tecnologia JSP ha consentito di ottenere un'interfaccia utente raggiungibile via Web dove i contenuti vengono generati dinamicamente. Il diagramma a seguire mostra l'architettura del livello di presentazione adottato, conosciuto come Model 1 (approccio introdotto dalle specifiche JSP 0.92). TOMCAT File System BROWSER JSP Data Base Figura 5.1.3.1 – Ruolo JSP/Tomcat nella visualizzazione web. Con questa soluzione le pagine JSP svolgono il seguente ruolo: •l'utente, interagendo con il Browser, richiede una pagina JSP; •il codice contenuto nella JSP richiesta esegue operazioni di controllo ed accesso ai dati; •la pagina JSP genera l'output HTML e lo invia al Browser. Le JSP contengono, in particolare, codice HTML per i contenuti statici e codice JAVA per la generazione di contenuti HTML dinamici; hanno quindi la responsabilità di gestione del flusso di controllo e di presentazione dei dati. Nel caso in esame, per la generazione dei dati dinamici, le JSP accedono ad un Database ed al file system del server che le ospita. Prevedendo sviluppi futuri del visualizzatore è stato scelto di strutturare le JSP Gianluca Coda 566/557 Pagina 50 di 131 secondo una scomposizione basata sugli scopi. Una generica pagina HTML ha la seguente struttura: HTML INTESTAZIONE MENU i-Frame centrale Figura 5.1.3.2 – Regioni del layout del Sistema di Visualizzazione. I componenti costituenti la pagina Web sono i seguenti: •INTESTAZIONE: contenuto statico, presente in tutte le schermate dell'applicazione, che costituisce l'intestazione delle pagine; •MENU: menu generato dinamicamente. Consente di navigare nella struttura dati delle informazioni (alberi genetici) da rappresentare. Nel contesto in esame tale struttura replica una porzione del file system contenente i dati di monitoraggio. Allo stato attuale sono presenti due menu distinti che offrono due tipologie di viste della struttura dati. ◦Il primo menu, denominato “Albero genetico” …......, ◦il secondo, “Albero inverso”, ........ •i-Frame centrale: porzione centrale in cui, alla selezione di una voce del Menu, viene visualizzata, in Gianluca Coda 566/557 Pagina 51 di 131 maniera dinamica, una rappresentazione delle informazioni relative alla voce selezionata. Riportiamo una schermata di esempio dell'applicazione realizzata. Figura 5.1.3.3 – Esempio di visualizzazione del sistema realizzato. Come si può osservare nell'interfaccia viene segnato in rosso l'intestazione statica, in verde il menu dinamico e, in giallo, l'area riservata al frame centrale. Gianluca Coda 566/557 Pagina 52 di 131 5.2 Diagrammi UML di progettazione 5.2.1 Component Diagram Figura 5.2.1.1 – Esempio di rappresentazione del diagramma delle componenti. Gianluca Coda 566/557 Pagina 53 di 131 5.2.2 Class Diagram L'intera applicazione realizzata risiede all'interno di un pacchetto denominato “qjmon”: Figura 5.2.2.1 – Package qjmon contenente l'intera applicazione realizzata. Il pacchetto “qjmon” contiene una classe Configuratore che ha la responsabilità di eseguire i thread dell'applicazione realizzata. È presente inoltre un pacchetto, “viewer”, che contiene il cuore dell'applicazione realizzata. Figura 5.2.2.2 – Contenuto del package qjmon. Gianluca Coda 566/557 Pagina 54 di 131 Si riporta di seguito il contenuto del pacchetto “viewer” con la descrizione delle responsabilità di ogni classe: Figura 5.2.2.3 – Diagramma delle classi. Nome Tipo Responsabilità Concatenatore Classe Astratta Generico Thread che si occupa della concatenazione dei dati. ConcatenatoreGruppi Classe Thread che ha la responsabilità concatenare i file dei dati dei gruppi. di ConcatenatoreVO Classe Thread che ha la responsabilità concatenare i file dei dati delle VO. di GeneratoreGrafici Classe Thread che, periodicamente, genera una lista di grafici. Grafico Classe Rappresenta un grafico. Per maggiori dettagli si rimanda alla documentazione JavaDoc allegata in formato elettronico al presente lavoro di tesi. Gianluca Coda 566/557 Pagina 55 di 131 5.2.3 Sequence Diagram Sequenza di operazione eseguite dal configuratore per avviare i Thread della applicazione realizzata. Tale sequenza viene eseguita per ogni intervallo temporale. Figura5. 2.3.1 – Diagramma di sequenza. Gianluca Coda 566/557 Pagina 56 di 131 6 Sviluppo dell'applicazione In questo paragrafo riportiamo la descrizione degli aspetti implementativi del generatore di grafici realizzato. I moduli software che sono stati realizzati rispecchiano la progettazione effettuata (riportata in diagrammi UML nel paragrafo precedente). L'intero sistema è stato sviluppato in JAVA, avvalendosi per la parte della generazione dei grafici, della libreria JFreeChart. 6.1 Dal diagramma delle classi al codice Java Nei diagrammi di progettazione UML sono presenti le seguenti classi: –ConcatenatoreGruppi.java –ConcatenatoreVO.java –Configuratore.java –GeneratoreGrafici.java –Grafico.java La classe ConcatenatoreGruppi.java si occupa della generazione del file contenente i dati relativi a tutti i gruppi appartenenti ad una stessa VO. ConcatenatoreVO.java funziona in maniera simile alla precedente ma relativamente all'albero di genesi inverso, in pratica concatena tutti i file di una determinata VO presenti su tutte le code. La classe Configuratore.java è quella che contiene i metodi che avviano i thread, tali thread sono 3 per ogni “intervallo di tempo” di dati (giornalieri, settimanali etc etc), i funzionamento due Concatenatori è stato già spiegato. Gianluca Coda 566/557 Pagina 57 di 131 GeneratoreGrafici.java è responsabile della generazione di un classe di grafici. Grafico.java è il cuore dell'applicazione, quella che implementa i metodi ed utilizza le librerie di JFreeChart. 6.2 Organizzazione dei file di sistema contenenti i dati dei grafici I dati riguardanti i job sono memorizzati su file ASCII (leggibili con qualsiasi editor di testo). Tali file sono memorizzati in un insieme di file di sistema (cartelle) che hanno una radice comune chiamata “struttura/”. Le cartelle prossime alla radice sono 4, le quali rappresentano gli intervalli di tempo di validità delle caratteristiche delle code: giorno, settimana, mese ed anno. Ogni sottocartella a partire da tali 4 cartelle rappresenta un insieme relativo ad una coda, organizzazione virtuale, gruppo. Figura 6.2.1 – Organizzazione del File System. Gianluca Coda 566/557 Pagina 58 di 131 6.3 Libreria di visualizzazione JFreeChart Il modulo designato alla generazione dei grafici per mezzo della libreria JFreeChart è costituito dalle classi: •GeneratoreGrafici •Grafico GeneratoreGrafici ha la responsabilità di generare un insieme di grafici. In particolare vi sono 4 possibili insiemi: •giornalieri •settimanali •mensili •annuali per ognuno di questi è responsabile un thread GeneratoreGrafici che, per mezzo della classe Grafico, a partire dai file dei dati, genera i relativi grafici. La classe Grafico a partire da un file di dati genera il relativo grafico nei seguenti formati: PNG, JPEG, PDF e SVG. In tali grafici i valori delle ascisse rispecchiano i valori della prima colonna del file (cioè la data) mentre le ordinate rappresentano i valori dei relativi stati. Le etichette delle legende dei grafici sono ricavate a partire dalle intestazioni dei file. 6.4 Tipi di grafici Il Sistema realizzato prevede tre tipi di plot per la rappresentazione dei dati di monitoraggio della Grid, denominati: Gianluca Coda 566/557 Pagina 59 di 131 1.a linee; 2.a curve stacked (impilate o a pila); 3.ibrido. Tutte le tipologie sono rappresentazioni nel piano cartesiano e prevedono l'uso di un colore univoco per ogni dato rappresentato al variare del tempo. L'asse delle ordinate ha come unità di misura il Job, mentre l'asse delle ascisse rappresenta intervalli di tempo (di osservazione) (figura 6.4.1: solo gli assi con le unità di misura senza dati). Figura 6.4.1 – Esempio di rappresentazione degli assi. Nel primo tipo di rappresentazione, a linee, i dati monitorati vengono rappresentati come spezzate i cui estremi sono due punti di osservazione consecutivi (figura 6.4.2: esempio di rappresentazione solo a linee). Gianluca Coda 566/557 Pagina 60 di 131 Figura 6.4.2 – Esempio di rappresentazione dei dai mediante rappresentazione a linee. In questo tipo di grafico potrebbero presentarsi problemi di sovrapposizione delle linee (dati con valori che si discostano di poco gli uni dagli altri, figura 6.4.3: esempio di diagramma a linee con valori sovrapposti) che comprometterebbero l'utilità stessa della rappresentazione. Figura 6.4.3 – Esempio di rappresentazione di dati per mezzo di linee. Gianluca Coda 566/557 Pagina 61 di 131 Il secondo tipo di rappresentazione, a curve impilate, prevede che i valori di un dato, nel tempo, vengono rappresentati tramite spezzate i cui estremi sono coppie consecutive di punti di osservazione. L'area sottesa dalle spezzate viene inoltre colorata in maniera univoca (figura 6.4.4: diagramma stacked con una sola area rappresentata). Figura 6.4.4 – Esempio di rappresentazione dei dati mediante aree. Una caratteristica importante di questa rappresentazione è il concetto di “curve impilate”. Questo concetto può essere espresso nel seguente modo: Siano da C1 a Ck le k curve stacked rappresentate in un grafico; C i={x 1 , .. , x n } i i con i=1,... , k , n i la cardinalità dell'insieme delle osservazioni dell'i-esima curva, x j , con i Gianluca Coda 566/557 j=1,... , n , la j-esima osservazione dell'i-esima curva. Pagina 62 di 131 La rappresentazione a curve stacked, prevede che la generica osservazione x j i della curva Ci , sia rappresentata dal punto A= j , x j x j . i i−1 In altri termini i valori di ogni curva vengono sommati ai corrispondenti (relativamente al tempo) valori della curva precedente. In figura 6.4.5 viene riportato un esempio di due curve stacked. È da notare che i valori della seconda curva sono ottenuti come somma dei valori della prima e della seconda. Figura 6.4.5 – Esempio di rappresentazione di più dati mediante aree impilate. Questo tipo di rappresentazione rimedia ai problemi di sovrapposizione della rappresentazione a linee, consentendo di valutare in maniera intuitiva gli andamenti di un dato in rapporto agli altri. L'ultima modalità è la “ibrida” (o mix) che, come suggerisce il nome, unisce le due precedenti modalità fornendo un più rapido strumento di monitoraggio e ne migliora la leggibilità in quanto possono essere rappresentate curve che indicano soglie interessanti (come ad esempio totali, disponibili) in modalità “linea” e quindi di lettura Gianluca Coda 566/557 Pagina 63 di 131 immediata mentre tutte le altre curve con la modalità “curve”. In questo modo si ha anche il vantaggio di non produrre grafici troppo dispersivi. Nell'esempio in figura 6.4.6 è possibile osservare un grafico che utilizza sia linee che aree per la rappresentazione dei dati (esempio di grafico ibrido). Figura 6.4.6 – Esempio di rappresentazione combinata dei dati. 6.5 L'esportazione verso il portale Tomcat, jsp, scelta dei contenuti statici/ non statici, aggiornamento grafici. 6.5.1 I formati immagini statici PNG, acronimo di Portable Network Graphics, è un particolare formato compresso di immagini, nato intorno al 1995, e di lì a poco approvato dal W3C (World Wide Web Consortium), per fronteggiare la richiesta di pagamento, per l'utilizzo in programmi, da parte dei detentori del brevetto di un altro formato molto diffuso in quel periodo, la GIF. La sua estensione è .png. Gianluca Coda 566/557 Pagina 64 di 131 Il PNG immagazzina immagini senza perdere informazioni ed è in grado di memorizzarle in colori reali (quindi non ha il limite, come per la GIF, di 256 colori) ed ha un canale dedicato per la trasparenza. Inizialmente non tutti i programmi erano in grado di leggere tali immagini ma oggi giorno è supportato da tutti i maggiori programmi di grafica e di navigazione. Tant'è che pur essendo scaduto il brevetto sulla GIF, già dal 2003, non si teme minimamente che l'utilizzo di tale formato possa diminuire. JPEG, acronimo di Joint Photographic Experts Group, anche esso è un formato gratuito, è il più diffuso formato di compressione per la memorizzazione di immagini ed è anche il più comune formato sul web. Ad esso è associata più di un'estensione, solitamente .jpg o .jpeg ma ce ne sono anche altre. Sono definiti due metodi di compressione, uno di tipo “lossless”, quindi senza perdita di informazione ed un altro di tipo “lossy”, basato sull'uso della trasformata discreta del coseno, cioè con perdita di informazione, per i file che utilizzano questo metodo, solitamente, sono utilizzate altre estensioni. Portable Document Format, il cui acronimo è PDF come la stessa estensione è un formato di file che può contenere sia immagini che testo. É un formato utilizzabile da chiunque per la creazioni di applicazioni che effettuano operazioni di lettura o scrittura. Ha la capacità di memorizzare immagini nella risoluzione preferita. Pur essendo il formato più diffuso nel suo genere, potrebbe presentare dei problemi in fase di stampa nel caso in cui siano stati usati caratteri locali non settati correttamente nelle opzioni di stampa oppure che siano presenti dipendenze da alcuni font esterni allo stesso. In ogni caso per ovviare a questi inconvenienti sono stati standardizzati alcuni sotto-formati, come il PDF/A, che incorpora le fonti nel file stesso, ai fini di una portabilità anche nel tempo (stampa anche tra due decenni oltre la creazione del file. Gianluca Coda 566/557 Pagina 65 di 131 Grazie al rilascio delle specifiche del formato è possibile creare un file PDF utilizzando molti linguaggi di programmazione. Altro aspetto del formato è la protezione crittografica che pur essendo attualmente molto debole, non è escluso che per il futuro sia rafforzata. 6.5.2 I formati immagini interattivi Il formato SVG, cioè Scalable Vector Graphics, è in grado di visualizzare immagini scalabili dimensionalmente, grazie ad oggetti di grafica vettoriale. Questo si traduce in una conservazione della qualità pur ridimensionando l'immagine. Ciò è possibile in quanto l'immagine è definita matematicamente e non è solo un insieme statico di pixel. Inoltre è bene osservare che la grafica vettoriale offre il vantaggio di conservare maggiori informazioni, rispetto alla sua “antagonista”, la grafica “raster”, in minore spazio ma che ha di contro che necessita di una potenza di calcolo da parte del processore per l'elaborazione di immagini complesse. Infine è da evidenziare che tale operazione non è calcolabile a priori, come succede con la grafica “raster”, quindi ci potremmo trovare nella condizione di non avere abbastanza risorse per completare il processo. In ogni caso è un formato indicato per grafici o dati cartografici in generale e non per per fotografie. Tale formato è stato raccomandato dal W3C, già nel 2001, che lo ha privilegiato ai suoi concorrenti VML e PGML, proposti rispettivamente dalla coppia Microsoft – Macromedia e dalla coppia Adobe - Sun Microsystems. Oggi giorno, il formato dovrebbe essere supportato nativamente dalle ultime versioni dei più diffusi browser mentre per altri è necessario installare dei plug-in sviluppati da Adobe o da Corel. Per quanto riguarda alcuni casi, tipo Safari, si attende una prossima implementazione. In ogni caso sono disponibili visualizzatori e librerie specifiche come la Batik SVG Toolkit per l'integrazione in codice Java. Gianluca Coda 566/557 Pagina 66 di 131 6.5.3 Le scelte effettuate Le pagine implementate i formati statici: su tali file non è possibile interagire se non con i classici strumenti del proprio visualizzatore di immagini ma che soffrono di una perdita di dettaglio. Per quanto riguarda le SVG, vista la mia esperienza, posso dire che non è garantita la compatibilità al 100%; pur istallando diversi plug-in le funzionalità utilizzabili non sono le stesse a seconda del browser che si utilizza ed in alcuni casi si presentano crash improvvisi. Quindi, pur riconoscendo grandi potenzialità, credo che sia un formato ancora acerbo. A tale scopo è stata implementata una Applet grazie alla quale è possibile visualizzare il ToolTip (l'etichetta che compare dove è posizionato il puntatore) ed effettuare operazioni di aumento di dettaglio, nel caso in cui ci interessasse solo un particolare intervallo (sia in ascisse che ordinate) rielaborando “al volo” un nuovo grafico. Ovviamente per utilizzare tale strumento è necessario che il nostro browser supporti le Applet. L'Applet in pratica viene caricata una sola volta dal nostro browser ed ad ogni interrogazione riceve in input un parametro che equivale alla stringa corrispondente al percorso del file contenente i dati e residente sul server da leggere. Tramite l'inserimento di nuovo codice Java è possibile generare altri eventi associabili al click del mouse o combinazioni di tasti. Gianluca Coda 566/557 Pagina 67 di 131 7 Conclusioni e futuri sviluppi In questa tesi si è progettato e realizzato un generatore di grafici per monitorare l'attività dei sistemi di attesa (code) e dei job di una Grid. I requisiti del generatore sono stati definiti con lo scopo di migliorare le funzionalità e le modalità di utilizzazione di un esistente visualizzatore. L'analisi ha portato ad individuare alcune tipologie di grafi più adeguati al tipo di variabili e relazioni da rappresentare. Un particolare diagramma è stato identificato per rappresentare genesi di popolazioni di attese (Virtual Organization, Gruppi e Code) in varie modalità di visualizzazione temporale (ad esempio rispetto ad un giorno, una settimana, un mese etc). Il generatore grafico è stato progettato secondo tecniche di Ingegneria del Software attraverso l'utilizzazione di diagrammi UML. Il sistema è stato realizzato secondo un'architettura che utilizza software Open Source Java, in particolare JFreeChart il quale garantisce piena compatibilità con tutte le libreria Java, ciò in prospettiva anche di eventuali estensioni del sistema. Tra le realizzazioni, infine, è stato implementato un modulo software che rende possibile la visualizzazione dei grafici su WEB. In relazione agli sviluppi futuri si prevede di aggiungere alcune funzionalità che diano la possibilità all'utente di scegliere quali dati mostrare nel grafico (selezionando specifici elementi e parametri della Grid) così da offrire uno strumento che, in maniera interattiva, consente di confrontare dati in uno stesso grafico. Per la funzionalità descritta sarebbe utile offrire all'utente la possibilità di scegliere il tipo di rappresentazione grafica da altre librerie grafiche. Tra i possibili sviluppi è di un certo interesse lo sviluppo di alcune funzionalità di monitoraggio che riguardano l'evidenziazione di stati di funzionamento anomalo della Grid i quali richiedono un intervento dell'amministratore. Gianluca Coda 566/557 Pagina 68 di 131 8 Bibliografia [1] UML Jim Arlow, iLa Neustadt, UML e Unified Process, McGraw-Hill, 2003 [2] J. T. Roff, Fondamenti di UML McGraw-Hill, 2003 [3] Winston M.E., Chaffin R., Herrmann D. A taxonomy of part-whole relations. Cognitive Science 11;417-444; 1987 [4] Artale A., Franconi E., Guarino N., Pazzi L. Part-Whole Relations in Object-Centred System: An overview. Data & Knowledge Engineering (DKE) journal 20 (1996) 347-383 – North-Holland, Elsevier [5] Gnuplot homepage http://www.gnuplot.info/ [6] JFreeChart: http://www.jfree.org/jfreechart/ [7] R. Pesenti, Teoria delle Code o File d'Attesa, http://venus.unive.it/pesenti/Old/docwww/Didattica/Logistica/Code.pdf [8] Ian Foster, Carl Kesselman, Steven Tuecke - The Anathomy of the Grid [9] Ian Foster, Carl Kesselman, Jeffrey M. Nick, Steven Tuecke - The Physiology of the Grid [10] Cnaf – http://www.cnaf.infn.it/ [11] Luciano Gaido - L'implementazione ed il deployment di una Grid http://server11.infn.it/testbed-grid/meetings/elba-ita.pdf Gianluca Coda 566/557 Pagina 69 di 131 9 Appendici – Installazione e funzionamento del sistema 9.1 Messa in esercizio Insieme ai sorgenti, l'applicazione viene rilasciata sotto forma di java bytecode in formato .jar. All'interno del jar vi è una classe “Configuratore.java” che rappresenta un esempio di utilizzo dell'applicazione realizzata. “Configuratore.java” avvia una serie di thread di esempio per la generazione di grafici a partire da alcuni file dati. È necessario modificare il metodo main della classe “Configuratore.java” con dei percorsi reali ai file dei dati. Per avviare l'applicazione d'esempio è necessario dare il seguente comando di shell bash: java -jar qjmon_viewer.jar L'interfaccia utente è rilasciata sotto forma di pagine jsp. La messa in esercizio dell'interfaccia avviene attraverso il deploy su tomcat. Tale processo può essere eseguito dall'interfaccia di amministrazione web di quest'ultimo o, più semplicemente, copiando la cartella “qjmon_viewer_gui” nella cartella “webapps” di Tomcat. Nella cartella “bin” di Tomcat va creata una cartella “struttura” all'interno della quale vanno inseriti i link simbolici alle cartelle contenenti i file dei dati (giornalieri, settimanali, …). Gianluca Coda 566/557 Pagina 70 di 131 9.2 Guida d'uso Il sistema di generazione dei grafici presenta un'interfaccia la quale propone all'utente alcuni modalità di presentazione/visualizzazione dei dati. L'interfaccia ha una forma del tipo: Figura 9.2.1 – Esempio di scherma per la visualizzazione degli storici giornalieri. Nella parte sinistra dell'interfaccia è presente un menu. Attraverso tale menu è possibile selezionare per ogni entità della Grid (code, VO, gruppo) un grafico. Nel menu esistono tre modalità di visualizzazione di alberi genetici di flussi: •(1) albero genetico; •(2) albero inverso; Gianluca Coda 566/557 Pagina 71 di 131 •(3) albero ridotto. Nelle prime due modalità il livello più alto dell'albero (si veda figura XX) è relativo agli intervalli temporali di osservazione dei dati (assente nella modalità di visualizzazione albero ridotto). Altra differenza fra i (1), (2) e quelli di tipo (3) consiste nel fatto che i primi possiedono una rappresentazione dei dati storicizzati mentre quelli di tipo (3) visualizzano i dati in forma tabellare. La funzionalità (1) produce una volta attività un grafico del tipo riportato nella figura 9.2.2. Il secondo livello degli alberi genetici forniscono una rappresentazione delle Code presenti sulla Grid. Il terzo livello degli alberi visualizzano le VO relative ad ogni Coda. Infine le voci di quarto livello indicano i gruppi relativi alla VO selezionata. Figura 9.2.2 Esempio di menu genetico. Gianluca Coda 566/557 Figura 9.2.3 Esempio di albero genetico inverso Pagina 72 di 131 Alla selezione di una delle voci del menu, nella parte centrale della schermata, viene caricata l'applet contenente il relativo grafico, come mostrato nella seguente figura: Figura 9.2.4 – Esempio di applet grafica relativa ai dati di un generico gruppo. nella parte inferiore del layout di visualizzazione sono presenti vari bottoni che consentono di salvare i grafici in vari formati (PNG, JPEG, PDF, SVG). Nell'immagine in figura è presenta la schermata relativa alla selezione del formato PDF la quale viene visualizzata attivando il programma di visualizzazione ADOBE ACROBAT (si veda figura 9.2.5). Gianluca Coda 566/557 Pagina 73 di 131 Figura 9.2.5 – Esempio di esportazione di un grafico in formato PDF. La funzionalità consente anche di effettuare un'operazione di zoom sui grafici, selezionando una desiderata porzione tramite l'utilizzo del mouse. Nelle figure 9.2.6 e 9.2.7 è riportata un esempio di attivazione della funzionalità di zoom attraverso le fasi. Figura 9.2.6 – Dettaglio della funzionalità zoom. Gianluca Coda 566/557 Pagina 74 di 131 Figura 9.2.7 – Esempio del grafico della figura 9.2.6 a seguito dello zoom. Un'altra funzione interattiva che l'applet offre sono le etichette che indicano i valori dei dati in corrispondenza del puntatore del mouse. Nella figura a seguire vi è un esempio di un'etichetta che indica il nome, la data ed il valore di un dato. Figura 9.2.8 – Esempio di tooltip. Gianluca Coda 566/557 Pagina 75 di 131 La schermata relativa al monitoraggio dei Job presenta un menu basato sull'albero genetico ridotto Figura 9.2.9 – Esempio di albero genetico ridotto. La rappresentazione utilizzata in questo caso è in forma tabellare come è possibile osservare dall'esempio in figura: Figura 9.2.10 – Esempio di schermata relativa ai dati di monitoraggio di una VO. Gianluca Coda 566/557 Pagina 76 di 131 nel caso in cui non sono presenti dati per una voce selezionata viene mostrata una tabella contenente unicamente l'intestazione. Figura 9.2.11 – Esempio di schermata relativa ad una tabella vuota. Gianluca Coda 566/557 Pagina 77 di 131 9.3 Codice sorgente CONFIGURATORE.JAVA: package qjmon; import import import import import import import import import import qjmon.viewer.ConcatenatoreVO; qjmon.viewer.GeneratoreGrafici; qjmon.viewer.ConcatenatoreGruppi; qjmon.viewer.Grafico; java.io.File; java.io.IOException; java.util.LinkedList; java.util.logging.Level; java.util.logging.Logger; java.util.regex.Pattern; /** * Classe main con la responsabilità di inizializzare ed avviare i thread * dell'applicazione. * * @author Gianluca Coda */ public class Configuratore { //Cartella contenente i dati giornalieri private static final String folderDatiGiornalieri = new String ("/root/apache-tomcat-6.0.24/webapps/definitivo/giornalieri"); //Cartella contenente i dati settimanali private static final String folderDatiSettimanali = new String ("/root/apache-tomcat-6.0.24/webapps/definitivo/settimanali/"); //Frequenza di aggiornamento, in secondi, per i dati giornalieri private static final int refrehGiornalieri = 10; //Frequenza di aggiornamento, in secondi, per i dati settimanali private static final int refreshSettimanali = 50; //Estensioni file nel primo livello private static final String firstLevelExt = ".VOconc"; //Estensioni file nel secondo livello private static final String secondLevelExt = ".coda"; //Estensioni file nel terzo livello private static final String thirdLevelExtFirst = ".vo"; //Estensioni di secondo tipo dei file nel terzo livello private static final String thirdLevelExtSecond = ".GRconc"; //Estensioni file nel quarto livello private static final String fourthLevelExt = ".gruppo"; Gianluca Coda 566/557 Pagina 78 di 131 /** * Avvia i thread per la concatenazione dei file * * @param refresh * frequenza di aggiornamento delle concatenazioni * * @param rootFolder * directory sorgente contenente i file da concatenare * * @throws IOException * in caso di errori durante l'avvio dei concatenatori */ private static void avviaConcatenatore(int refresh, String rootFolder) throws IOException { //Crea un concatenatore per i gruppi ConcatenatoreGruppi concatenatoreGruppi = new ConcatenatoreGruppi(refresh,rootFolder); //Crea un thread per concatenare i gruppi Thread threadConcatenatoreGruppi = new Thread(concatenatoreGruppi); //Avvia il thread per la concatenazione dei gruppi threadConcatenatoreGruppi.start(); //Crea un concatenatore per le VO ConcatenatoreVO concatenatoreVO = new ConcatenatoreVO(refresh,rootFolder); //Crea un thread per concatenare le VO Thread threadConcatenatoreVO = new Thread(concatenatoreVO); //Avvia il thread per la concatenazione delle VO threadConcatenatoreVO.start(); } /** * Avvia i generatori di grafici. * * @param refresh * frequenza di aggiornamento dei grafici * * @param rootFolder * cartella sorgente contenente i file dei dati da graficare */ private static void avviaGeneratoreGrafici(int refresh, String rootFolder) { LinkedList<Grafico> listaGrafici = new LinkedList<Grafico>(); File root = new File(rootFolder); if (root.isFile()) throw new RuntimeException(rootFolder+" isn't a directory"); Gianluca Coda 566/557 Pagina 79 di 131 for (File firstLevelFile : root.listFiles()) if (firstLevelFile.isFile() && firstLevelFile.getName().substring(firstLevelFile.ge tName().length()-firstLevelExt.length()).equals(firstLevelExt)) listaGrafici.add(new Grafico(firstLevelFile.getAbsolutePath(), firstLevelFile.getAbsolutePath())); else if (firstLevelFile.isDirectory()) for (File secondLevelFile : firstLevelFile.listFiles()) if (secondLevelFile.isFile() && secondLevelFile.getName().substring(seco ndLevelFile.getName().length()secondLevelExt.length()).equals(secondLevelExt)) listaGrafici.add(new Grafico(secondLevelFile.getAbsolutePath(), secondLevelFile.getAbsolutePath())); else if (secondLevelFile.isDirectory()) for (File thirdLevelFile : secondLevelFile.listFiles()) if (thirdLevelFile.isFile() && ( thirdLevelFile.getName().sub string(thirdLevelFile.getName().length()thirdLevelExtFirst.length()).equals(thirdLevelExtFirst) || thirdLevelFile.getName().sub string(thirdLevelFile.getName().length()thirdLevelExtSecond.length()).equals(thirdLevelExtSecond) ) ) listaGrafici.add(new Grafico(thirdLevelFile.getAbsolutePath(), thirdLevelFile.getAbsolutePath())); (thirdLevelFile.isDirectory()) else if for (File fourthLevelFile : thirdLevelFile.listFiles()) if (fourthLevelFile.isFile() && fourthLevelFile. getName().substring(fourthLevelFile.getName().length()fourthLevelExt.length()).equals(fourthLevelExt) Gianluca Coda 566/557 Pagina 80 di 131 ) listaGrafici.add (new Grafico(fourthLevelFile.getAbsolutePath(), fourthLevelFile.getAbsolutePath())); for(Grafico gr : listaGrafici) { System.out.println(gr.toString()); } GeneratoreGrafici generatore = new GeneratoreGrafici(refresh, listaGrafici); Thread threadGeneratore = new Thread (generatore); threadGeneratore.start(); } /** * Metodo main * * @param args * parametri ignorati * * @throws IOException * in caso di errori durante i processi di i/o */ public static void main (String[]args) throws IOException{ //Avvia i concatenatori per i dati giornalieri avviaConcatenatore(refrehGiornalieri, folderDatiGiornalieri); //Avvia i concatenatori per i dati settimanali avviaConcatenatore(refreshSettimanali, folderDatiSettimanali); try { Thread.sleep(10000); } catch (InterruptedException ex) { Logger.getLogger(Configuratore.class.getName()).log(Leve l.SEVERE, null, ex); } //Avvia i generatori di grafici per i dati giornalieri avviaGeneratoreGrafici(refrehGiornalieri, folderDatiGiornalieri); //Avvia i generatori di grafici per i dati settimanali avviaGeneratoreGrafici(refreshSettimanali, folderDatiSettimanali); } } Gianluca Coda 566/557 Pagina 81 di 131 CONCATENATORE.JAVA: package qjmon.viewer; import java.io.File; /** * Classe astratta per un generico concatenatore di file. * * @author Gianluca Coda */ public abstract class Concatenatore implements Runnable{ /** * Ritorna il frequenza di aggiornamento per il concatenatore. * * @return * frequenza di aggiornamento per il concatenatore */ public abstract int getRefreshTime(); la /** * Ritorna l'oggetto File relativo alla cartella root contenente * struttura di cartelle e file di dati. * * @return * File relativo alla cartella root contenente la struttura di cartelle * e file di dati. */ public abstract File getRootFolder(); /** * Ritorna l'estensione dei file di dati usati dal concatenatore. * * @return * estensione dei file di dati usati dal concatenatore. */ public abstract String getFileExt(); /** * Imposta l'estensione dei file di dati usati dal concatenatore. * * @param fileExt * estensione dei file di dati usati dal concatenatore. */ Gianluca Coda 566/557 Pagina 82 di 131 public abstract void setFileExt(String fileExt); /** * Ritorna l'estensione del file di output ottenuto dal processo * di concatenazione. * * @return * estensione del file di output ottenuto dal processo * di concatenazione. */ public abstract String getOutputFileExt(); /** * Imposta l'estensione per il file di dati ottenuto dalla concatenazione. * * @param fileExt * estensione del file di dati ottenuto dalla concatenazione. */ public abstract void setOutputFileExt(String outputFileExt); } CONCATENATOREGRUPPI.JAVA: package qjmon.viewer; import import import import import import import import import import import import import import java.io.BufferedReader; java.io.File; java.io.FileInputStream; java.io.FileNotFoundException; java.io.FileOutputStream; java.io.IOException; java.io.InputStreamReader; java.io.PrintStream; java.util.HashMap; java.util.LinkedList; java.util.NoSuchElementException; java.util.StringTokenizer; java.util.logging.Level; java.util.logging.Logger; /** * Classe che implementa un concatenatore per i gruppi delle vo. * Il risultato viene salvato in un nuovo file. * Gianluca Coda 566/557 Pagina 83 di 131 * @author Gianluca Coda */ public class ConcatenatoreGruppi extends Concatenatore { private int refreshTime; concatenazione private File rootFolder; cartelle e //Frequenza di aggiornamento //Root della struttura delle // file dei dati private String groupFileExt = ".gruppo"; //Estensione dei file dei dati private String concFileExt = ".GRconc"; //Estensione del file ottenuto dalla // concatenazione /** * Crea un nuovo oggetto atto alla concatenazione dei file dei gruppi * * @param refreshTime * Frequenza di aggiornamento concatenazione * * @param pathNameRootFolder * Pathname (percorso completo) alla cartella root contenente la struttura * di cartelle e file dei dati * * @throws IOException * In caso di errori nell'individuazione della cartella root */ public ConcatenatoreGruppi(int refreshTime, String pathNameRootFolder) throws IOException { this.refreshTime = refreshTime; rootFolder = new File(pathNameRootFolder); if (!rootFolder.isDirectory()) throw new IOException(pathNameRootFolder+" isn't a directory"); } @Override public void run() { LinkedList<File> fileList = null; //Lista dei file dei dati code) //Ciclo infinito while(true) { //Ciclo sulle sottodirectory della root (directory delle for (File codeDir : rootFolder.listFiles()) { Gianluca Coda 566/557 Pagina 84 di 131 //Per ogni directory delle code if (codeDir.isDirectory()) //Ciclo sulle sottodirectory delle code (directory delle vo) for (File voDir : codeDir.listFiles()) { //Per ogni directory delle vo if (voDir.isDirectory() ) { //Aggiunge i file dei gruppi della vo alla lista file fileList = visita(voDir); try { //Assegnazione nome per il file da creare ottenuto // dalla concatenazione dei file dei gruppi // della vo in esame String fileName = voDir.getPath() +"/"+voDir.getName()+concFileExt; //Concatena tutti i file della lista salvandone // il risultato in un nuovo file (fileName) concatena(fileList, fileName); } catch (FileNotFoundException ex) { //Log di eventuali eccezioni per file non trovato Logger.getLogger(ConcatenatoreGruppi .class.getName()).log(Level.SEVERE, null, ex); } catch (IOException ex) { //Log di eventuali eccezioni di i/o su file Logger.getLogger(ConcatenatoreGruppi .class.getName()).log(Level.SEVERE, null, ex); } } } } try { //Attende refreshTime secondi prima di effettuare nuovamente // la concatenazione Thread.sleep(refreshTime * 1000); } catch (InterruptedException ex) { //In caso di interruzione del thread mostra su stdout lo stack // dell'eccezione ex.printStackTrace(); } } Gianluca Coda 566/557 Pagina 85 di 131 } /** * Metodo privato che, a partire dalla cartella "voFolder", ritorna * una lista di file contenenti i dati dei gruppi. * * @param voFolder * file che punta alla directory di una vo * * @return * lista di file contenenti i dati dei gruppi */ private LinkedList<File> visita(File voFolder) { LinkedList<File> fileList = new LinkedList<File>(); //Lista da ritornare //Ciclo su file e sottodirectory della cartella della vo for (File groupDir : voFolder.listFiles()) //Per ogni sottodirectory della vo (cartella di un gruppo) un gruppo if (groupDir.isDirectory()) //Per ogni file e sottodirectory della cartella di for (File groupFile : groupDir.listFiles()) //Per ogni file contenente dati di un gruppo if (groupFile.isFile() && groupFile.getPath().endsWith(groupFileExt)) fileList.add(groupFile); //Aggiunge il file alla lista return fileList; //Ritorna la lista dei file dei gruppi } // i nomi delle etichette devono essere ricavati dai nomi delle cartelle in cui si // trovano i file. Quindi è la concatenazione del nome della cartella più il nome della // etichetta. /** * Esegue la concatenazione dei file della lista "fileList" memorizzandone * il risultato in un nuovo file con pathname pari ad "outputFileName". * * @param fileList * lista di file da concatenare * Gianluca Coda 566/557 Pagina 86 di 131 * @param outputFileName * pathname del nuovo file che conterra' i dati della concatenazione * * @return * oggetto File che punta al file ottenuto dalla concatenazione * * @throws FileNotFoundException * nel caso in cui un file non viene trovato * * @throws IOException * in caso di errori nel processo di i/o su file */ private File concatena(LinkedList<File> fileList, String outputFileName) throws FileNotFoundException, IOException { LinkedList<HashMap<Integer, LinkedList<String>>> dataFileList = new LinkedList<HashMap<Integer, LinkedList<String>>>(); int fileNumber = fileList.size(); File outputFile = new File(outputFileName); FileInputStream streamin = null; InputStreamReader readerin = null; BufferedReader readerbuff = null; StringTokenizer stringatok = null; String valoreCampo = null; String stringa = null; HashMap<Integer, LinkedList<String>> dataFileMap = null; int rowCount = 0; int colCount = 0; for (File fileI : fileList) { dataFileMap = new HashMap<Integer, LinkedList<String>>(); streamin = new FileInputStream(fileI); readerin = new InputStreamReader(streamin); readerbuff = new BufferedReader(readerin); rowCount = 0; for (stringa = readerbuff.readLine(); stringa != null; stringa = readerbuff.readLine()) { colCount = 0; stringatok = new StringTokenizer(stringa); LinkedList<String> row = new LinkedList<String>(); try { for (colCount = 0; ; ++colCount) { valoreCampo = stringatok.nextToken(); Gianluca Coda 566/557 Pagina 87 di 131 row.add(valoreCampo); } }catch (NoSuchElementException ex) {} dataFileMap.put(rowCount, row); ++rowCount; } dataFileList.add(dataFileMap); } FileOutputStream streamout = new FileOutputStream(outputFile); PrintStream output = new PrintStream(streamout); for (int i = 0; i < rowCount; ++i) { int fileCount = 0; for (int j = 0; j < colCount; ++j) { for (HashMap<Integer, LinkedList<String>> fileMap : dataFileList) { LinkedList<String> valueList = fileMap.get(i); String valore = valueList.get(j); //inserire controllo IF per primo rigo if (i == 0) {//inserito per prova String prefix = fileList.get(fileCount).getName(); prefix = prefix.substring(0, prefix.length()-groupFileExt.length()); valore = prefix+"_"+valore; //System.out.print(prefix+"_"+valore+"\t"); //inserito per prova //sto sul fileCount per ottenere il nome del file fileList.get(fileCount).getPath } if (j == 0 && fileCount == 0) output.print(valore+"\t"); if (j != 0) output.print(valore+"\t"); ++fileCount; } fileCount=0; } output.println(); } return outputFile; } @Override Gianluca Coda 566/557 Pagina 88 di 131 public int getRefreshTime() { return refreshTime; } @Override public File getRootFolder() { return rootFolder; } @Override public String getFileExt() { return groupFileExt; } @Override public void setFileExt(String fileExt) { this.groupFileExt = fileExt; } @Override public String getOutputFileExt() { return concFileExt; } @Override public void setOutputFileExt(String outputFileExt) { this.concFileExt = outputFileExt; } } CONCATENATOREVO.JAVA: package qjmon.viewer; import import import import import import import import import import java.io.BufferedReader; java.io.File; java.io.FileInputStream; java.io.FileNotFoundException; java.io.FileOutputStream; java.io.IOException; java.io.InputStreamReader; java.io.PrintStream; java.util.HashMap; java.util.LinkedList; Gianluca Coda 566/557 Pagina 89 di 131 import import import import java.util.NoSuchElementException; java.util.StringTokenizer; java.util.logging.Level; java.util.logging.Logger; /** * Classe che implementa un concatenatore per le vo di una coda. * Il risultato viene salvato in un nuovo file. * * @author Gianluca Coda */ public class ConcatenatoreVO extends Concatenatore { private int refreshTime; concatenazione private File rootFolder; cartelle e //Frequenza di aggiornamento //Root della struttura delle // file dei dati private String voFileExt = ".vo"; //Estensione dei file dei dati private String concFileExt = ".VOconc"; //Estensione del file ottenuto dalla // concatenazione /** * Crea un nuovo oggetto atto alla concatenazione dei file dei gruppi * * @param refreshTime * Frequenza di aggiornamento concatenazione * * @param pathNameRootFolder * Pathname (percorso completo) alla cartella root contenente la struttura * di cartelle e file dei dati * * @throws IOException * In caso di errori nell'individuazione della cartella root */ public ConcatenatoreVO(int refreshTime, String pathNameRootFolder) throws IOException { this.refreshTime = refreshTime; rootFolder = new File(pathNameRootFolder); if (!rootFolder.isDirectory()) throw new IOException(pathNameRootFolder+" isn't a directory"); } Gianluca Coda 566/557 Pagina 90 di 131 @Override public void run() { LinkedList<File> fileList = null; concatenare String concFileName = null; salvataggio del //Lista dei file da //Pathname per il // file ottenuto dalla concatenazione concFileName = rootFolder+"/"; //Ciclo infinito while(true) { fileList = visita(); try { LinkedList<File> voFileList = null; File fileTmp = null; File fileHead = null; //Ciclo sulla lista dei file da concatenare for (; !fileList.isEmpty();) { voFileList = new LinkedList<File>(); fileHead = fileList.poll(); voFileList.add(fileHead); for (int i = 0; i < fileList.size(); ++i) { fileTmp = fileList.get(i); if (fileTmp.getName().equals(fileHead.getName())) { fileList.remove(i); voFileList.add(fileTmp); --i; } } //Concatena i file concatena(voFileList, concFileName+fileHead.getName()+concFileExt); } } catch (FileNotFoundException ex) { //Logga eventuali errori dovuti all'accesso a file Logger.getLogger(ConcatenatoreVO.class.getName()).lo g(Level.SEVERE, null, ex); } catch (IOException ex) { //Logga eventuali errori durante i processi di i/o su file Logger.getLogger(ConcatenatoreVO.class.getName()).lo g(Level.SEVERE, null, ex); } try { //Attende refreshTime secondi prima di effettuare Gianluca Coda 566/557 Pagina 91 di 131 nuovamente // la concatenazione Thread.sleep(refreshTime * 1000); } catch (InterruptedException ex) { //In caso di interruzione del thread mostra su stdout lo stack // dell'eccezione ex.printStackTrace(); } } } /** * Metodo privato che ritorna una lista di file contenenti i dati delle VO. * * @return * lista di file contenenti i dati delle VO */ private LinkedList<File> visita() { LinkedList<File> fileList = new LinkedList<File>(); for (File codeDir : rootFolder.listFiles()) if (codeDir.isDirectory()) for (File voDir : codeDir.listFiles()) if (voDir.isDirectory()) for (File voFile : voDir.listFiles()) if (voFile.isFile() && voFile.getPath().endsWith(voFileExt)) fileList.add(voFile); } return fileList; /** * Esegue la concatenazione dei file della lista "fileList" memorizzandone * il risultato in un nuovo file con pathname pari ad "outputFileName". * * @param fileList * lista di file da concatenare * * @param outputFileName * pathname del nuovo file che conterra' i dati della concatenazione * * @return * oggetto File che punta al file ottenuto dalla concatenazione * Gianluca Coda 566/557 Pagina 92 di 131 * @throws FileNotFoundException * nel caso in cui un file non viene trovato * * @throws IOException * in caso di errori nel processo di i/o su file */ private File concatena(LinkedList<File> fileList, String outputFileName) throws FileNotFoundException, IOException { LinkedList<HashMap<Integer, LinkedList<String>>> dataFileList = new LinkedList<HashMap<Integer, LinkedList<String>>>(); int fileNumber = fileList.size(); File outputFile = new File(outputFileName); FileInputStream streamin = null; InputStreamReader readerin = null; BufferedReader readerbuff = null; StringTokenizer stringatok = null; String valoreCampo = null; String stringa = null; HashMap<Integer, LinkedList<String>> dataFileMap = null; int rowCount = 0; int colCount = 0; for (File fileI : fileList) { dataFileMap = new HashMap<Integer, LinkedList<String>>(); streamin = new FileInputStream(fileI); readerin = new InputStreamReader(streamin); readerbuff = new BufferedReader(readerin); rowCount = 0; for (stringa = readerbuff.readLine(); stringa != null; stringa = readerbuff.readLine()) { colCount = 0; stringatok = new StringTokenizer(stringa); LinkedList<String> row = new LinkedList<String>(); try { for (colCount = 0; ; ++colCount) { valoreCampo = stringatok.nextToken(); row.add(valoreCampo); } }catch (NoSuchElementException ex) {} dataFileMap.put(rowCount, row); ++rowCount; } Gianluca Coda 566/557 Pagina 93 di 131 dataFileList.add(dataFileMap); } FileOutputStream streamout = new FileOutputStream(outputFile); PrintStream output = new PrintStream(streamout); for (int i = 0; i < rowCount; ++i) { int fileCount = 0; for (int j = 0; j < colCount; ++j) { for (HashMap<Integer, LinkedList<String>> fileMap : dataFileList) { LinkedList<String> valueList = fileMap.get(i); String valore = valueList.get(j); //inserire controllo IF per primo rigo if (i == 0) {//inserito per prova String prefix = fileList.get(fileCount).getPath(); // concFile fileName int x = rootFolder.getPath().length() + 1; //System.out.println("x: "+x); //System.out.println("nome file: "+fileList.get(fileCount).getName()); int y = prefix.length() 2*(fileList.get(fileCount).getName().length()) + voFileExt.length() - 2; prefix = prefix.substring(x, y); valore = prefix+"_"+valore; //System.out.print(prefix+"_"+valore+"\t"); //inserito per prova //sto sul fileCount per ottenere il nome del file fileList.get(fileCount).getPath } if (j == 0 && fileCount == 0) output.print(valore+"\t"); if (j != 0) output.print(valore+"\t"); ++fileCount; } fileCount=0; } } output.println(); streamin.close(); readerin.close(); readerbuff.close(); streamout.close(); output.close(); Gianluca Coda 566/557 Pagina 94 di 131 return outputFile; } @Override public int getRefreshTime() { return refreshTime; } @Override public File getRootFolder() { return rootFolder; } @Override public String getFileExt() { return voFileExt; } @Override public void setFileExt(String fileExt) { this.voFileExt = fileExt; } @Override public String getOutputFileExt() { return concFileExt; } @Override public void setOutputFileExt(String outputFileExt) { this.concFileExt = outputFileExt; } } GENERATOREGRAFICI.JAVA: package qjmon.viewer; import java.io.FileNotFoundException; import java.io.IOException; import java.util.LinkedList; /** * Classe per la generazione di grafici a partire da un file dei Gianluca Coda 566/557 Pagina 95 di 131 dati. * * @author Gianluca Coda */ public class GeneratoreGrafici implements Runnable { private int refreshTime; //Frequenza, in secondi, di generazione dei grafici private LinkedList<Grafico> listaGrafici; //Lista di grafici da generare /** * Crea un nuovo oggetto atto alla generazione, ad intervalli di tempo * regolari, di grafici. * * @param refreshTime * frequenza, in secondi, per l'aggiornamento dei grafici. * * @param listaGrafici * lista di grafici da generare ed aggiornare. */ public GeneratoreGrafici (int refreshTime, LinkedList<Grafico> listaGrafici){ this.refreshTime = refreshTime; this.listaGrafici = listaGrafici; } @Override public void run() { //Ciclo infinito while (true){ //Ciclo sui grafici da generare for (Grafico grafico: listaGrafici){ try { //Genera il grafico grafico.generaGrafico(); } catch (FileNotFoundException ex) { //In caso di errore nell'accesso al file dei dati ex.printStackTrace(); } catch (IOException ex) { //In caso di errori durante i processi di i/o ex.printStackTrace(); } } try { //Attende refreshTime secondi prima di effettuare nuovamente Gianluca Coda 566/557 Pagina 96 di 131 } } // la generazione dei grafici Thread.sleep(refreshTime * 1000); } catch (InterruptedException ex) { ex.printStackTrace(); } } GRAFICO: package qjmon.viewer; import com.lowagie.text.Document; import com.lowagie.text.DocumentException; import com.lowagie.text.Rectangle; import com.lowagie.text.pdf.FontMapper; import com.lowagie.text.pdf.PdfContentByte; import com.lowagie.text.pdf.PdfTemplate; import com.lowagie.text.pdf.PdfWriter; import java.awt.Graphics2D; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStreamReader; import java.io.OutputStream; import java.text.NumberFormat; import java.text.SimpleDateFormat; import java.util.HashMap; import java.util.Locale; import java.util.NoSuchElementException; import java.util.StringTokenizer; import org.jfree.chart.ChartUtilities; import org.jfree.chart.axis.DateAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.labels.StandardXYToolTipGenerator; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.StackedXYAreaRenderer2; import org.jfree.chart.renderer.xy.XYItemRenderer; import org.jfree.data.xy.DefaultTableXYDataset; import org.jfree.data.xy.TableXYDataset; import org.jfree.data.xy.XYSeries; import org.jfree.experimental.chart.renderer.xy.XYSmoothLineAndShapeRendere r; Gianluca Coda 566/557 Pagina 97 di 131 import import import import import import import import import import java.awt.geom.Rectangle2D; java.io.File; java.io.FileOutputStream; java.io.IOException; java.io.OutputStreamWriter; java.io.Writer; org.apache.batik.dom.GenericDOMImplementation; org.apache.batik.svggen.SVGGraphics2D; org.jfree.chart.JFreeChart; org.w3c.dom.DOMImplementation; /** * Classe che rappresenta un generico grafico * * @author Gianluca Coda */ public class Grafico { private static final char tagLine = '*';//Tag per i dati da plottare come linee private static final char tagNoPlot = '!';//Tag per i dati che devono essere ignorati private String pathNameData; //Percorso al file dei dati private String pathNameGraph; //Nome del file, senza estensione, // per il salvataggio del grafico private final static int costantTime = 1000; /** * Crea un nuovo grafico riferito ai dati del file "pathNameData". * * @param pathNameData * pathname al file contenente i dati da plottare. * * @param pathNameGraph * nome del file, senza estensione, con cui salvare il grafico */ public Grafico (String pathNameData, String pathNameGraph){ this.pathNameData = pathNameData; this.pathNameGraph = pathNameGraph; } /** * Crea un oggetto JFreeChart a partire dal dataset passato come * parametro. Il grafico è del tipo "aree impilate" (Stacked) * Gianluca Coda 566/557 Pagina 98 di 131 * @param dataset * dataset da plottare. * * @return * oggetto JFreeChart rappresentante il grafico ottenuto dal dataset * passato come parametro. */ private JFreeChart createChart(TableXYDataset dataset) { //Generazione etichette StandardXYToolTipGenerator labelGenerator = new StandardXYToolTipGenerator( StandardXYToolTipGenerator.DEFAULT_TOOL_TIP_FORMAT, new SimpleDateFormat("dd-MMM-yyyy", Locale.ITALY), NumberFormat.getInstance() ); //Imposta il nome dell'asse delle x DateAxis xAxis = new DateAxis("Time"); //Imposta i margini superiori ed inferiori del grafico per l'asse x xAxis.setLowerMargin(0.0); xAxis.setUpperMargin(0.0); //Imposta il nome dell'asse delle y NumberAxis yAxis = new NumberAxis("Job"); //Imposta l'unità di misura delle y (interi) yAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits ()); //Imposta il grafico come aree impilate StackedXYAreaRenderer2 renderer = new StackedXYAreaRenderer2(labelGenerator, null); XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer); //Crea il grafico JFreeChart chart = new JFreeChart(null, JFreeChart.DEFAULT_TITLE_FONT, plot, true); try { //Salvataggio grafico in formato png (risoluzione 500x300) ChartUtilities.saveChartAsPNG(new File(pathNameGraph+".png"), chart, 500,300); //Salvataggio grafico in formato jpeg (risoluzione 500x300) ChartUtilities.saveChartAsJPEG(new File(pathNameGraph+".jpg"), chart, 500,300); //Salvataggio grafico in formato pdf (risoluzione Gianluca Coda 566/557 Pagina 99 di 131 500x300) OutputStream out = new BufferedOutputStream(new FileOutputStream(pathNameGraph+".pdf")); writeChartAsPDF(out, chart, 500, 300, null); out.close(); //Salvataggio grafico in formato svg (risoluzione 500x300) DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation(); // Creazione di un'instanza di org.w3c.dom.Document org.w3c.dom.Document document = domImpl.createDocument(null, "svg", null); // Creazione di un'istanza di Generatore SVG SVGGraphics2D svgGenerator = new SVGGraphics2D(document); // imposta la precisione al fine di evitare eccezioni dovute a puntatori null // nella Batik 1.5 svgGenerator.getGeneratorContext().setPrecision(6); // Importa il rendering del grafico per la SVG Graphics2D chart.draw(svgGenerator, new Rectangle2D.Double(0, 0, 500, 300), null); // Salvataggio SVG boolean useCSS = true; Writer out_svg = new OutputStreamWriter( new FileOutputStream(new File(pathNameGraph+".svg")), "UTF-8"); svgGenerator.stream(out_svg, useCSS); } catch (Exception e) { //In caso di errori nel salvataggio del grafico System.out.println("Problema nella esportazione del grafico"); } } //Ritorna il grafico creato return chart; // crea un grafico con primo dataset a linea ed il secondo impilato Gianluca Coda 566/557 Pagina 100 di 131 /** * Crea un oggetto JFreeChart a partire dai dataset passati come * parametri. Il primo dataset viene graficato "a linee", mentre il secondo * come "aree impilate". * * @param lineDataset * dataset da rappresentare come linee. * * @param stackedDataset * dataset da rappresentare come aree impilate. * * @return * oggetto JFreeChart rappresentante il grafico ottenuto dai dataset * passati come parametri. */ private JFreeChart createChart(TableXYDataset lineDataset, TableXYDataset stackedDataset) { //Generazione etichette StandardXYToolTipGenerator labelGenerator = new StandardXYToolTipGenerator( StandardXYToolTipGenerator.DEFAULT_TOOL_TIP_FORMAT, new SimpleDateFormat("dd-MMM-yyyy", Locale.ITALY), NumberFormat.getInstance() ); x //Imposta il nome dell'asse x DateAxis xAxis = new DateAxis("Time"); //Imposta margini superiori ed inferiori del grafico per l'asse xAxis.setLowerMargin(0.0); xAxis.setUpperMargin(0.0); //Imposta il nome dell'asse y NumberAxis yAxis = new NumberAxis("Job"); //Imposta l'unità di misura per l'asse y (interi) yAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits ()); //Imposta il renderer per il dataset a linee XYSmoothLineAndShapeRenderer renderer = new XYSmoothLineAndShapeRenderer(); XYPlot plot = new XYPlot(lineDataset, xAxis, yAxis, renderer); plot.setDataset(1, stackedDataset); Gianluca Coda 566/557 Pagina 101 di 131 //Imposta il renderer per il dataset ad aree impilate XYItemRenderer renderer1 = new StackedXYAreaRenderer2(labelGenerator, null); plot.setRenderer(1, renderer1); //Crea il grafico JFreeChart chart = new JFreeChart(null, JFreeChart.DEFAULT_TITLE_FONT, plot, true); 500x300) try { //Salvataggio grafico in formato png (risoluzione ChartUtilities.saveChartAsPNG(new File(pathNameGraph+".png"), chart, 500,300); //Salvataggio grafico in formato jpeg (risoluzione 500x300) ChartUtilities.saveChartAsJPEG(new File(pathNameGraph+".jpg"), chart, 500,300); //Salvataggio grafico in formato pdf (risoluzione 500x300) OutputStream out = new BufferedOutputStream(new FileOutputStream(pathNameGraph+".pdf")); writeChartAsPDF(out, chart, 500, 300, null); out.close(); //Salvataggio grafico in formato svg (risoluzione 500x300) DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation(); // Creazione di un'instanza di org.w3c.dom.Document org.w3c.dom.Document document = domImpl.createDocument(null, "svg", null); // Creazione di un'istanza di Generatore SVG SVGGraphics2D svgGenerator = new SVGGraphics2D(document); // imposta la precisione al fine di evitare eccezioni dovute a puntatori null // nella Batik 1.5 svgGenerator.getGeneratorContext().setPrecision(6); // Importa il rendering del grafico per la SVG Graphics2D Gianluca Coda 566/557 Pagina 102 di 131 chart.draw(svgGenerator, new Rectangle2D.Double(0, 0, 500, 300), null); // Salvataggio SVG boolean useCSS = true; Writer out_svg = new OutputStreamWriter( new FileOutputStream(new File(pathNameGraph+".svg")), "UTF-8"); svgGenerator.stream(out_svg, useCSS); } catch (Exception e) { //In caso di errori nel salvataggio del grafico System.out.println("Problema nella esportazione del grafico"); } } return chart; //Ritorna il grafico creato /** * Genera il grafico. * * @throws FileNotFoundException * in caso di errori nell'accesso al file dei dati. * * @throws IOException * in caso di errori durante i processi di i/o */ public void generaGrafico() throws FileNotFoundException, IOException{ DefaultTableXYDataset stackedDataset = new DefaultTableXYDataset(); DefaultTableXYDataset lineDataset = new DefaultTableXYDataset(); HashMap<Integer, XYSeries> mappaCurve = new HashMap<Integer, XYSeries>(); HashMap<Integer, XYSeries> mappaAree = new HashMap<Integer, XYSeries>(); File dataFile = new File(pathNameData); FileInputStream streamin = new FileInputStream(dataFile); InputStreamReader readerin = new InputStreamReader(streamin); BufferedReader readerbuff = new BufferedReader(readerin); String stringa = readerbuff.readLine(); //lettura intestazione file StringTokenizer stringatok = null; String valoreCampo = null; //lettura data Gianluca Coda 566/557 Pagina 103 di 131 int numeroCurve = 0; if (stringa!=null) try{ stringatok = new StringTokenizer(stringa); valoreCampo = stringatok.nextToken(); //lettura data for (numeroCurve = 0; ; ++numeroCurve ){ valoreCampo = stringatok.nextToken(); if (!(valoreCampo.substring(0, 1)).equals(""+tagNoPlot)) { if ((valoreCampo.substring(0, 1)).equals(""+tagLine)) { valoreCampo = valoreCampo.replaceFirst("["+tagLine+"]", ""); mappaCurve.put(numeroCurve, new XYSeries(valoreCampo,true,false)); } else { mappaAree.put(numeroCurve, new XYSeries(valoreCampo,true,false)); } } } } catch(NoSuchElementException ex){ } //ciclo sulle righe for (stringa = readerbuff.readLine();stringa != null;){ stringatok = new StringTokenizer(stringa); try { Long time = Long.parseLong(stringatok.nextToken())*costantTime; //ciclo sulle colonne for (int i=0; i<numeroCurve ; ++i){ Integer value = Integer.parseInt(stringatok.nextToken()); XYSeries serie = mappaAree.get(i); if (serie == null) serie = mappaCurve.get(i); if (serie != null) Gianluca Coda 566/557 Pagina 104 di 131 serie.add(time, value); } }catch(NoSuchElementException ex){ ex.printStackTrace(); streamin.close(); readerin.close(); readerbuff.close(); } stringa = readerbuff.readLine(); } for (Integer x : mappaAree.keySet()) { XYSeries serie = mappaAree.get(x); if (serie != null) stackedDataset.addSeries(serie); } for (Integer x : mappaCurve.keySet()) { XYSeries serie = mappaCurve.get(x); if (serie != null) lineDataset.addSeries(serie); } JFreeChart chart = createChart(lineDataset, stackedDataset); } public String toString(){ return("Datafile: " + pathNameData + "\t pathNameGraph: " + pathNameGraph); } * * * * * * /** Salva il grafico nel formato PDF @param out stream sul quale scrivere il pdf @param chart Gianluca Coda 566/557 Pagina 105 di 131 * grafico da salvare in pdf * * @param width * largezza del pdf * * @param height * altezza del pdf * * @param mapper * mapper per i font da usare nel pdf * * @throws IOException * in caso di errori durante il processo di salvataggio */ public static void writeChartAsPDF(OutputStream out, JFreeChart chart, int width, int height, FontMapper mapper) throws IOException { Rectangle pagesize = new Rectangle(width, height); Document document = new Document(pagesize, 50, 50, 50, 50); try { PdfWriter writer = PdfWriter.getInstance(document, out); document.addAuthor("JFreeChart"); document.addSubject("Demonstration"); document.open(); PdfContentByte cb = writer.getDirectContent(); PdfTemplate tp = cb.createTemplate(width, height); Graphics2D g2 = tp.createGraphics(width, height, mapper); Rectangle2D r2D = new Rectangle2D.Double(0, 0, width, height); chart.draw(g2, r2D); g2.dispose(); cb.addTemplate(tp, 0, 0); } catch (DocumentException de) { System.err.println(de.getMessage()); } document.close(); } } Gianluca Coda 566/557 Pagina 106 di 131 APPLETGRAFICA.JAVA /* * To change this template, choose Tools | Templates * and open the template in the editor. */ /** * * @author Pensieroso */ /* -----------* AppletGrafica.java * -----------* (C) Copyright 2002-2005, by Object Refinery Limited. */ package applet; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.URL; import java.text.NumberFormat; import java.text.SimpleDateFormat; import java.util.HashMap; import java.util.Locale; import java.util.NoSuchElementException; import java.util.StringTokenizer; import javax.swing.JApplet; import org.jfree.chart.ChartPanel; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.DateAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.labels.StandardXYToolTipGenerator; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.StackedXYAreaRenderer2; import org.jfree.chart.renderer.xy.XYItemRenderer; import org.jfree.data.xy.DefaultTableXYDataset; import org.jfree.data.xy.TableXYDataset; import org.jfree.data.xy.XYSeries; import org.jfree.experimental.chart.renderer.xy.XYSmoothLineAndShapeRendere r; /** * A simple applet demo. */ public class AppletGrafica extends JApplet { Gianluca Coda 566/557 Pagina 107 di 131 /** Time series for total memory used. */ //private TimeSeries total; /** Time series for free memory. */ //private TimeSeries free; private private private private String pathNameData = null; static final char tagLine = '*'; static final char tagNoPlot = '!'; final static int costantTime = 1000; private DefaultTableXYDataset stackedDataset DefaultTableXYDataset(); private DefaultTableXYDataset lineDataset DefaultTableXYDataset(); = new = new public void init() { pathNameData= this.getParameter("stringa"); //this.total = new TimeSeries("Total", Millisecond.class); //this.total.setMaximumItemAge(30000); //this.free = new TimeSeries("Free", Millisecond.class); //this.free.setMaximumItemAge(30000); //TimeSeriesCollection dataset = new TimeSeriesCollection(); //dataset.addSeries(total); //dataset.addSeries(free); //String text=null; /*try { URL url = new URL(getDocumentBase(),filePathName); //DataInputStream stream DataInputStream(url.openStream()); BufferedReader buff InputStreamReader(url.openStream())); text = buff.readLine(); = new = new BufferedReader(new }catch (IOException e){ e.printStackTrace(); } DateAxis domain = new DateAxis(text); NumberAxis range = new NumberAxis("Memory"); false); XYItemRenderer renderer = new XYLineAndShapeRenderer(true, Gianluca Coda 566/557 Pagina 108 di 131 XYPlot plot = new XYPlot(dataset, domain, range, renderer); plot.setBackgroundPaint(Color.lightGray); plot.setDomainGridlinePaint(Color.white); plot.setRangeGridlinePaint(Color.white); renderer.setSeriesPaint(0, Color.red); renderer.setSeriesPaint(1, Color.green); renderer.setSeriesStroke(0, new BasicStroke(1.5f)); renderer.setSeriesStroke(1, new BasicStroke(1.5f)); domain.setAutoRange(true); domain.setLowerMargin(0.0); domain.setUpperMargin(0.0); domain.setTickLabelsVisible(true); ()); true ); range.setStandardTickUnits(NumberAxis.createIntegerTickUnits JFreeChart chart = new JFreeChart( "Memory Usage", JFreeChart.DEFAULT_TITLE_FONT, plot, chart.setBackgroundPaint(Color.white); */ //stackedDataset = new DefaultTableXYDataset(); //lineDataset = new DefaultTableXYDataset(); HashMap<Integer, XYSeries> mappaCurve = new HashMap<Integer, XYSeries>(); HashMap<Integer, XYSeries> mappaAree = new HashMap<Integer, XYSeries>(); try { URL url = new URL(getDocumentBase(),pathNameData); BufferedReader readerbuff = new BufferedReader(new InputStreamReader(url.openStream())); String stringa = readerbuff.readLine(); //lettura intestazione file StringTokenizer stringatok = null; String valoreCampo = null; //lettura data int numeroCurve = 0; if (stringa!=null) try{ stringatok = new StringTokenizer(stringa); valoreCampo = stringatok.nextToken(); //lettura data for (numeroCurve = 0; ; ++numeroCurve ){ Gianluca Coda 566/557 Pagina 109 di 131 valoreCampo = stringatok.nextToken(); 1)).equals(""+tagNoPlot)) { if (!(valoreCampo.substring(0, if ((valoreCampo.substring(0, 1)).equals(""+tagLine)) { valoreCampo = valoreCampo.replaceFirst("["+tagLine+"]", ""); mappaCurve.put(numeroCurve, new XYSeries(valoreCampo,true,false)); } else { mappaAree.put(numeroCurve, new XYSeries(valoreCampo,true,false)); } } } } catch(NoSuchElementException ex){ //ex.printStackTrace(); //streamin.close(); //readerin.close(); //readerbuff.close(); } //ciclo sulle righe for (stringa = readerbuff.readLine();stringa != null;){ stringatok = new StringTokenizer(stringa); try { Long.parseLong(stringatok.nextToken())*costantTime; Long time = value = //ciclo sulle colonne for (int i=0; i<numeroCurve ; ++i){ Integer Integer.parseInt(stringatok.nextToken()); XYSeries serie = mappaAree.get(i); if (serie == null) serie = mappaCurve.get(i); if (serie != null) serie.add(time, value); //XYSeries serie = seriesList.get(i); Gianluca Coda 566/557 Pagina 110 di 131 //serie.add(time,value); } }catch(NoSuchElementException ex){ ex.printStackTrace(); } stringa = readerbuff.readLine(); } //for (XYSeries serie : seriesList) // dataset.addSeries(serie); for (Integer x : mappaAree.keySet()) { XYSeries serie = mappaAree.get(x); if (serie != null) stackedDataset.addSeries(serie); } for (Integer x : mappaCurve.keySet()) { XYSeries serie = mappaCurve.get(x); if (serie != null) lineDataset.addSeries(serie); } //JFreeChart chart = new JFreeChart("Memory Usage", JFreeChart.DEFAULT_TITLE_FONT, plot, true); JFreeChart chart = createChart(lineDataset, stackedDataset); ChartPanel chartPanel = new ChartPanel(chart); chartPanel.setPopupMenu(null); getContentPane().add(chartPanel); //new AppletGrafica.DataGenerator().start(); }catch(Exception ex) { ex.printStackTrace(); } } Gianluca Coda 566/557 Pagina 111 di 131 private JFreeChart createChart(TableXYDataset TableXYDataset stackedDataset) { lineDataset, StandardXYToolTipGenerator labelGenerator = StandardXYToolTipGenerator( StandardXYToolTipGenerator.DEFAULT_TOOL_TIP_FORMAT, new SimpleDateFormat("dd-MMM-yyyy", Locale.ITALY), NumberFormat.getInstance() ); DateAxis xAxis = new DateAxis("Domain (X)"); xAxis.setLowerMargin(0.0); xAxis.setUpperMargin(0.0); new NumberAxis yAxis = new NumberAxis("Range (Y)"); //yAxis.setAutoRangeIncludesZero(true); yAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits ()); XYSmoothLineAndShapeRenderer XYSmoothLineAndShapeRenderer(); XYPlot plot = new renderer XYPlot(lineDataset, = xAxis, new yAxis, renderer); //XYItemRenderer renderer0 = plot.getRenderer(); plot.setDataset(1, stackedDataset); XYItemRenderer StackedXYAreaRenderer2(labelGenerator, null); renderer1 = new plot.setRenderer(1, renderer1); JFreeChart chart = JFreeChart.DEFAULT_TITLE_FONT, plot, true); new JFreeChart(null, return chart; } /** * Creates AppletGrafica new instance. */ public AppletGrafica() { } } Gianluca Coda 566/557 Pagina 112 di 131 INDEX.JSP: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>DEMO - Monitor</title> <link rel="stylesheet" href="jquery.treeview.css"> </head> <body> <table style="text-align: left; width: 100%; height: 600px;" border="0" cellpadding="2" cellspacing="2"> <tbody> <tr align="left"> <td style="width: 100%; height: 120px;" colspan="2" rowspan="1"><big style="font-weight: bold;"><big>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&n bsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbs p;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;QJMON Monitoraggio<br> </big></big> <div style="text-align: left;"> <a target="frame_menu" href="menu_job.jsp">Job Monitoring</a> <br> <a target="frame_menu" href="menu.jsp">Albero classico</a> <!-- <br> <a target="frame_menu" href="menu_inverso.jsp">Albero inverso</a> --> </div> </td> </tr> <tr> <td style="width: 250px; height: 460px; vertical-align: top;"> <iframe name="frame_menu" src="menu.jsp" height="450" width="240" frameborder="0">Contenuto alternativo per i browser che non leggono gli iframe. Gianluca Coda 566/557 Pagina 113 di 131 </iframe> </td> <td style="height: 500px; width: 100%; vertical-align: top;"><big style="font-weight: bold;"><!--Data related to Feb 7th, 2010<br>--></big> <iframe name="frame_centrale" src="" height="440" width=100% frameborder="0">Contenuto alternativo per i browser che non leggono gli iframe. </iframe> </td> </tr> </tbody> </table> </body> </html> JSP_CENTRALE.JSP: <%@page import="java.io.File"%> <APPLET ARCHIVE="AppletGrafica.jar, jfreechart-1.0.13.jar,jcommon1.0.16.jar,jfreechart-1.0.13-experimental.jar" CODE="applet.AppletGrafica" width=530 height=400 ALT="You should see an applet, not this text."> <PARAM name=stringa value=" <% String ext1 = ".tot"; String ext2 = ".coda"; String ext3 = ".GRconc"; String ext4 = ".gruppo"; String selectedFile = null; String root = (new File(".")).getAbsolutePath(); //out.println(root+"<br>"); String jsp_centrale = "jsp_centrale.jsp"; root = root.substring(0, root.length() - 1) + "struttura/"; //out.println(root+"<br>"); String fileName = null; File rootFile = new File(root+request.getParameter("folder")); //out.println(root); //out.println(request.getParameter("folder")); if (rootFile.isDirectory()) { //out.println("è una directory<br>"); for (File file : rootFile.listFiles()) { //out.println(file+"<br>"); Gianluca Coda 566/557 Pagina 114 di 131 if (file.isFile()) { if (file.isFile()) { fileName = file.getName(); //out.println("fileName: "+fileName+"<br>"); //out.println(fileName.substring(fileName.length ()-ext1.length(),fileName.length())); if (fileName.charAt(0)!='.' && (( (fileName.length()>ext1.length()) && (fileName.substring(fileName.length()ext1.length(),fileName.length())).equals(ext1) ) || ( (fileName.length()>ext2.length()) && (fileName.substring(fileName.length()ext2.length(),fileName.length())).equals(ext2) ) || ( (fileName.length()>ext3.length()) && (fileName.substring(fileName.length()ext3.length(),fileName.length())).equals(ext3) ) || ( (fileName.length()>ext4.length()) && (fileName.substring(fileName.length()ext4.length(),fileName.length())).equals(ext4) ) ) ) { selectedFile = request.getParameter("folder")+"/"+fileName; out.println(selectedFile); } } } } } %> "> </APPLET> <br> <input type="button"value="PNG" onclick="window.location.href='< %out.print("http://143.225.93.195:8080/definitivo/"+selectedFile+".p ng");%>'"> Gianluca Coda 566/557 Pagina 115 di 131 <input type="button"value="JPEG" onclick="window.location.href='< %out.print("http://143.225.93.195:8080/definitivo/"+selectedFile+".j pg");%>'"> <input type="button"value="PDF" onclick="window.location.href='< %out.print("http://143.225.93.195:8080/definitivo/"+selectedFile+".p df");%>'"> <input type="button"value="SVG" onclick="window.location.href='< %out.print("http://143.225.93.195:8080/definitivo/"+selectedFile+".s vg");%>'"> JSP_CENTRALE_INVERSO.JSP: <%@page import="java.io.File"%> <APPLET ARCHIVE="AppletGrafica.jar, jfreechart-1.0.13.jar,jcommon1.0.16.jar,jfreechart-1.0.13-experimental.jar" CODE="applet.AppletGrafica" width=530 height=400 ALT="You should see an applet, not this text."> <PARAM name=stringa value=" <% String selectedFile = null; String root = (new File(".")).getAbsolutePath(); //out.println(root+"<br>"); String jsp_centrale = "jsp_centrale.jsp"; root = root.substring(0, root.length() - 1) + "struttura/"; File dataFile = new File(request.getParameter("dataFile")); out.println(dataFile); %> "> </APPLET> <br> <input type="button"value="PNG" onclick="window.location.href='< %out.print("http://143.225.93.195:8080/definitivo/"+dataFile+".png") ;%>'"> <input type="button"value="JPEG" onclick="window.location.href='< %out.print("http://143.225.93.195:8080/definitivo/"+dataFile+".jpg") ;%>'"> <input type="button"value="PDF" onclick="window.location.href='< Gianluca Coda 566/557 Pagina 116 di 131 %out.print("http://143.225.93.195:8080/definitivo/"+dataFile+".pdf") ;%>'"> <input type="button"value="SVG" onclick="window.location.href='< %out.print("http://143.225.93.195:8080/definitivo/"+dataFile+".svg") ;%>'"> MENU_JOB.JSP: <link rel="stylesheet" href="jquery.treeview.css"> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js "></script> <script src="jquery.cookie.js" type="text/javascript"></script> <script src="jquery.treeview.js" type="text/javascript"></script> <script type="text/javascript" src="demo.js"></script> <ul id="red" class="treeview-red"> <li><a target="frame_centrale" href="http://143.225.93.195:8080/Tirocinio/atlas.coda.jsp"><span>atl as</span></a> <ul> <li><a target="frame_centrale" href="http://143.225.93.195:8080/Tirocinio/atlas.atlas.vo.jsp"><span >atlas VO</span></a> </li> <li><a target="frame_centrale" href="http://143.225.93.195:8080/Tirocinio/other.atlas.vo.jsp"><span >other</span></a></li> </ul> Gianluca Coda 566/557 Pagina 117 di 131 </li> <li><a target="frame_centrale" href="http://143.225.93.195:8080/Tirocinio/egee_long.coda.jsp"><span >egee_long</span></a> <ul> <li><a target="frame_centrale" href="http://143.225.93.195:8080/Tirocinio/argo.egee_long.vo.jsp"><s pan>argo VO</span></a></li> <li><a target="frame_centrale" href="http://143.225.93.195:8080/Tirocinio/biomed.egee_long.vo.jsp"> <span>biomed VO</span></a></li> <li><a target="frame_centrale" href="http://143.225.93.195:8080/Tirocinio/gilda.egee_long.vo.jsp">< span>gilda VO</span></a></li> <li><a target="frame_centrale" href="http://143.225.93.195:8080/Tirocinio/superbvo.egee_long.vo.jsp "><span>superbvo.org VO</span></a></li> <li><a target="frame_centrale" href="http://143.225.93.195:8080/Tirocinio/other.egee_long.vo.jsp">< span>other</span></a></li> </ul> </li> <li><a target="frame_centrale" href="http://143.225.93.195:8080/Tirocinio/egee_short.coda.jsp"><spa n>egee_short</span></a> Gianluca Coda 566/557 Pagina 118 di 131 <ul> <li><a target="frame_centrale" href="http://143.225.93.195:8080/Tirocinio/argo.egee_short.vo.jsp">< span>argo VO</span></a></li> <li><a target="frame_centrale" href="http://143.225.93.195:8080/Tirocinio/biomed.egee_short.vo.jsp" ><span>biomed VO</span></a></li> <li><a target="frame_centrale" href="http://143.225.93.195:8080/Tirocinio/gilda.egee_short.vo.jsp"> <span>gilda VO</span></a></li> <li><a target="frame_centrale" href="http://143.225.93.195:8080/Tirocinio/other.egee_short.vo.jsp"> <span>other</span></a></li> </ul> </li> <li><a target="frame_centrale" href="http://143.225.93.195:8080/Tirocinio/grisu_long.coda.jsp"><spa n>grisu_long</span></a> <ul> <li><a target="frame_centrale" href="http://143.225.93.195:8080/Tirocinio/argo.grisu_long.vo.jsp">< span>argo VO</span></a></li> <li><a target="frame_centrale" href="http://143.225.93.195:8080/Tirocinio/gilda.grisu_long.vo.jsp"> <span>gilda VO</span></a> Gianluca Coda 566/557 Pagina 119 di 131 <li><a target="frame_centrale" href="http://143.225.93.195:8080/Tirocinio/other.grisu_long.vo.jsp"> <span>other</span></a></li> </ul> </li> <li><a target="frame_centrale" href="http://143.225.93.195:8080/Tirocinio/grisu_short.coda.jsp"><sp an>grisu_short</span></a> <ul> <li><a target="frame_centrale" href="http://143.225.93.195:8080/Tirocinio/gilda.grisu_short.vo.jsp" ><span>gilda VO</span></a></li> <li><a target="frame_centrale" href="http://143.225.93.195:8080/Tirocinio/other.grisu_short.vo.jsp" ><span>other</span></a></li> </ul> <li><a target="frame_centrale" href="http://143.225.93.195:8080/Tirocinio/unina_long.coda.jsp"><spa n>unina_long</span></a> <ul> <li><a target="frame_centrale" href="http://143.225.93.195:8080/Tirocinio/unina.unina_long.vo.jsp"> <span>unina.it VO</span></a></li> <li><a target="frame_centrale" href="http://143.225.93.195:8080/Tirocinio/other.unina_long.vo.jsp"> <span>other</span></a></li> Gianluca Coda 566/557 Pagina 120 di 131 </ul> </li> </ul> MENU.JSP: <link rel="stylesheet" href="jquery.treeview.css"> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js "></script> <script src="jquery.cookie.js" type="text/javascript"></script> <script src="jquery.treeview.js" type="text/javascript"></script> <script type="text/javascript" src="demo.js"></script> <ul id="red" class="treeview-red"> <%@page import="java.io.File"%> <%@page import="java.util.LinkedList"%> <%@page import="java.util.Arrays"%> <% String root = (new File(".")).getAbsolutePath(); String jsp_centrale = "jsp_centrale.jsp"; root = root.substring(0, root.length() - 1) + "struttura/"; File dir = new File(root); File[] listFile = dir.listFiles(); if (listFile != null && listFile.length > 0) { for (File file : listFile) { if (!(file.getName().charAt(0) == '.') && file.isDirectory()) { //out.println("<li><a target=\"frame_centrale\" href=\"" + jsp_centrale + "?folder=" + file + "\"><span>" + file.getName() + "</span></a></li>"); out.println("<li><a target=\"frame_centrale\" href=\"" + jsp_centrale + "?folder=" + file.getName() + "\"><span>" + file.getName() + "</span></a>"); out.println("<ul>"); File[] firstListFiles = file.listFiles(); Arrays.sort(firstListFiles, 0, firstListFiles.length); for (File firstLevelFile : firstListFiles) { if (firstLevelFile.isDirectory()) { out.println("<li><a target=\"frame_centrale\" href=\"" + jsp_centrale + "?folder=" + file.getName() Gianluca Coda 566/557 Pagina 121 di 131 +"/"+firstLevelFile.getName() + "\"><span>" + firstLevelFile.getName() + "</span></a>"); out.println("<ul>"); File[] secondListFiles = firstLevelFile.listFiles(); Arrays.sort(secondListFiles, 0, secondListFiles.length); for (File secondLevelFile : secondListFiles) { if (secondLevelFile.isDirectory()) { out.println("<li><a target=\"frame_centrale\" href=\"" + jsp_centrale + "?folder=" + file.getName()+"/"+firstLevelFile.getName() + "/"+secondLevelFile.getName() + "\"><span>" + secondLevelFile.getName() + "</span></a>"); out.println("<ul>"); File[] thirdListFiles = secondLevelFile.listFiles(); Arrays.sort(thirdListFiles, 0, thirdListFiles.length); for (File thirdLevelFile : thirdListFiles) { if (thirdLevelFile.isDirectory()) { out.println("<li><a target=\"frame_centrale\" href=\"" + jsp_centrale + "?folder=" + file.getName()+"/"+firstLevelFile.getName() + "/"+secondLevelFile.getName()+ "/"+thirdLevelFile.getName() + "\"><span>" + thirdLevelFile.getName() + "</span></a></li>"); //out.println("<ul>"); //for (File fourthLevelFile : thirdLevelFile.listFiles()) //if (fourthLevelFile.isDirectory()) //out.println("</li></ul>"); }//IF5 }//FOR4 out.println("</li></ul>"); }//IF4 }//FOR3 out.println("</li></ul>"); }//IF3 }//FOR2 out.println("</li></ul>"); }//IF2 }//FOR1 } //IF1 %> </ul> Gianluca Coda 566/557 Pagina 122 di 131 MENU_INVERSO.JSP: <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" import="java.sql.*" import="bean.*" import="java.util.*"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO8859-1"> <jsp:useBean id="DBJ" class="bean.Job" scope="session"/> <jsp:useBean id="DBJV" class="bean.JobView" scope="session"/> <title>View Vo</title> </head> <body> <link rel="stylesheet" href="jquery.treeview.css"> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js "></script> <script src="jquery.cookie.js" type="text/javascript"></script> <script src="jquery.treeview.js" type="text/javascript"></script> <script type="text/javascript" src="demo.js"></script> <ul id="red" class="treeview-red"> <%@page <%@page <%@page <%@page <%@page import="java.io.File"%> import="java.util.LinkedList"%> import="java.util.Arrays"%> import="java.util.HashMap"%> import="java.util.Iterator"%> <% String root = (new File(".")).getAbsolutePath(); String jsp_centrale = "jsp_centrale_inverso.jsp"; root = root.substring(0, root.length() - 1) + "struttura/"; File dir = new File(root); File[] listFile = dir.listFiles(); // se la struttura delle cartelle non è vuota if (listFile != null && listFile.length > 0) { HashMap<String, LinkedList<String>> voCodeMap = null; // mappa contenente tutte le VO e per ognuna di esse la lista delle code Gianluca Coda 566/557 Pagina 123 di 131 sulle quali è presente // (String: PathName della cartella della VO, LinkedList<String>: lista dei PathName delle cartelle delle code) // ciclo su file-cartelle della struttura for (File file : listFile) { // se è una sottocartella della struttura (giornalieri, settimanali, ...) voCodeMap = new HashMap<String, LinkedList<String>>(); // nuova mappa per la sottocartella della struttura in esame (giornalieri, settimanali, ...) if (!(file.getName().charAt(0) == '.') && file.isDirectory()) { // inserimento voce menu di primo livello (giornalieri, settimanali, ....) out.println("<li><a target=\"frame_centrale\" href=\"" + jsp_centrale + "?dataFile=" + file.getName() + "/" + file.getName() + ".tot" + "\"><span>" + file.getName() + "</span></a>"); out.println("<ul>"); File[] firstListFiles = file.listFiles(); Arrays.sort(firstListFiles, 0, firstListFiles.length); // ciclo sul contenuto delle cartelle di primo livello for (File firstLevelFile : firstListFiles) { // per ogni cartella delle code if (firstLevelFile.isDirectory()) { //String codaDirPathName = file.getName() +"/"+firstLevelFile.getName(); // pathname della cartella della coda String codaName = firstLevelFile.getName(); // nome della coda File[] secondListFiles = firstLevelFile.listFiles(); Arrays.sort(secondListFiles, 0, secondListFiles.length); // ciclo sul contenuto delle cartelle di secondo livello (code) for (File secondLevelFile : secondListFiles) { // per ogni cartella delle VO if (secondLevelFile.isDirectory()) { LinkedList<String> codeList = voCodeMap.remove(secondLevelFile.getName()); if (codeList == null) codeList = new LinkedList<String>(); codeList.add(codaName); voCodeMap.put(secondLevelFile.getName(), codeList); }//IF4 }//FOR3 }//IF3 Gianluca Coda 566/557 Pagina 124 di 131 }//FOR2 Iterator keysIterator = voCodeMap.keySet().iterator(); while (keysIterator.hasNext()){ String key = (String) keysIterator.next(); out.println("<li><a target=\"frame_centrale\" href=\"" + jsp_centrale + "?dataFile=" + file.getName() + "/" + key + ".vo.VOconc" + "\"><span>" + key + "</span></a>"); out.println("<ul>"); LinkedList<String> codeKeys = voCodeMap.get(key); for(String coda : codeKeys) out.println("<li><a target=\"frame_centrale\" href=\"" + jsp_centrale + "?dataFile=" + file.getName() + "/" + coda + "/" + key + "/" + key + ".vo" + "\"><span>" + coda + "</span></a>" ); out.println("</ul></li>"); // chiusura tag seconda voce (VO) } out.println("</li></ul>"); // chiusura tag prima voce (giornalieri, settimanali, ...) }//IF2 }//FOR1 }//IF1 %> </ul> JSP JOB MONITORING VO: <% ResultSet rs=DBJV.GetJobRunVo("atlas","atlas"); //Job in running sul sistema %> <center><table border="1"> <tr><th>User</th> <th>JOB ID</th> <th>Submitted<br>from</th> <th>Running on<br>(slots/RAM/KSI)</th> <th>Start<br>running</th> <th>Collecting<br>time</th> <th>WCT<br>hh:mm:ss</th> <th>CPU Time<br>hh:mm:ss</th> <th>CPU/WCT<br>%(new/old)</th> <th>RSS<br>MBytes</th> Gianluca Coda 566/557 Pagina 125 di 131 <th>Virtual<br>MBytes</th></tr> <% while(rs.next()){%> <tr><td align="center"><%=rs.getString("User")%> </td> <td align="center"><%=rs.getString("JobId")%> </td> <%if (rs.getString("StatusPBS")==null){ %> <td align="center"><%=rs.getString("L_From_Host")%></td> <td align="center"><%=rs.getString("L_Exec_Host")%>, < %=rs.getString("MEM")%>,?</td> <td align="center"><%=rs.getString("L_Submit_time")%></td> <td align="center" ><%=rs.getString("TimeC")%></td> <td align="center" ><%=rs.getString("WCT")%></td> <td align="center"><%=rs.getString("CPUTime")%></td> <td align="center" >?</td> <td align="center" >?</td> <td align="center" ><%=rs.getString("Vmem")%></td> <%}else{ %> <td align="center"><%=rs.getString("P_From_Host")%></td> <td align="center"><%=rs.getString("P_WN")%>,<%=rs.getString("MEM") %>,?</td> <td align="center"><%=rs.getString("TimeR")%></td> <td align="center" ><%=rs.getString("TimeC")%></td> <td align="center" ><%=rs.getString("WCT")%></td> <td align="center"><%=rs.getString("CPUTime")%></td> <td align="center" >?</td> <td align="center" >?</td> <td align="center" ><%=rs.getString("Vmem")%></td> <%} %> </tr> Gianluca Coda 566/557 Pagina 126 di 131 <%} DBSupport.setClose(rs);%> </table></center> </body> </html> JSP JOB MONITORING CODA: <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" import="java.sql.*" import="bean.*" import="java.util.*"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO8859-1"> <jsp:useBean id="DBJ" class="bean.Job" scope="session"/> <jsp:useBean id="DBJV" class="bean.JobView" scope="session"/> <title>View Coda</title> </head> <body> <% ResultSet rs=DBJV.GetJobCoda("atlas"); //Job in running sul sistema %> <center><table border="1"> <tr><th>User</th> <th>JOB ID</th> <th>Vo</th> Gianluca Coda 566/557 Pagina 127 di 131 <th>Submitted<br>from</th> <th>Running on<br>(slots/RAM/KSI)</th> <th>Start<br>running</th> <th>Collecting<br>time</th> <th>WCT<br>hh:mm:ss</th> <th>CPU Time<br>hh:mm:ss</th> <th>CPU/WCT<br>%(new/old)</th> <th>RSS<br>MBytes</th> <th>Virtual<br>MBytes</th></tr> <% while(rs.next()){%> <tr><td align="center"><%=rs.getString("User")%> </td> <td align="center"><%=rs.getString("JobId")%> </td> <%if (rs.getString("NomeVo")==null){ %> <td width="15%"> Local </td> <% }else {%> <td width="15%"> < %=rs.getString("NomeVo")%> </td> <%} %> <%if (rs.getString("StatusPBS")==null){ %> <td align="center"><%=rs.getString("L_From_Host")%></td> <td align="center"><%=rs.getString("L_Exec_Host")%>, < %=rs.getString("MEM")%>,?</td> <td align="center"><%=rs.getString("L_Submit_time")%></td> <td align="center" ><%=rs.getString("TimeC")%></td> <td align="center" ><%=rs.getString("WCT")%></td> <td align="center"><%=rs.getString("CPUTime")%></td> <td align="center" >?</td> <td align="center" >?</td> <td align="center" ><%=rs.getString("Vmem")%></td> <%}else{ %> <td align="center"><%=rs.getString("P_From_Host")%></td> <td align="center"><%=rs.getString("P_WN")%>,<%=rs.getString("MEM") %>,?</td> <td align="center"><%=rs.getString("TimeR")%></td> <td align="center" ><%=rs.getString("TimeC")%></td> Gianluca Coda 566/557 Pagina 128 di 131 <td <td <td <td <td align="center" ><%=rs.getString("WCT")%></td> align="center"><%=rs.getString("CPUTime")%></td> align="center" >?</td> align="center" >?</td> align="center" ><%=rs.getString("Vmem")%></td> <%} %> </tr> <%} DBSupport.setClose(rs);%> </table></center> </body> </html> JSP JOB MONITORING OTHER: <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" import="java.sql.*" import="bean.*" import="java.util.*"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO8859-1"> <jsp:useBean id="DBJ" class="bean.Job" scope="session"/> <jsp:useBean id="DBJV" class="bean.JobView" scope="session"/> <title>View Vo</title> </head> <body> Gianluca Coda 566/557 Pagina 129 di 131 <% ResultSet rs=DBJV.GetJobRunOtherCoda("atlas"); //Job in running sul sistema %> <center><table border="1"> <tr><th>User</th> <th>JOB ID</th> <th>Submitted<br>from</th> <th>Running on<br>(slots/RAM/KSI)</th> <th>Start<br>running</th> <th>Collecting<br>time</th> <th>WCT<br>hh:mm:ss</th> <th>CPU Time<br>hh:mm:ss</th> <th>CPU/WCT<br>%(new/old)</th> <th>RSS<br>MBytes</th> <th>Virtual<br>MBytes</th></tr> <% while(rs.next()){%> <tr><td align="center"><%=rs.getString("User")%> </td> <td align="center"><%=rs.getString("JobId")%> </td> <%if (rs.getString("StatusPBS")==null){ %> <td align="center"><%=rs.getString("L_From_Host")%></td> <td align="center"><%=rs.getString("L_Exec_Host")%>, < %=rs.getString("MEM")%>,?</td> <td align="center"><%=rs.getString("L_Submit_time")%></td> <td align="center" ><%=rs.getString("TimeC")%></td> <td align="center" ><%=rs.getString("WCT")%></td> <td align="center"><%=rs.getString("CPUTime")%></td> <td align="center" >?</td> <td align="center" >?</td> <td align="center" ><%=rs.getString("Vmem")%></td> <%}else{ %> <td align="center"><%=rs.getString("P_From_Host")%></td> <td align="center"><%=rs.getString("P_WN")%>,<%=rs.getString("MEM") Gianluca Coda 566/557 Pagina 130 di 131 %>,?</td> <td align="center"><%=rs.getString("TimeR")%></td> <td align="center" ><%=rs.getString("TimeC")%></td> <td align="center" ><%=rs.getString("WCT")%></td> <td align="center"><%=rs.getString("CPUTime")%></td> <td align="center" >?</td> <td align="center" >?</td> <td align="center" ><%=rs.getString("Vmem")%></td> <%} %> </tr> <%} DBSupport.setClose(rs);%> </table></center> </body> </html> Gianluca Coda 566/557 Pagina 131 di 131