68

j2007 01 jj2

Embed Size (px)

Citation preview

Page 1: j2007 01 jj2
Page 2: j2007 01 jj2
Page 3: j2007 01 jj2

EDITORIALEwww.infomedia.it

n.2 - gennaio/febbraio 2007 3

JAVA Journal

BIMESTRALE - ANNO 1 - N.2

D I R E T T O R E R E S P O N S A B I L E MA R I A L E T I Z I A MA R I

(M M A R I@I N F O M E D I A . I T )

D I R E T T O R E E S E C U T I V O

MI C H E L E SC A B A R R À

(M I C H E L E .S C I A B A R R A@J A V A J O U R N A L . I T )

E D I T O R I A L B O A R D

UG O LA N D I N I , ST E F A N O SA N N A, TO N Y MO B I L Y

C O L L A B O R A T O R I

MI C H E L E FE R R E T T IUG O LA N D I N I

ST E F A N O SA N N AED O A R D O SC H E P I S

FA B I O ST A R OMA T T E O Z I N A T O

GR U P P O ED I T O R I A L E IN F O M E D I A S R L

V I A VA L D E R A P.11656038 PO N S A C C O (PI ) I T A L I A

TE L 0587736460 FA X 0587732232E -M A I L I N F O@I N F O M E D I A . I T

S I T O WE B W W W. I N F O M E D I A . I T

D I R E Z I O N E NA T A L E FI N O (N F I N O@I N F O M E D I A . I T )

C O N T A T T I

TE C H N I C A L B O O K(B O O K@I N F O M E D I A . I T )

M A R K E T I N G & A D V E R T I S I N GSE G R E T E R I A : 0587736460

M A R KE T I N G@I N F O M E D I A . I T

A M M I N I S T R A Z I O N E(A M M I N I S T R A Z I O N E@I N F O M E D I A . I T )

S E G R E T E R I A ( I N F O@I N F O M E D I A . I T )

G R A F I C AS T E F A N O B E L L A N I

(G R A F I C A@G R U P P O I N F O M E D I A . I T )

U F F I C I O A B B O N A M E N T ITE L 0587736460 FA X 0587732232

A B B O N A M E N T I@I N F O M E D I A . I T

W W W. I N F O M E D I A . I T

J A V A E T U T T I I M A R C H I D E R I V A T I S O N O M A R C H I O M A R C H I R E G I S T R A T I D I SU N M I C R O S Y S T E M S , I N C . N E G L I USA E I N A L T R I PA E S I . I L GR U P P O E D I T O R I A L E I N F O M E D I A È I N D I P E N -D E N T E D A SU N M I C R O S Y S T E M S , I N C .

Java Journal in presa diretta

In occasione del secondo numero di una rivista, Java Journal compresa, non è cer-to tempo di bilanci; però un resoconto su come sono andate le cose dopo il primo numero si può dire. Innanzitutto, la rivista sta riscuotendo interesse. A riprova di ciò abbiamo ottenuto un primo riscontro anche dalla casa madre di Java, Sun Microsystems, dalla quale siamo completamente indipendenti, ma con cui siamo onorati, quando vi è occasione, di poter collaborare. E in effetti una prima forma di collaborazione si è concretizzata in occasione dell’evento più importante nel mondo Java, non solo degli ultimi mesi.Tra l’uscita del primo numero e la preparazione del secondo, è avvenuta una delle più importanti transizioni per Java: l’annuncio del rilascio dei sorgenti Java in licenza GPL. Dopo tanti anni, Java diventa, finalmente e definitivamente, un prodotto sia Open Source sia Free Software. In questo modo, Java soddisfa anche i palati più esigenti in fatto di “apertura” e di “libertà” della piattaforma.In seguito a questo importante avvenimento, abbiamo avuto occasione di poter intervistare, su invito di Sun, il principale artefice di questa trasformazione: Simon Phipps. Simon, per chi non è addentro agli organigrammi di Sun, è il direttore delle iniziative Open Source di Sun. Prima di lavorare in Sun è stato un importante pro-tagonista della adozione di Java, XML e dell’Open Source in IBM. Un personaggio chiave, quindi, di indiscussa rilevanza.E il sottoscritto, vostro umile (ma fiero) direttore della neonata Java Journal, ha avu-to l’onore di intervistarlo nel corso di una sua visita in Italia, a Milano. Nonostante l’apprensione del dover sostenere, e in inglese, la prima intervista “ufficiale” per Java Journal con un personaggio di tale spessore, sono sopravvissuto indenne. E gra-zie agli anni di analisi delle problematiche dell’Open Source, gli ho posto un numero di domande, abbastanza dettagliate. Troverete in questo stesso numero l’intervista a Phipps, ovviamente … tradotta in italiano (un buon motivo in più per sostenere le iniziative di Java Journal). Detto questo, passiamo alle “comunicazioni di servizio”. Innanzitutto, abbiamo de-ciso di utilizzare meglio lo spazio cartaceo, e di eliminare il corso base di Java, che abbiamo invece sostituito con un videocorso. Il videocorso era nato in altri contesti, ma ho avuto la possibilità di portarlo in dote alla rivista Java Journal. Questo video-corso lo abbiamo anche offerto gratuitamente a tutti coloro che si sono abbonati fino al 31 gennaio 2007. Inoltre, da questo numero Stefano Sanna inaugura la rubrica “Community Watch”, in cui ci parla di delle varie iniziative in corso nella comunità Java, di cui è, da sem-pre, un membro attivissimo.Lo speciale di questo numero riguarda la Java Micro Edition (Java ME), che riscuote sempre più interesse. E non è un caso che il primo codice di Java disponibile come Open Source sia proprio una implementazione di Java ME. Detto questo, non mi resta che passare la mano ai collaboratori di questo secondo numero: gli argomenti presentati sono tutti molto interessanti e gli articoli, come potrete apprezzare, sono stati redatti da professionisti di ampia e consolidata esperienza.

Michele SciabarràDirettore Esecutivo

Java Journal

Page 4: j2007 01 jj2

Speciale Java Micro EditionI package opzionali della Java Micro Editiondi Edoardo Schepis 8J2ME Best Practicedi Matteo Zinato 15

EducationalApache Antdi Michele Ferretti 31Java 5, le buone nuove e le novità meno buoneSeconda partedi Ugo Landini 38

Focus Resource Management con JMXdi Fabio Staro 49Accedere a file di Excel in Javadi Luigi Zanderighi 63

Rubriche

CommunityIntervista a Simon PhippsChief Open Source Officer di Sun Microsystemsdi Michele Sciabarrà 46

Community Watchdi Stefano Sanna 60

IdiomaticaCodice orribile, antipattern e pratiche discutibilidi Ugo Landini 61

SOMMARIO Gennaio/Febbraio 2007

numero 2 JAVA Journal

n.2 - gennaio/febbraio 2007

Page 5: j2007 01 jj2
Page 6: j2007 01 jj2

IN VETRINA JAVA JOURNAL 2

n.2 - gennaio/febbraio 20076

IN VETRINA JAVA JOURNAL 2

Scrivi a [email protected] specificando nell’oggetto della e-mail:

IN VETRINA JAVA JOURNAL 2OPPURE

inviaci il coupon al numero di fax 0587/732232

Potrai acquistare i libri qui riportaticon uno SCONTO ECCEZIONALE

del 10% anche se acquisti solo un libroOPPURE

del 20% se acquisti 3 libri

JAVA JOURNAL 2

Learning JavaScriptdi S. Power

Attraverso gli esempi più recenti di pratiche di sviluppo per i moderni browser, il libro insegna come in-tegrare il linguaggio con l’ambiente del browser. Attraverso la lettura di questo libro si padroneggerà l’uso dell’intero linguaggio JavaScript e di molti object model presenti nei browser web; sarà anche possibile creare applicazioni Ajax elementari.

O’ Reilly351 pp - 31,00 EuroISBN 0596527462

Mobile 3D Graphicsdi A. Malizia

Il libro illustra gli elementi fonda-mentali per la programmazione grafica 3D mobile con gli standard API, trattando sia le interfacce di base che quelle avanzate, oltre ai principali device wireless e mobile che supportano la programma-zione grafica 3D mobile. Include una spiegazione esaustiva sulla programmazione 3D mobile; un lungo elenco di esempi di codice in C e in Java; combina metodi grafici 2D e 3D.

Springer162 pp - 58,60 EuroISBN 1846283833

Ajax trucchi e segretidi B. W. Perry

Il libro presenta una serie di solu-zioni pronte all’uso e svela tutte le potenzialità di Ajax. Imparerete a dotare le form HTML di funzionalità Ajax, personalizzan-dole per rispondere alle aspettative degli utenti, esplorare e combinare tra loro le API di Google Maps, di Yahoo! Maps e di GeoURL e molto altro ancora.

Tecniche Nuove432 pp - 29,90 EuroISBN 8848119751

An introduction to Design Patterns in C++ with Qt 4

di A. Ezust, P. Ezust

Si tratta di un tutorial completo che non dà per scontate pregresse conoscenze di C, C++, oggetti o pattern. Una guida a piccoli passi corredata di numerosi esempi ed esercizi di Qt 4.1.

Prentice Hall656 pp - 46,70 EuroISBN 0131879057

Java Generics and Collections

di M. Naftalin, P. Wadler

Il libro copre ogni ambito, dagli usi dei generics fino ai casi più particolari. Insegna tutto ciò che serve sulle collections libraries, permettendo di sapere sempre quale sia la col-lection appropriata per il compito dato e come usarla.

O’ Reilly294 pp - 34,95 EuroISBN 0596527756

Enterprise Service Oriented Architectures.

Concepts, Challenges, RecommendationsJ. McGovern et al

Enterprise Service Oriented Archi-tectures permette al lettore di far comunicare diverse applicazioni in modo “loosely coupled”. Si tratta di un classico attraverso cui si possono imparare i principi basilari e fondamentali di un’archi-tettura service-oriented.

Springer408 pp - 57,80 EuroISBN 140203704X

Visual Basic 2005Tecniche e soluzioni

di J. Kent

Questa guida all’autoapprendi-mento inizia spiegando i concetti fondamentali della programma-zione. Quindi illustra come creare sofisticati elementi dell’interfaccia utente grafica, tra cui menu, barre degli strumenti e finestre di dialogo. Al termine del libro, il lettore sarà in grado di realizzare applicazioni Windows e Web anche con l’utilizzo di database. Corredato di quiz alla fine di ogni capitolo ed un esame finale.

Mc Graw Hill300 pp, euro 26.00ISBN 8838644470

Reti domestichedi S. Lowe

Al giorno d’oggi milioni di compu-ter sono connessi a Internet, ma il collegamento di pochi computer in una rete casalinga risulta ancora difficile. Con un linguaggio chiaro e senza termini gergali, e con un tocco di umorismo, il libro aiuta a capire tutto ciò che è necessario per installare una rete domestica.

O’ Reilly/Tecniche Nuove250 pp - 24,90 EuroISBN 8848118437

Page 7: j2007 01 jj2

IN VETRINA JAVA JOURNAL 2

n.2 - gennaio/febbraio 20076

IN VETRINA JAVA JOURNAL 2

Scrivi a [email protected] specificando nell’oggetto della e-mail:

IN VETRINA JAVA JOURNAL 2OPPURE

inviaci il coupon al numero di fax 0587/732232

Potrai acquistare i libri qui riportaticon uno SCONTO ECCEZIONALE

del 10% anche se acquisti solo un libroOPPURE

del 20% se acquisti 3 libri

JAVA JOURNAL 2

Programmare con Visual Basic 2005 Express

di W. Wang

La maggior parte dei libri di pro-grammazione cerca di insegnare contemporaneamente come scrive-re un programma utilizzando un lin-guaggio specifico, come program-mare, come scrivere un programma utilizzando un particolare ambiente di sviluppo e un compilatore. Ciò può portare alla confusione. Visual Basic 2005 Express offre una introduzione su come funziona la programmazione indipendentemen-te dal linguaggio usato.

O’ Reilly/Tecniche Nuove560 pp - 39,90 EuroISBN 8848119484

Firewall e VPNdi T. Behrens et al.

Vero e proprio punto di riferimento nella protezione delle reti e configu-razione di Firewall e VPN. Oltre alla teoria necessaria generale è pre-sente un’attenta descrizione delle procedure con il software 7.0 per l’intera famiglia di Firewall Cisco PIX della serie 500. Questo volume spiega dettagliatamente come sfruttare le funzionalità integrate per reti virtuali reti private virtuali ad accesso remoto e site-to-site offerte dai Firewall Cisco PIX.

Mc Graw Hill590 pp - 44,00 EuroISBN 8838644624

Riparazione & upgrade per pc, workstation e server

di S. Mueller et al

Da quasi 20 anni i testi di Scott Mueller sono il riferimento per la manutenzione e la comprensione dei computer. Ora anche gli ammi-nistratori di workstation e server troveranno soddisfazione in un te-sto con spiegazioni complete sulla riparazione e sull’upgrade. Questo libro è la prima linea di difesa per le società che gestiscono installazioni e supporto dei propri computer e un eccellente riferimento per gli amministratori esperti.

Mc Graw Hill1050 pp - 74,00 EuroISBN 8838644640

Page 8: j2007 01 jj2

JAVA Journal

8 n.2 - gennaio/febbraio 2007

speciale Java Micro Edition

In questo articolo prendiamo in considerazione le principali librerie della Java Micro Edition, orientate allo sviluppo di applicazioni per dispostivi mobili, tra cui cellulari, smartphone, PDA e palmari

>> di Edoardo Schepis ([email protected])

Dopo una breve panoramica sulla tecnologia e sulle principali im-plementazioni oggi presenti sul mercato, sposteremo l’attenzione su quali novità ci riserva il futuro. Sfruttando le API ancora in fase di studio vedremo come sarà pos-

sibile ben presto gestire direttamente da una nostra applicazione:

• una conversazione telefonica, • i micropagamenti, • ed anche una trasmissione interattiva del digitale

terrestre.

Per capire quanto sia articolato e vario il mondo delle specifiche Java in campo Micro Edition dobbiamo in-nanzitutto fare qualche premessa riguardante il pro-cesso relativo alla loro stesura e implementazione.

Il Java Community Process

Tutto nasce, e direi cresce, nell’ambito del Java Community Process (JCP): una organizzazione com-posta da membri attivi, aperta a tutti (chiunque può prendervi parte), che si occupa della stesura e dello sviluppo delle specifiche tecniche nel mondo Java: le cosiddette Java Specification Requests (JSR).

Il JCP ha come compito principale quello di defini-re ed alimentare l’evoluzione della tecnologia Java, assicurandone un elevato livello di stabilità e di interoperabilità proprio grazie alla partecipazione al processo di tutti i suoi membri.

Dal lavoro del JCP vengono fuori tutte le JSR sulle quali si basano le applicazioni Java attualmente realizzate nel mondo, negli ambiti Java Standard, Enterprise e Micro Edition.Per un breve elenco delle principali JSR relative alla diverse Edition della piattaforma Java rimandiamo al Riquadro 1. Nel seguito ci occuperemo più in detta-glio di quelle legate alla Micro Edition.

Le origini della Java Micro Edition

Al momento della sua nascita (parliamo dell’anno 1999) la Java Micro Edition costituiva più che altro il tentativo di portare Java là dove esisteva solo tecno-logia embedded proprietaria. Nessun altro operatore tecnologico nel campo del software aveva investito abbastanza in quell’area, lasciando così che ciascun produttore di hardware si occupasse dello sviluppo del software secondo modalità e specifiche del tutto chiuse e proprietarie.

Il primo tentativo di rompere con questo approccio fu fatto con l’obiettivo di permettere agli svilup-patori Java di creare proprie applicazioni anche su quei dispositivi che fino ad allora erano rimasti accessibili solo nei laboratori delle aziende produt-trici. Si partì con un approccio a dir poco analitico: biso-gnava innanzitutto classificare il vasto mondo della tecnologia “micro”; e non era certo semplice. In que-st’area rientravano infatti le smart card, i dispositivi mobili con cui oggi abbiamo maggiore confidenza (cellulari e PDA), ma anche i navigatori satellitari delle nostre automobili e i decoder del digitale ter-restre tanto in voga oggi. Si iniziò così a parlare di configuration e profile.

I package opzionali della Java Micro Edition

Page 9: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 2007 9

Java Micro Edition speciale

RIQUADRO 1 Elenco delle principali JSR presenti nella Java Enterprise e nella Standard Edition

Elenco delle principali JSR presenti nella Java Enterprise e nella Standard Edition

Nella tabella di seguito sono riportate le principali JSR definite per le piattaforma Java Enterprise, Standard e Micro Edition.È possibile approfondire lo studio di ciascuna di esse collegandosi al sito “http://www.jcp.org/en/jsr/detail?id=” se-guito dal numero della JSR di interesse.

Nel sito è possibile trovare un documento contenente le specifiche e una reference implementation da utilizzare per lo sviluppo software.

Java Enterprise Edition

JSR 3 Java Management Extensions (JMX) Specification

JSR 16 J2EE Connector Architecture

JSR 19 Enterprise JavaBeans 2.0

JSR 53 Java Servlet 2.3 and JavaServer Pages 1.2 Specifications

JSR 54 JDBC 3.0 Specification

JSR 116 SIP Servlet APIJSR 124 J2EE Client Provisioning SpecificationJSR 127 JavaServer Faces

JSR 151 Java 2 Platform, Enterprise Edition 1.4 (J2EE 1.4) SpecificationJSR 162 Portlet APIJSR 220 Enterprise JavaBeans 3.0

JSR 233 J2EE Mobile Device Management and Monitoring Specification

Java Standard Edition

JSR 12 Java Data Objects (JDO) Specification

JSR 51 New I/O APIs for the Java Platform

JSR 63 Java API for XML Processing 1.1

JSR 80 Java USB API

JSR 93 Java API for XML Registries 1.0 (JAXR)

JSR 101 Java APIs for XML based RPC

JSR 176 J2SE 5.0 (Tiger) Release Contents

JSR 189 Java 3D API 1.4

JSR 197 Generic Connection Framework Optional Package for the J2SE Platform

JSR 241 The Groovy Programming Language

JSR 270 Java SE 6 (“Mustang”) Release Contents

Java Micro Edition

JSR 30 J2ME Connected, Limited Device Configuration

JSR 36 Connected Device Configuration

JSR 37 Mobile Information Device Profile for the J2ME Platform

JSR 75 PDA Optional Packages for the J2ME Platform

JSR 82 Java APIs for Bluetooth

JSR 118 Mobile Information Device Profile 2.0

JSR 120 Wireless Messaging API

JSR 135 Mobile Media API

JSR 172 J2ME Web Services Specification

JSR 177 Security and Trust Services API for J2ME

JSR 179 Location API for J2ME

JSR 184 Mobile 3D Graphics API for J2ME

JSR 272 Mobile Broadcast Service API for Handheld Terminals

Page 10: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 200710

speciale Java Micro Edition

Configuration e profile

Una configuration definisce il minimo ambiente per l’ese-cuzione del Runtime Java per una certa famiglia di disposi-tivi. Si tratta in altre parole dell’accoppiata Java VM e API di base che permettano l’esecuzione di una applicazione Java su una particolare famiglia di dispositivi che risponda a certe caratteristiche:

• con la connected limited device configuration (CLDC) si indirizzano i dispositivi con capacità molto limitate in termini di I/O, di risorse di memoria e di possibilità com-putazionali;

• con la connected device configuration (CDC), un sovrain-sieme della precedente, si approcciano i dispositivi con qualche risorsa hardware in più e che si avvicinano di più al mondo della Standard Edition.

Un profile rappresenta invece l’insieme delle API che, aggiunte ad una configuration, forniscono il supporto completo per lo svi-luppo di una applicazione per un certo insieme di dispositivi. Il Mobile Information Device Profile (MIDP), ad esempio, costituisce quell’insieme di librerie che si basa sul CLDC e che permette lo sviluppo di applicazioni per dispositivi, tra cui i cellulari.

In Figura 1 è possibile vedere a confronto le diverse Edi-tion della Java Platform.

Superare i limiti: l’approccio a package

Di configuration e profile ne sono state rilasciati un paio di versioni a testa. Attualmente siamo alla CLDC 1.1 e al MIDP 2.0, ma l’idea sulla quale si fonda tale classificazione ci fa suonare da subito un piccolo campanello d’allarme. Chi ha avuto a che fare con i sistemi embedded sa, più degli altri, che suddividere le librerie per lo sviluppo sulla base delle risorse hardware a disposizione può portare ad una limitazione. Dopo pochi anni l’hardware fa grossi pas-si in avanti e il supporto allo sviluppo applicativo rischia di restare al palo.Se infatti parlare di MIDP nel 2000 significava affrontare quanto di più all’avanguardia ci fosse in campo Java ME, già dopo qualche anno si sono scoperti i limiti di questo approccio.Non era possibile infatti pensare di andare avanti inse-guendo l’evoluzione dell’hardware a colpi di profile (oggi saremmo al MIDP 5.0) e ad un insieme di librerie da dichiarare obsolete (approccio utilizzato da altri e che si

FIGURA 1 Confronto delle Edition della Java Platform

Page 11: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 2007 11

Java Micro Edition speciale

è rivelato dannoso per la complessità e la poca chiarezza delle specifiche e delle interfacce a disposizione dei pro-grammatori).

È così che il JCP decide di risolvere questa esigenza gesten-do l’evoluzione della tecnologia Java ME con l’approccio a package. Nulla di nuovo sotto il sole, si tratta di quanto vie-ne normalmente fatto in area Enterprise e Standard, ma in area Micro c’è un problema da non sottovalutare.Se infatti nelle altre Edition rilasciare una nuova libreria significa rilasciare una JSR con relativa reference imple-mentation e permettere così allo sviluppatore di utilizzarla includendola nel suo ambiente Java Runtime, in ambito Micro non è altrettanto immediato.In Java ME, infatti, si ha a che fare con il firmware! Ossia le API di una certa JSR vivono all’interno dell’ambiente di esecuzione, inglobate nel dispositivo. In altre parole una nuova versione di MIDP o una nuova libreria potrà vedere la luce solo su un nuovo cellulare; mentre i prodotti rilasciati fino ad allora non avranno alcuna possibilità di evoluzione.Non si tratta né di una scelta del JCP né dei costruttori del-l’hardware, ma di una esigenza imprescindibile del settore tecnologico di riferimento. Infatti, in questo settore ogni libreria ha una sua implementazione solo se è strettamente agganciata all’hardware sul quale risiede.Tanto per fare un esempio: le librerie per la gestione del di-splay incluse in MIDP permettono la visualizzazione di im-magini ad una risoluzione che verrà scelta e implementata da ciascun costruttore di cellulari in modo proprietario. Se da una parte lo sviluppatore ha a disposizione una API (interfacce) standard, dall’altra il costruttore ha il compito (non banale) di tradurre l’astrazione in implementazione effettiva. E ciò, se per gli sviluppatori può essere sicura-mente visto come un vantaggio, dal punto di vista del-

l’evoluzione della tecnologia rischia di essere un freno. Nel prossimo paragrafo vedremo come il JCP ha perciò iniziato a rilasciare numerose JSR che vengono definite opzionali in quanto non rientrano nella categoria di configuration o profile e che il costruttore di hardware può decidere di in-cluderle o meno nei propri prodotti.

Alcuni di questi optional package sono ormai degli standard de-facto, perché presenti sulla maggior parte dei dispositi-vi; altri sono delle vere e proprie chimere … al cui insegui-mento si gettano gli sviluppatori più smaliziati.

Optional package: lo stato dell’arte

Si potrebbe proseguire nella panoramica delle JSR in area Java ME passo dopo passo, ma dopo la “relazione intro-duttiva” sulla teoria, penso sia proprio ora di passare ai risvolti pratici di quanto si è detto. Perciò, affronteremo in dettaglio i presupposti dello sviluppo di applicazioni basa-te sulle tecnologie CLDC e MIDP, perché di maggior uso e diffusione sul mercato: infatti, il numero di cellulari sui

FIGURA 2 Trend della piattaforma Java ME

Sull’emulatore fun-ziona tutto… ma non sui

dispositivi!

Page 12: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 200712

speciale Java Micro Edition

quali è possibile trovare una implementazione Java ME è in frenetica crescita!In Figura 2 è possibile vedere il trend positivo del 2005: tanto per fornire qualche numero, siamo di fronte a 635 diversi modelli di cellulari, pari a più di 700 milioni di uni-tà installate. In altre parole, più di 7 cellulari su 10 hanno una Java Virtual Machine a bordo!

I ferri del mestiere: necessari e sufficienti

Abbiamo visto che CLDC e MIDP coprono le aree principali per lo sviluppo di applicazioni per cellulari. E allora di co-s’altro si avrebbe bisogno?Se da una parte CLDC e MIDP rappresentano un supporto essenziale per lo sviluppo di applicazioni (fornendo i mat-toni base e le API per l’accesso alla grafica di alto e basso livello e alla rete), dall’altra parte gli sviluppatori hanno sin dall’inizio notato alcune carenze.Alcune aree, infatti, furono demandate in apposite JSR, che iniziarono a popolare i cosiddetti optional package.La gestione della messaggistica (CBS, ossia Cell Broadcast System, SMS, ossia Short Messaging System, e MMS, ossia Multimedia Messaging System), delle connessioni via Blue-tooth, dell’audio, della grafica avanzata e del file system del cellulare, sono funzionalità lasciate al di fuori del MIDP e di competenza di alcune librerie opzionali.Ecco allora che viene alla luce il vero significato di optional packages:

• un costruttore di cellulari per fregiarsi del marchio Java On Board deve implementare CLDC e MIDP, poi ha la facoltà di includere quanti e quali package opzionali de-sidera.

Gli sviluppatori, quindi, sono costretti a fare i conti con questa frammentazione, affidandosi ad una nuova acce-zione del motto vincente di Java “Write Once, Run Anywhere” (ossia, una volta scritta, la mia applicazione sarà eseguita dappertutto, basta che sia installata una Java Virtual Ma-chine).

Nel mondo Java ME ciò continua a valere, a patto che nes-suno dimentichi cosa offre il mercato e le specificità delle implementazioni: la qual cosa è valida anche nelle altre edition, ma di fatto è esasperata in questo settore. Rischiando di somigliare un po’ troppo a Monsieur de la Palice (il più illustre “lapalissiano”), potrei dire:

• non basta una JVM, ma serve una JVM più le librerie necessarie all’esecuzione dell’applicazione.

In altre parole: gli sviluppatori creano l’applicazione uti-lizzando emulatori e kit di sviluppo che includono le im-plementazioni di riferimento delle librerie scelte, ma poi dovranno fare i conti con le librerie presenti sul cellulare di riferimento. Se volessimo quindi affrontare lo sviluppo di una appli-cazione che sfrutti sul serio le peculiarità di un cellulare (ossia un dispositivo mobile in grado di inviare messaggi, gestire audio e video, ed effettuare connessioni di rete) avremmo bisogno di un minimo di package opzionali a bordo del cellulare. In particolare:

• Wireless Messaging API 1.1 (JSR 120) e Wireless Messaging API 2.0 (JSR 205) per la gestione della messaggistica;

• Bluetooth API (JSR 82) per la gestione delle connessioni bluetooth;

• Mobile Media API (JSR 135) per la gestione avanzata di audio e video;

• PDA API (JSR 75) per la gestione del Personal Informa-tion Management (PIM) e del File System.

In pratica, se volessimo implementare in Java un’applica-zione “a caso” per il nostro cellulare, che permetta di ac-quistare dalla rete dei brani musicali, salvarli sul cellulare, ascoltarli e scambiarli con gli amici attraverso messaggi e/o via Bluetooth avremmo bisogno che il suddetto cellulare supporti, oltre a CLDC e MIDP, anche le sopra citate cinque API opzionali.

Siamo al punto di svolta. Da un mercato pervaso di di-spositivi Java-based ci spostiamo verso un sottoinsieme: significativo, ma pur sempre un sottoinsieme. Non tutti i cellulari hanno infatti a bordo queste librerie e quindi la nostra applicazione potrà funzionare solo in tale sot-toinsieme.

Lo stato dell’arte delle implementazioni Java ME in quest’area ci permette di dire che ad oggi i cellulari con questi package a bordo (non più da considerarsi opzionali a nostro avviso) sono la maggior parte, e comunque sono quelli di riferimento per il mercato. Il che assicura una grande diffusione, almeno potenziale, della nostra appli-cazione.

Ma c’è dell’altro. Non paghi di questo scenario i nostri svi-luppatori, sempre alla ricerca della sottile linea di confine “tra la scienza e la fantascienza”, hanno “sollevato altre eccezioni” e sono alla ricerca di un giusto “catch”: mi pia-cerebbe gestire il segnale GPS per localizzare l’utente; e se volessi interagire con i Webc service? E se volessi creare un’applicazione di segreteria telefonica per il mio cellulare?

Una configurazion

definisce il minimo am-

biente per l’esecuzione

del Runtime Java per

una certa famiglia di di-

spositivi

Page 13: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 2007 13

Java Micro Edition speciale

Da qui in poi entriamo in un campo minato; e ci entriamo bendati! Ci avventuriamo, cioè, in un settore che sa poco di implementazione e ancora troppo di specifiche.

Manca poco

“Ma sugli emulatori tutto funziona!”. È questa la frase che tormenta la vita dello sviluppatore Java ME. Ma ancora più si tormenta quando riesce a scrivere un’ap-plicazione perfettamente funzionante, ma che non può vedere funzionare su nessun cellulare attualmente in commercio. Già, perché, grazie alla prolificità del JCP, molte specifiche sono rilasciate con la relativa referen-ce implementation molto prima di finire sui cellulari; e grazie ai laboratori Sun gli sviluppatori hanno a dispo-sizione uno strumento per lo sviluppo e l’emulazione

delle applicazioni perfettamente integrato (il Sun Wireless Toolkit) che li supporta in tutte le fasi del ciclo di vita del software.Molte delle JSR sono ancora poco diffuse sui cellulari, ma è già possibile utilizzarle nell’emulatore. Più precisamente si tratta delle:

• Location API (JSR 179); • Security and Trust Services API (JSR 177); • SIP API (JSR 180); • J2ME Web Services Specification (JSR 172).

Ci aspettiamo naturalmente una rapida diffusione sul mercato anche di queste implementazioni e, come nel caso delle API ormai consolidate, consigliamo vivamente di anticipare i tempi sfruttando l’enorme vantaggio di avere a disposizione una piattaforma per lo sviluppo più avanti rispetto al mercato.

Cosa bolle in pentola

Ma c’è di più. Pensateci un attimo: cosa vi piacerebbe avere a disposizione sul vostro cellulare che sia gestibile attraverso la vostra “killer application”? Ecco che saltano fuori le applicazioni del futuro: micropagamenti, il digitale terrestre sul cellulare, l’RFID. Ci vorrà ancora un bel po’ per vederle all’opera, ma la direzione è segnata. Esistono già le JSR e i relativi comitati di studio, quindi c’è solo da ben sperare.Quello che invece ci sembra utile rimarcare è la tendenza del mercato a spostare l’accento verso la configuration meno limited. Già alcuni produttori di cellulari hanno annunciato l’intenzione di portare una intera serie di cellulari verso

FIGURA 3 Gerarchia delle classi del GCF

Un profile sono le API che forniscono il supporto completo per lo sviluppo di una appli-

cazione

Page 14: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 200714

speciale Java Micro Edition

la CDC e altri stanno puntando su sistemi operativi scritti interamente in Java. Di questo passo ben presto avremo non solo una piattaforma Java completa per lo sviluppo di applicazioni sul cellulare, ma qualcosa di molto vicino alla Standard Edition.Ed allora a conclusione di quest’articolo ci viene il dub-bio che la frontiera della Micro Edition in realtà non sia, semplicemente, la Standard Edition (come si poteva immaginare nei primi giorni), ma piuttosto sia la fan-tasia degli operatori di marketing del mondo wireless e mobile… E vi assicuriamo che di fantasia ne hanno da vendere!

Il Generic Connection Framework

Crediamo di non fare torto a nessuno dicendo che la più grande spinta per la diffusione dello sviluppo in Java ME, e che permette allo sviluppatore di creare in pochissimo tempo una applicazione completa, sia stato il Generic Con-nection Framework (GCF). Si tratta di un meccanismo per la creazione di connessioni che consente di astrarsi dallo specifico dell’implementazione sottostante. Il vantaggio dell’approccio per astrazioni qui raddoppia: infatti, oltre ad avere a che fare con una macchina virtuale, in Java ME si ha il notevole vantaggio di poter creare connessioni di diverso tipo verso l’esterno, utilizzando la stessa gerarchia di classi descritta nella Figura 3.

Il GCF nasce dal bisogno di avere un sostituto nella Micro Edition delle librerie java.net e java.io ivi mancanti o ridot-te. Si tratta, infatti, di una gerarchia di classi che fornisce proprio un approccio generico alla connettività. Grazie ad una gerarchia estendibile di interfacce, ad una factory di connessioni e all’utilizzo delle specifiche standard degli Uniform Resource Locator (URL), lo sviluppatore ha un unico punto di ingresso verso le connessioni: la classe Connector e l’interfaccia Connection. Il GCF supporta molte connessioni e permette di delegare all’implementazione sottostante i dettagli per la loro costruzione e tutto passa attraverso lo schema degli URL.Il formato generico di un URL è definito nelle RFC 1738 e 2396 ed è il seguente:

scheme://user:password@host:port/url-path;parameters

dove:

• scheme specifica il metodo di accesso o il protocollo (http, https,…). In GCF per ciascuno schema ci si aspetta una specializzazione dell’interfaccia Connection (HttpConnec-tion, HttpsConnection, …);

• user e password sono opzionali;• host è l’indirizzo del server sul quale è ospitata la risor-

sa;• port è opzionale e la sua interpretazione dipende dallo

schema utilizzato;• url-path è il percorso della risorsa. Il suo formato e la sua

interpretazione dipendono dallo schema e può richiede-re ulteriori parametri.

Facciamo un esempio. Se vogliamo creare una connessione http verso un certo URL basterà utilizzare la seguente riga di codice:

HttpConnection httpConn = (HttpConnection) Connector.open(“http://www.javajournal.it”);

L’implementazione del GCF residente sul cellulare inter-pretando l’URL capirà di dover creare una HttpConnection verso l’host specificato e così via per altri casi.

Per creare una connessione socket:

SocketConnection socketConn = (SocketConnection)Connector.open(“socket://myhost.com:8081”);

Per accedere ad una risorsa presente nel filesystem del cellulare:

FileConnection fileConn = (FileConnection) Connector.open(“file:///myMusic.mp3”);

Per creare una connessione utile all’invio di un SMS:

MessageConnection msgConn = (MessageConnection) Connector.open(“sms://+12345689:5000”);

Approfondimenti sul GCF

Per tutte le altre sfaccettature relative al GCF, che tra l’altro può essere utilizzato anche in ambito Java Standard Edi-tion, si rimanda a http://www.jcp.org/en/jsr/detail?id=197 e alle risorse reperibili sul sito http://developers.sun.com/techtopics/mobility/.

Note Biografiche

Edoardo Schepis è Java ME Tech Lead presso Funambol. È stato Java Architect in Sun Microsystems e ha fatto parte del grup-po di lavoro che ha prodotto la certificazione Sun Certified Mobile Application Developer (SCMAD). Edoardo è il fonda-tore e responsabile del Java Mobile Developers Forum (http://www.jmdf.org), community italiana dedicata al mondo Java e alle tecnologie wireless.

La più grande spinta per la diffusione dello sviluppo in Java Me è stato il Generic Connec-

tion Framework (GCF)

Page 15: j2007 01 jj2

JAVA Journal

15n.2 - gennaio/febbraio 2007

Java Micro Edition speciale

La diffusione di J2ME è ormai un dato di fatto

>> di Matteo Zinato ([email protected])

DSe qualche anno fa c’era grande scetticismo sulla possibile diffu-sione di J2ME, oggi si può affer-mare che la tecnologia Java per la programmazione dei cellulari si è ritagliata una discreta porzione di mercato, forse non grande quan-

to ci si aspettava, ma sicuramente incoraggiante. Gli applicativi scritti in C++ sviluppati per Symbian hanno avuto una partenza bruciante e sembravano dover superare e affossare J2ME; ma relativamente pochi cellulari si basano su Symbian e la percentua-le di diffusione del sistema operativo, anche se de-stinata a crescere, difficilmente andrà a coprire tutto il mercato dei cellulari. A tutto ciò si aggiunge una sempre maggiore necessità di sviluppare applicati-vi che siano utilizzabili anche da dispositivi mobili e sempre più spesso si vedono cellulari inseriti nei dise-gni di architetture di applicazioni enterprise.

Da applicazioni amatoriali o poco più, gli applicativi J2ME si sono quindi trasformati in componenti di sistemi client-server molto importanti e sofisticati. Se qualche anno fa ci si poteva accontentare che l’applicazione funzionasse, ora questo non basta più. Un buon programmatore questo lo sa bene, ma per altri può non essere così scontato. Un’applicazione deve ovviamente funzionare, ma deve anche essere robusta, efficiente, chiara da comprendere e facile da modificare ed estendere nelle funzionalità. Per questo motivo si sono sviluppate, come per parecchie delle tecnologie che già sono molto diffuse, delle così dette best-practice, una sorta di lista di cose da fare e cose da non fare nello sviluppo di un’applicazione. Alcune delle cose che vedremo riguarderanno la tec-nica di implementazione di alcune funzionalità, altre riguarderanno la progettazione più in generale, altre

ancora, più che delle best-practice, potrebbero essere definite delle regole di buon senso nei riguardi di quello che sarà l’utilizzatore finale della nostra ap-plicazione.

L’applicazione di esempio

Per non parlare solo a livello teorico presentiamo un’ap-plicazione di esempio, che comprende al suo interno gran parte delle possibili situazioni che si presentano ad uno sviluppatore di applicativi J2ME. L’applicazione in questione è un semplice esempio di gestione di una rubrica telefonica, in cui un utente ha la possibilità di inserire dei contatti che vengono salvati nel database locale, di effettuare delle ricerche sui contatti inseriti, di inviare i dati inseriti ad un server e di riceverli dallo stesso server. Vedremo principalmente come realizzare la parte J2ME ma anche come implementare la tecno-logia lato server che riceve e invia i dati.

J2ME Best Practice

U n’ a p p l i c a z i o n e

deve essere robusta,

efficiente, chiara da

comprendere, facile da

modificare ed estendere

nelle funzionalità

Page 16: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 200716

speciale Java Micro Edition

L’organizzazione delle classi e dei package

Una delle prime cose di cui occuparsi in un’applicazione, anche di piccole dimensioni, è l’organizzazione delle classi e la suddivisione in package. È vero che con i moderni IDE per lo sviluppo Java il refactoring delle classi e la riorga-nizzazione in package è molto semplice, ma è sempre bene partire organizzando il lavoro nel giusto modo. La scelta delle funzionalità che una classe deve compiere (in altre parole i metodi che deve avere) non è soltanto una questione di pulizia o di stile del codice, ma va anche ad influire sulle prestazioni dell’applicativo. Infatti le classi di una midlet vengono caricate in memoria solo al momento dell’utilizzo, per cui, dovremmo riuscire a raggruppare le funzionalità nelle giuste classi, in modo che se l’utente uti-lizza solo una determinata funzionalità dell’applicativo, il sistema non vada a caricare anche classi che non vengono utilizzate, se non per un metodo. Facciamo un esempio: se scriviamo in una classe tutti i metodi per l’accesso al database locale (RMS) e in un’altra classe tutti i metodi per l’accesso ai dati in remoto sul server (con connessioni http) e l’utente utilizza l’applicativo solo nelle funzionalità di salvataggio in locale, tutte le altre classi non verranno caricate, con un risparmio nel tempo di esecu-zione e soprattutto nella memoria occupata a runtime. Invece, per quanto riguarda l’organizzazione delle classi in package la strategia non influisce sulle prestazioni e sull’efficienza dell’applicativo, ma soprattutto sull’ordine delle classi all’interno del progetto (e non è poco…).

Ciò che si deve fare è distinguere e separare le classi se-guendo una sorta di paradigma MVC (Model View Con-troller) che dovrebbe risultare familiare a chi ha esperien-za di programmazione in ambiente J2EE. Nel nostro caso i package saranno così organizzati (Figura 1):

• view: contiene tutte le classi che svolgono funzionalità di visualizzazione grafica sul display;

• model: contiene tutte le classi che contengono la logica dell’applicativo che nella nostra applicazione consiste prevalentemente nell’accesso ai dati in locale o in remo-to;

• shared: contiene le classi che vanno condivise con il server;

• utils: contiene classi di utilità per la nostra applicazio-ne;

In questo modo ci accorgeremo subito, nel momento di scrivere il codice, se stiamo commettendo un errore di design dell’applicativo. Se ad esempio stiamo scrivendo una classe di view, per la visualizzazione di una form per la raccolta dei dati, ed all’interno della stessa classe ci tro-viamo a scrivere del codice per effettuare connessioni al server, dovremmo capire che stiamo sbagliando qualcosa. L’approccio corretto è scrivere il codice per la connessio-ne al server all’interno di una classe nel package model e richiamarne i metodi dalla classe di view. Può sembrare macchinoso ma chi ha esperienza di programmazione sa quanto sia importante diversificare la logica applicativa dallo strato di visualizzazione.

È importante anche prevedere una classe che faccia da controller, che centralizzi, attraverso dei semplici metodi,

FIGURA 1 L’organizzazione delle classi sul progetto J2me best Practice (J2ME) in Eclipse

LISTATO 1 Alcuni metodi della classe UIController

public void displayUserForm(){ userForm.clearForm(); display.setCurrent(userForm);}

public void displayUserForm( Contact user){ userForm.loadForm(user); display.setCurrent(userForm);} public void displayUserList(){ userList.loadUserList(“”, “”); display.setCurrent(userList);}

public void displaySearchUserList( String name, String surname){ userList.loadUserList(name, surname); display.setCurrent(userList);} public void displayUserSearch(){ display.setCurrent(userSearchForm);}

Page 17: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 2007 17

Java Micro Edition speciale

il passaggio da una view ad un’altra. In questo modo, un riferimento alla classe UIController verrà passato a tutte le istanze delle classi di view e queste invocheranno i metodi di UIController per gestire il passaggio della visualizzazione alla view successiva (Listato 1). In questo modo, abbiamo ricreato, con le dovute limitazio-ni del caso, una sorta di pattern MVC che ci aiuta a lavora-re con più ordine sulle nostre applicazioni J2ME. A questo punto non resta che entrare nel dettaglio delle classi e ve-dere caso per caso quali sono le best practice da seguire.

La classe MIDlet e il caricamento delle classi.

Come abbiamo detto, il lavoro di visualizzazione sul di-splay delle varie classi di view viene fatto dall’UIControl-ler; per cui, la classe principale che estende MIDlet, non dovrà fare altro che creare un’istanza di UIController e passare a questa ultima classe il controllo dell’applicazio-ne (Listato 2). La classe UIController, da parte sua, non deve fare altro che istanziare tutti gli oggetti di view che compongono la nostra applicazione, ma con le dovute precauzioni. Creare un’istanza di una classe significa caricarla completamente in memoria con tutti i suoi oggetti ed elementi annessi. Pensiamo, ad esempio, ad una classe di tipo Canvas che al suo interno ha immagini, testo, disegni, ecc. Istanziarla si-gnificherebbe occupare gran parte della memoria disponi-bile a run-time, ma a volte può succedere che la classe non viene nemmeno mai utilizzata, perché chi usa l’applicazio-ne va ad agire su altre classi. Nel nostro esempio, abbiamo inserito una classe di view AppInfoCanvas che carica una

serie di immagini, disegna delle forme geometriche e scri-ve del testo. In casi come questo si deve valutare l’utilizzo della lazy initialization delle classi (“lazy” letteralmente significa “pigro”, e in questo caso assume il significato di “solo quando serve”). Questa tecnica consiste nel caricare l’istanza di una classe solo quando si è sicuri che verrà utilizzata e cioè un attimo prima di caricarla sullo schermo (Listato 3 e Listato 4).

Con l’aiuto del Memory Monitor del Wireless Toolkit (che vedremo meglio più avanti) possiamo avere un riscontro immediato della differenza tra i due diversi modi di scrive-re il codice. Nel primo caso (senza utilizzare la lazy initia-lization dell’oggetto Canvas), infatti, la memoria si assesta subito sui 58.824 byte; nel secondo caso, invece, il valore di memoria occupata rimane molto più basso e sale soltanto al momento in cui la classe viene effettivamente richiesta (Figura 2 e Figura 3).

Molte altre volte, però, è più semplice caricare subito tutte le classi nel costruttore dell’UIController. Se le classi che dobbiamo utilizzare non contengono oggetti pesanti o semplicemente ci vogliamo fidare del cellulare su cui dovrà girare l’applicazione partendo dal presupposto che abbia una buona disponibilità di memoria (anche se è sempre meglio evitare questo tipo di presupposti) la lazy initializa-tion può essere evitata. In questo caso, però, è necessario predisporre un’indicazione per l’utente sullo stato di avan-zamento del caricamento. Una delle regole più importanti nel design di un’applicazione Java ME è:

• Informare sempre l’utente su quello che sta facendo l’applica-zione durante le operazioni che richiedono qualche secondo.

Ciò, banalmente, per evitare che l’utente pensi che l’ap-plicazione si sia bloccata. In questo caso è sufficiente pre-disporre un oggetto di tipo Gauge che viene aggiornato al momento della creazione delle singole istanze delle classi della nostra applicazione (Listato 5).

FIGURA 2 L’utilizzo della memoria (valore “Current”) senza utilizzare la lazy initialization sulla classe AppInfoCanvas

LISTATO 2 Pezzi di codice del MainMidlet.java

UIController uiController = null; protected void startApp() throws MIDletStateChangeException { // ... uiController = new UIController(this);}

È buona norma rag-

gruppare tutte le stringhe

di testo che caratterizza-

no le interfacce grafiche

in un’unica classe

Page 18: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 200718

speciale Java Micro Edition

Utilizzare una progress bar con un oggetto Gauge è la soluzione più semplice (Figura 4), ma nessuno vieta di realizzare interfacce graficamente più gradevoli con ogget-ti di tipo Canvas.

Internazionalizzazione delle applicazioni

Al di là del fatto che l’applicazione che dobbiamo svilup-pare debba essere tradotta o meno in altre lingue, è buona norma raggruppare tutte le stringhe di testo che caratteriz-zano le interfacce grafiche in un’unica classe. La classe in questione sarà simile a quella mostrata nel Listato 6.In questo modo, la traduzione e la distribuzione del-l’applicazione in altre lingue si ridurrebbe al semplice lavoro di modifica delle etichette contenute nel file LanguageLabel.java, evitando di dover spulciare i singoli file Java alla ricerca del testo da tradurre.

Programmare ad oggetti

Può sembrare una provocazione ma molte volte ci si di-mentica che Java è un linguaggio ad oggetti. Il concetto

dell’incapsulamento delle funzionalità viene sovente tra-scurato e ci troviamo ad avere codice ripetuto, o al limite inserito all’interno di metodi di utilità dichiarati come static. Prendiamo ad esempio la nostra applicazione per la rubrica telefonica: tutto ruota intorno al concetto di con-tatto telefonico, che nell’applicazione è rappresentato dalla classe Contact. Questa classe conterrà al suo interno tutte le proprietà che caratterizzano l’oggetto contatto e tutti i metodi per accedere alle proprietà (get e set). Ma non basta, abbiamo anche bisogno di:

FIGURA 3 L’utilizzo della memoria (valore “Current”) utilizzando la lazy initialization sulla classe AppInfoCanvas

LISTATO 3 Se non si utilizza la lazy initialization tutte le classi vengono instanziate nel costruttore dell’UIController

private LoadingForm loadingForm = null;private Menu menu = null;private ContactForm userForm = null;private ContactList userList = null;private ContactSearchForm userSearchForm = null;private ConnectionSaveForm connectionSaveForm = null;private ConnectionGetForm connectionGetForm = null;private AppInfoCanvas appInfoCanvas = null;

public UIController(MainMIDlet mainMidlet){ this.mainMidlet = mainMidlet; display = Display. getDisplay( this.mainMidlet );

loadingForm = new LoadingForm(this); display.setCurrent(loadingForm);

loadingForm. updateProgress(LanguageLabel.LOADING); menu = new Menu(this);

loadingForm.updateProgress(); userForm = new ContactForm(this);

loadingForm.updateProgress(); userList = new ContactList(this);

loadingForm.updateProgress(); userSearchForm = new ContactSearchForm(this);

loadingForm.updateProgress(); connectionSaveForm = new ConnectionSaveForm(this);

loadingForm.updateProgress(); connectionGetForm = new ConnectionGetForm(this);

displayMenu();

}

Le operazioni di

connessione ad un ser-

ver devono essere fatte

col l’ausilio dei thread

Page 19: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 2007 19

Java Micro Edition speciale

• due metodi che serializzano e deserializzano i dati del-l’oggetto (per inviarlo al server);

• due metodi che restituiscono una rappresentazione del-l’oggetto in array di byte (per il salvataggio su RMS);

• dei tipici metodi save(), delete() e update() per effettuare il salvataggio dell’oggetto in locale;

• e possiamo anche prevedere un metodo validate() che controlla se le proprietà dell’oggetto hanno dei valori congruenti.

Il relativo codice è contenuto nel Listato 7 e nel Listato 8.Una volta che la classe Contact è stata scritta come si deve, la scrittura del codice nel resto dell’applicazione diventa molto semplice e intuitiva, proprio come si richiede nella programmazione ad oggetti (Listato 9).

C’è da fare una piccola precisazione. La classe Contact in realtà estende un’altra classe, ContactBean che è all’interno del package shared. ContactBean va condivisa con il server, ossia dovrà essere presente sia sul server sia sul client J2ME? In questo modo, dal server, al momento opportuno richiameremo gli stessi metodi serialize() e deserialize() per preparare o ricevere gli stream di dati. ContactBean viene poi estesa da Contact aggiungendo i metodi di scrittura e lettura sul Record Management System. Questi metodi, infatti, non potrebbero esistere sul server, in quanto le classi del package javax.microedition.rms non esistono in un contesto J2EE, e non sarebbe possibile compilare la classe. In altre parole, al momento di inviare i dati di un contatto al server, verrà richiamato il metodo serialize() sull’istanza della classe da inviare; il server riceverà lo stream di dati serializzato e invocherà il metodo deserialize() della classe ContactBean presente nel server, ottenendo così gli stessi valori dell’istanza originale inviata dal client.

Comunicare con il server

Abbiamo appena accennato alle comunicazioni con il server. Vediamo ora nel dettaglio come gestire questo ge-nere di problematiche sia nella parte client sia nella parte server.

Utilizzare i Thread

Innanzi tutto, cominciamo col dire che le operazioni di connessione ad un server devono essere fatte col l’ausilio

dei thread. Utilizzare un Thread per gestire una connes-sione non è soltanto un buon consiglio, ma è a tutti gli effetti un obbligo. Le specifiche MIDP, infatti, dichiarano testualmente che:

FIGURA 4 La progress bar visualizzata sul terminale

LISTATO 4 Se si utilizza la lazy initialization le classi in questione vengono instanziate solo nel momento in cui ne viene richiesto l’utilizzo

public void displayCanvas(){ appInfoCanvas = new AppInfoCanvas(this); display.setCurrent(appInfoCanvas);}

public void closeCanvas(){ appInfoCanvas = null; display.setCurrent(menu);}

LISTATO 5 L’utilizzo di un oggetto Gauge per visualizzare una progress-bar che viene aggiornata dopo il caricamento di ogni classe di view

loadingForm = new LoadingForm(this);display.setCurrent(loadingForm);

loadingForm.updateProgress( LanguageLabel.LOADING);menu = new Menu(this);

loadingForm.updateProgress();userForm = new ContactForm(this);

loadingForm.updateProgress();userList = new ContactList(this);

loadingForm.updateProgress();userSearchForm = new ContactSearchForm(this);

loadingForm.updateProgress();connectionSaveForm = new ConnectionSaveForm(this);

loadingForm.updateProgress();connectionGetForm = new ConnectionGetForm(this);

Page 20: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 200720

speciale Java Micro Edition

• “ogni azione associata ad un comando dell’utente deve ritornare subito il controllo dell’applicazione per non rischiare deadlock”.

Nel momento in cui l’utente preme il pulsante per inviare dei dati al server, l’applicazione non farà altro che far par-tire un Thread separato, che effettua la connessione e in-forma l’utente dello stato di avanzamento delle operazioni; mentre il Thread principale dell’applicazione non subisce nessun tipo di blocco di attesa per lo svolgimento delle operazioni di comunicazione col server.

Visualizzare un’indicazione di avanzamento

Come abbiamo detto nella prima parte dell’articolo, “best practice” non vuol dire solamente soluzione tecniche, ma anche soluzioni che vadano a favore dell’usabilità dell’ap-plicazione da parte degli utenti. In questa logica, un fat-tore molto importante è tenere sempre informato l’utente di quello che l’applicazione sta facendo, e soprattutto

permettergli di fermare eventuali operazioni che l’applica-zione sta svolgendo (quando queste non sono immediate). Per cui, tornando alla comunicazione delle midlet con il server, è molto importante mantenere informato l’utente e dargli la possibilità di fermare il collegamento se i tempi si rivelano troppo lunghi (cosa più che normale per le con-nessioni GPRS). Vediamo come realizzare in pratica quello che si è descritto finora (Figura 5).

Siamo nel caso in cui un utente debba inviare ad un server una serie di informazioni residenti nella midlet. Dal menù dell’applicazione, l’utente sceglie la voce che corrisponde all’azione di invio dei dati al server; verrà visualizzata sul display un’istanza di una classe di tipo Form (all’interno del package view) che contiene al suo interno un oggetto di tipo Gauge, che, a sua volta, visualizzerà l’avanzamento

FIGURA 5 Menu di opzioni della midlet di collegamento con il server

FIGURA 6 Risultato dell’esecuzione del codice della classe view

FIGURA 7 Grafico di occupazione della memoria prima dell’invocazione della GC

LISTATO 6 La classe LanguageLabel contiente tutte le stringhe di testo visualizzate negli oggetti di view

public static String LOADING = “Caricamento in corso...”;public static String MENU = “Menu”;public static String MENU_NEW_USER = “Nuovo Utente”;public static String MENU_USER_LIST = “Lista degli utenti”;public static String MENU_SEARCH_USER = “Ricerca Utente”;public static String MENU_SEND_HTTP = “Invia dati al server”;public static String MENU_GET_HTTP = “Ricevi dati dal server”;public static String MENU_INFO_APP = “Info”;public static String DELETE = “Cancella Utente”;

Page 21: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 2007 21

Java Micro Edition speciale

della connessione e del trasferimento dei dati al server. Tutta la parte che gestisce la connessione sarà inserita in un’apposita classe, a sua volta inserita nel package model in modo da tenere separato il codice di view dal codice di model. La classe di view conterrà un codice del tipo riportato nel Listato 10, il cui risultato è visibile nella Figura 6.La cosa importante da tenere presente è che il metodo performConnection() deve essere esplicitamente invocato dall’esterno; per cui, al momento della selezione dell’ope-razione di invio dei dati al server dovrà essere eseguito il codice riportato nel Listato 11.

Vediamo ora come è costruita la classe di model per la con-nessione http (Listato 12). La prima cosa che si nota è che la classe estende RemoteModel ed implementa Runnable. L’implementazione dell’interfaccia Runnable è indispensa-bile per avere una classe di tipo Thread che può essere ese-

guita in modo indipendente dal resto dell’applicazione. La classe RemoteModel è una classe dell’applicazione che con-tiene al suo interno tutti i metodi alla base di una connes-sione HTTP e verrà estesa da tutte le classi che dovranno eseguire collegamenti col server (Listato 13).

Torniamo ora alla classe RemoteContactSaveList.java con-centrandoci sul codice contenuto nel metodo run() che, ricordiamo, viene invocato quando il Thread viene lanciato con start(). A parte gli aggiornamenti alla progress bar che l’utente vede a video, dopo aver aperto la connessione, ed aver aperto un outputStream per l’invio dei dati al server (utilizzando i metodi della superclasse RemoteModel) la prima cosa che viene fatta è quella di scrivere nello stream in uscita un byte. Il valore di questo byte è quello della costante MessageConstants.OPERATION_SAVE_USER_LIST. Un’operazione di questo tipo viene fatta perché in questo

LISTATO 7 I metodi inseriti nella classe ContactBean per la serializzazione e la deserializzazione

/*** popola l’oggetto ContactBean partendo * da uno DataInputStream* @param DataInputStream* @throws IOException*/public void deserialize( DataInputStream reader ) throws IOException {

name = reader.readUTF(); surname = reader.readUTF(); long dataLong = reader.readLong(); if (dataLong != 0) borndate = new Date(dataLong); telephone = reader.readUTF(); email = reader.readUTF();}

/*** restituisce un DataOutputStream * dei dati dell’utente* @param DataOutputStream* @throws IOException*/public void serialize( DataOutputStream writer ) throws IOException {

writer.writeUTF( name ); writer.writeUTF( surname ); if (borndate != null){ writer.writeLong( borndate.getTime()); } else { writer.writeLong(0); } writer.writeUTF(telephone); writer.writeUTF(email);}

/*** popola l’oggetto UserBean partendo * da un array di byte* @param data* @throws IOException*/public void fromByteArray( byte[] data ) throws IOException {

ByteArrayInputStream stream = new ByteArrayInputStream( data );

DataInputStream reader = new DataInputStream( stream );

deserialize( reader ); stream.close();}

/*** Restituisce una array di byte * dell’oggetto utente* @return* @throws IOException*/public byte[] toByteArray() throws IOException {

ByteArrayOutputStream stream = new ByteArrayOutputStream();

DataOutputStream writer = new DataOutputStream(stream);

serialize( writer ); writer.close();

return stream.toByteArray();}

Page 22: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 200722

speciale Java Micro Edition

modo possiamo gestire, lato server, un unico servizio a cui connettersi. Questo servizio, che di fatto non è altro che una servlet, come prima cosa legge quel byte dalla richiesta http e, in base al suo valore, esegue le operazioni che sono state richieste. La classe MessageConstants infatti è inserita nel package shared che, come detto, contiene le classi che devono essere presenti anche sul server (Listato 14).

Il resto del codice nella classe RemoteContactSaveList è sem-plice e leggibile. Non fa altro che scrivere sull’outputStream tutte le informazioni che devono essere inviate al server. Nel nostro caso, per prima cosa viene inserito in valore di tipo long che indica il numero di oggetti Contact che ver-ranno poi serializzati (con i metodi della classe Contact) e

inseriti a loro volta nello stream. Quando tutti i dati sono stati serializzati all’interno dell’outputStream questo viene chiuso e si attende la risposta del server, che dovrebbe inviare a sua volta un byte che indica se l’operazione è an-data a buon fine. Il lavoro di controllo sul valore di questo byte viene eseguito dal metodo openConnectionInputStream(Connection) della superclasse RemoteModel che abbiamo visto prima. Se dal server non arriva un byte con il valore MessageConstants.ERROR_NONE, viene lanciata un’eccezio-ne che la classe RemoteContactSaveList catturerà come una normale eccezione nel collegamento http. Se invece che inviare dei dati al server avessimo avuto la necessità di ricevere dei dati dal server, la logica dell’appli-cativo sarebbe stata molto simile. In questo caso le infor-mazioni da inviare al server sarebbero state circoscritte al-l’invio del byte che identifica l’operazione da eseguire (ad esempio MessageConstants.OPERATION_GET_USER_LIST). Una volta chiuso l’outputStream riceveremmo lo stream con tutte le informazioni inviate dal server, sempre precedu-te dal byte che indica l’esito (il controllo lo fa il metodo openConnectionInputStream(Connection)).

La servlet sul server

Vediamo velocemente come è realizzato il servizio che sul server riceve le richieste. Come detto si tratta di una serv-let ed il suo funzionamento è centralizzato all’interno del metodo perfom() che viene invocato in caso di richiesta di tipo POST. A questo metodo arrivano tutte le richieste fatte dal client; come prima cosa legge quel byte che gli viene in-viato come prima informazione della richiesta e esegue un metodo piuttosto che un altro, in base al valore di quest’ul-timo, che viene confrontato con i valori di MessageConstants che è una classe shared (Listato 12).

FIGURA 8 Percentuale di occupazione degli oggetti prima della Garbage Collection

Le operazioni di

connessione ad un

server devono essere

fatte col l’ausilio dei

thread

Page 23: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 2007 23

Java Micro Edition speciale

Ai metodi che possono essere eseguiti vengono passati i riferimenti ad un DataInputStream per la lettura di tutti i dati allegati alla richiesta, e un DataOutputStream per la scrittura di tutti i dati di risposta. Dopo aver terminato l’esecuzione del metodo richiesto, il metodo perform scrive come prima cosa un byte in cui indica che l’esito dell’operazione è positivo e subito dopo

appende gli eventuali dati di risposta che dal server devono passare al client.Questo metodo di gestione delle richieste al server è molto semplice da realizzare e soprattutto molto facile da man-tenere visto che tutte le richieste entrano nell’applicativo server da un’unica porta di accesso e la logica applicativa di ogni singola richiesta può essere gestita nel modo che

LISTATO 8 I metodi inseriti nella classe Contact per il salvataggio, l’aggiornamento e la cancellazione

/*** Effettua il salvataggio dell’oggetto * userBean nel db* @return esito < 0 se si sono * verificati errori > 0 altrimenti*/public synchronized int save() {

int esito = -1;

try {

// ottiene l’array di byte // dell’oggetto utente byte[] rec = this.toByteArray();

// apre il recordStore ed // effettua il salvataggio RecordStore rs = RecordStore. openRecordStore(Constants.DB_NAME, true);

esito = rs.addRecord(rec,0, rec.length);

rs.closeRecordStore();

}catch (IOException e) {

Utils.debug(“Errore nella preparazione del DataOutputStream”); esito = -1; }catch (RecordStoreFullException rsfe) { Utils.debug(“RecordStore full”); esito = -2; }catch (RecordStoreException rse) { Utils.debug(“Errore durante il salvataggio”); esito = -3; }

return esito;}

/*** Effettua l’aggiornamento * dell’oggetto userBean nel db* @return esito < 0 se si sono * verificati errori > 0 altrimenti*/public synchronized int update() {

int esito = -1;

try { //ottiene l’array di

// byte dell’oggetto utente byte[] rec = this.toByteArray();

//apre il recordStore ed // effettua il salvataggio RecordStore rs = RecordStore. openRecordStore(Constants.DB_NAME, true);

rs.setRecord(this.getRecordId(), rec,0,rec.length);

rs.closeRecordStore(); esito = 1;

}catch (IOException e) { Utils.debug(“Errore nella preparazione del DataOutputStream”); esito = -1; }catch (RecordStoreFullException rsfe) { Utils.debug(“RecordStore full”); esito = -2; }catch (RecordStoreException rse) { Utils.debug(“Errore durante il salvataggio”); esito = -3; } return esito;}

/*** Cancella uno user* @param user* @return esito < 0 se si sono * verificati errori > 0 altrimenti*/public synchronized int delete(){ int esito = -1;

try { RecordStore rs = RecordStore. openRecordStore(Constants.DB_NAME, true);

rs.deleteRecord(this.getRecordId()); rs.closeRecordStore(); esito = 1;

}catch (RecordStoreFullException rsfe) { Utils.debug(“RecordStore full”); esito = -2; }catch (RecordStoreException rse) { Utils.debug(“Errore durante il salvataggio”); esito = -3; }

return esito;}

Page 24: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 200724

speciale Java Micro Edition

si ritiene più opportuno (nell’esempio di fatto non è stata implementata nessuna logica applicativa).

Controllo della memoria

Nello sviluppo di una midlet è fondamentale avere un controllo costante sulla memoria che l’applicativo occupa durante il suo ciclo di vita. Quello della memoria occupata è un problema molto ostico soprattutto nello sviluppo di giochi, in cui il caricamento di immagini, ma soprattutto l’esecuzione in un loop continuo della logica del gioco, por-ta a facili accumuli di memoria occupata. Ciò porta, come si può immaginare, ad un blocco dell’applicazione con la tipica eccezione OutOfMemory. Evitare ciò è molto semplice utilizzando uno dei tool che viene fornito all’interno del Java Wireless Toolkit. È sufficiente abilitare il monitor sulla memoria (dal menu Edit/preferences) e al successivo avvio dell’applicazione verrà visualizzata un grafico della memoria occupata durante l’esecuzione dell’applicazione. In un’applicazione ben scritta, la linea verde della memo-ria dovrebbe mantenersi più o meno costante durante il ciclo di vita del software o comunque non seguire dei trend di crescita costante. A volte può capitare che la memoria salga senza apparenti motivi. A questo punto, è necessario capire quali oggetti occupano memoria che non dovrebbero occupare e, dopo aver individuato questi oggetti, agire di conseguenza. Molte volte è sufficiente rilasciare qualche oggetto non appena questo non è più utilizzato, ma molte volte anche questo tipo di operazione non basta o non è così semplice

da effettuare. Provate, ad esempio, ad eseguire la nostra applicazione di gestione contatti: caricate qualche con-tatto e infine visualizzate la lista. Se tenete sott’occhio la memoria, vedrete che questa aumenta sensibilmente, nonostante tutti gli oggetti vengano rilasciati. Se diamo un’occhiata alla memoria, e soprattutto esplodiamo l’al-bero degli oggetti che occupano la memoria, vedremo che gran parte dell’aumento della memoria occupata è causato da variabili di tipo char[] istanziate dalla classe com.sun.midp.rms.RecordStoreFile. A questo punto, non resta che inserire nel codice delle chiamate esplicite al garbage collector. Nel nostro caso, inserendo una chiamata a System.gc() subito dopo aver letto dal recordStore la lista de contatti, vedremo che molti degli oggetti prima occupati vengono subito libe-rati (Figura 7 e Figura 8). La chiamata esplicita al garbage collector è una pratica che deve essere usata con cautela, in quanto molto onerosa a livello computazionale per il terminale. Di norma bisogna sempre aiutare il garbage collector a liberare in modo au-tonomo gli oggetti non più utilizzati settandoli a null. Solo quando ciò non basta, si può inserire qualche chiamata esplicita al metodo System.gc().

Distribuzione dell’applicazione

Una fase molto importante nella creazione di un’applica-zione J2ME è quella che riguarda il packaging. Come sap-piamo, la distribuzione di un’applicazione avviene tramite due file, il jar (che contiene le classi e tutti i file dell’ap-plicazione) e il jad (un semplice file di testo che descrive l’applicazione). Anche se le specifiche J2ME prevedono la presenza obbligatoria di entrambi i file per l’installazione dell’applicazione, molti cellulari consentono l’installazione partendo dal solo file jar. In questo modo, oltre a bypassare tutti i controlli sull’effettiva compatibilità dell’applicazio-ne con il cellulare in questione, si perdono anche tutti gli eventuali parametri di configurazione dell’applicazione eventualmente presenti nel jad. Se, ad esempio, preve-diamo un parametro che indica l’indirizzo del server web a cui connettersi, cosa non solo legittima ma altamente consigliata, bisogna però sempre prevedere la possibile mancanza di quel parametro.Ecco il contenuto del file jad:

MIDlet-1: J2meBP, , it.wmlscript.bestpractise.MainMIDletMIDlet-Jar-Size: 42457MIDlet-Jar-URL: J2ME_BP.jarMIDlet-Name: J2ME_BPMIDlet-Permissions: javax.microedition.io.Connector.httpMIDlet-Vendor: wmlscript.itMIDlet-Version: 1.0MicroEdition-Configuration: CLDC-1.0MicroEdition-Profile: MIDP-2.0serverurl: http://localhost:8080/bpserver/RemoteContactService

Per cui all’interno della nostra applicazione avremo qual-cosa di simile a quanto riportato nel Listato 15. In questo modo l’applicazione funziona anche in mancanza del file jad con i relativi parametri.

LISTATO 9 La semplicità nell’effettuare un salvataggio di un oggetto Contact

if ( user.validate() ){

if (recordId != -1){ //effettua l’aggiornamento //di un nuovo record int esito = user.update(); if (esito < 0){ uiController.showAlert( Constants.AT_INSERT_ERROR); } uiController.showAlert( Constants.AT_INSERT_OK);

}else{ //effettua il salvataggio //di un nuovo record int esito = user.save(); if (esito < 0){ uiController.showAlert( Constants.AT_INSERT_ERROR); } uiController.showAlert( Constants.AT_INSERT_OK); }//else....

Page 25: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 2007 25

Java Micro Edition speciale

LISTATO 10 La classe ConnectionSaveForm che contiene solo istruzioni di view

public class ConnectionSaveForm extends Form implements CommandListener {

UIController uiController = null;

private StringItem stringItemStatus = null; private Command cmdStop = null; private Command cmdDone = null;

private static final int GAUGE_MAX = 5; int current = 0; Gauge gauge = null;

RemoteContactSaveList remote = null;

/** * Nel costruttore vengono istanziati: * - un oggetto di tipo gauge * per la progress bar * - un comando di ok che viene * visualizzato suolo alla fine * delle operazioni * - un comando per interrompere * le operazioni che viene * visualizzato subito * e fino a che non vengono * terminate le operazioni * * @param controller */ public ConnectionSaveForm( UIController controller) { super(LanguageLabel.WAITING);

uiController = controller;

stringItemStatus = new StringItem(“”, “”); gauge = new Gauge(“”, false, GAUGE_MAX, 0);

cmdDone = new Command( LanguageLabel.CONFIRM, Command.OK, 0);

cmdStop = new Command( LanguageLabel.UNDO, Command.BACK, 0);

addCommand(cmdStop);

append(stringItemStatus); append(gauge);

setCommandListener( this ); }

/** * viene creata un’istanza della

* classe RemoteContactSaveList * di tipo Runnable, * passandoli il riferimento a questa * classe di view in modo che il * Thread che viene lanciato possa * aggiornare la progress bar * invocando il metodo updateProgress(); */ public void performConnection(){ addCommand(cmdStop); removeCommand(cmdDone); remote = new RemoteContactSaveList(this); remote.start(); }

public void updateProgress() { current += 1; if (current > GAUGE_MAX) current = GAUGE_MAX; gauge.setValue(current++); }

/** * Viene invocato dal Thread * Gestisce la barra per * il progresso delle operazioni */ public void updateProgress(String note) {

if (note != null){ setTitle(note); stringItemStatus.setLabel(note); } updateProgress();

return; }

/** * Viene invocato dal Thread * Notifica alla classe di view il * termine delle operazioni. * Rimuove il tasto di cancel e mette * quell di ok. */ public void notifyFinish() { removeCommand(cmdStop); addCommand(cmdDone); }

public void commandAction(Command c, Displayable d) {

if (c == cmdStop){ remote.stop(); uiController.displayMenu(); } if (c == cmdDone){ uiController.displayMenu(); } }

}

Page 26: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 200726

speciale Java Micro Edition

Per quanto riguarda il file jar, è sempre consigliato effet-tuare un’operazione di obfuscation prima della sua distri-buzione. Lo scopo principale per cui si crea un obfuscated package è di nascondere il codice sorgente delle classi da operazioni di decompilazione, ma allo stesso tempo si ottengono anche dei piccoli miglioramenti nelle perfor-mance dell’applicazione. Offuscando una midlet, infatti, tutte le classi che la compongono vengono rinominate in singole lettere dell’alfabeto (ad esempio MainMidlet.class diventa a.class) e tutti i metodi e gli attributi delle classi vengono rinominati allo stesso modo (ad esempio UIController.displaySearchUserList() diventa b.a()). In questo modo, la dimensione delle classi caricate in memoria durante l’esecuzione dell’applicativo diminuisce, e di conseguenza diminuisce l’occupazione di memoria. È ovvio che si tratta di una miglioria di poco conto, ma è sempre bene tenerlo presente.

Tuning delle performance

Descriviamo, infine, alcuni consigli per effettuare il co-siddetto tuning delle performance in un’applicazione java per cellulari. Sono piccole cose, che migliorano la gestione della memoria e aumentano la velocità di esecuzione, e che potrebbero sembrare banali e inutili quando ormai si di-spone di terminali con più di 1 MB di memoria per l’esecu-zione run-time (heap-memory) e di processori nell’ordine dei 30 MHz. Non dimenticate, però, che Java è stato pen-sato per far girare applicazioni su tutti i cellulari abilitati a questa tecnologia, e che sviluppare un’applicazione che poi non funziona su gran parte dei cellulari è sicuramente una cosa da evitare. Un’applicazione dovrebbe sempre essere sviluppata basandosi sui requisiti minimi, per cui è sempre bene avere a disposizione un terminale di prima generazio-ne Java, niente di meglio di un vecchio Nokia 6310i, con pochi Kb di memoria heap. Ecco quindi una lista di cose da fare e non fare:

• Evitare di lanciare eccezioni quando non è strettamente necessa-rio.

Evitare di scrivere istruzioni del tipo:

if (validate == false)throw new Exception(“Dati non validi”)

ma scrivere piuttosto dei valori di ritorno nei metodi che indichino eventualmente l’esito delle operazioni, ad esem-pio

if (validate == false) return false;

• Usare con cura le collection.Ricordiamo che le classi Collections non sono altro che dei wrapper per gli array, per cui è preferibile utilizzare diret-tamente gli array (ad esempio new int[]) piuttosto che una collection di tipo Vector. Se decidiamo si usare Vector o HashTable è bene cercare di dimensionarli in modo corretto al momento della loro istanziazione. Se un vettore supe-ra la capacità con la quale era stato creato, viene creato un array interno più grande, vengono copiati i valori nel nuovo array e viene eliminato quello vecchio. Come ben capite, una gestione di questo tipo è piuttosto onerosa per un terminale con ridotte capacità computazionali. Anche l’utilizzo di HashTable è oneroso per il terminale, in quanto ad ogni inserimento e cancellazione di elementi viene ef-fettuato il rehashing degli elementi.

• Evitare la concatenazione di stringhe. La concatenazione di stringhe dovrebbe essere evitata in qualsiasi applicativo Java, anche in ambiente Enterprise proprio perché molto onerosa, in termini di occupazione di memoria e di complessità computazione delle operazioni; infatti, concatenando più stringhe con l’operatore + ven-gono create delle stringhe temporanee, poi copiate nell’og-getto di destinazione. Per ovviare al problema è sufficiente utilizzare un oggetto di tipo StringBuffer:

String x = “a” + “b” + “c”;

diventa:

Stringx=newStringBuffer().append(“a”).append(“b”).append(“c”).toString()

Conclusioni

Abbiamo visto come realizzare un’applicazione seguendo le più importanti “best practice” nello sviluppo di appli-cativi Java ME, dal design dell’applicazioni ai più semplici consigli per evitare problemi banali, ma a volte molto peri-colosi, con la memoria del terminale Java.

LISTATO 11

public void displaySendHttpData(){ display.setCurrent(connectionSaveForm); connectionSaveForm.performConnection();}

Nello sviluppo di

una midlet è fondamen-

tale avere un controllo

costante sulla memoria

Note Biografiche

Matteo Zinato: esperto ed appassionato di wireless, segue Java Micro Edition sin dalla sua presentazione. Lavora nel mon-do dell’IT coordinando progetti JEE e JME e nel tempo li-bero cura i contenuti del portale sul wireless programming www.wmlscript.it

Page 27: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 2007 27

Java Micro Edition speciale

LISTATO 12 La classe RemoteContactSaveList che contiene solo istruzioni di model

public class RemoteContactServlet extends Http-Servlet {

public void doGet( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

perform(request, response);}

public void doPost( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

perform(request, response);}

private void perform( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

if (request.getContentLength() > 0){

InputStream is = request.getInputStream(); DataInputStream reader = new DataInputStream(is);

OutputStream out = response.getOutputStream(); DataOutputStream writer = new DataOutputStream(out);

//due stream per i dati di ritorno ByteArrayOutputStream bufferedResult = new ByteArrayOutputStream(); DataOutputStream methodResult = new DataOutputStream(bufferedResult);

byte operation = reader.readByte(); switch (operation) {

case MessageConstants. OPERATION_SAVE_USER_LIST: saveContactList(reader, methodResult); break;

case MessageConstants. OPERATION_GET_USER_LIST: getContactList(reader, methodResult); break;

default: break; }

methodResult.flush(); byte[] bufferedBytes =

bufferedResult.toByteArray();

writer.writeByte(MessageConstants.ERROR_NONE);

//appende al DataOutputStream i byte //scritti dal metodo con i dati //da inviare al client writer.write(bufferedBytes, 0, bufferedBytes.length);

response.setContentType( “application/octet-stream”); int contentLength = writer.size(); response.setContentLength( contentLength);

}else{

//nessun dato ricevuto

}

}//perform

private void saveContactList( DataInputStream reader, DataOutputStream result) throws IOException {

long numOfRecords = reader.readLong();

for (int i=0; i<numOfRecords; i++){ ContactBean contact = new ContactBean();

contact.deserialize(reader);

//... }

return;}//saveContactList

private void getContactList( DataInputStream reader, DataOutputStream result) throws IOException {

long numItem = 10; result.writeLong(numItem); for (int i=0; i<numItem; i++){ ContactBean contact = new ContactBean(); contact.setName(“nome”+i); contact.setSurname(“cognome”+i); contact.setBorndate(new Date()); contact.setEmail(“[email protected]”); contact.serialize(result); }

}//getContactList

}//RemoteContactServlet

Page 28: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 200728

speciale Java Micro Edition

LISTATO 13

public class RemoteModel {

public static String serverURL = “http://localhost:8080/bpserver/RemoteContactService”;

/*** Apre la connessione al server * impostando user-agent e content-type* @return HttpConnection: la * connessione al server* @throws Exception*/protected HttpConnection openConnection() throws Exception {

try { HttpConnection conn = (HttpConnection) Connector.open(serverURL);

conn.setRequestProperty(“User-Agent”, System.getProperty(“microedition.profiles”)); conn.setRequestProperty(“Content-Type”, “application/octet-stream”); conn.setRequestMethod(HttpConnection.POST);

return conn; } catch (IOException ioe) {

throw ioe; } } //openConnection

/*** Apre un DataOutputStream sulla * connessione* @param connection* @return DataOutputStream* @throws IOException*/protected DataOutputStream openConnectionOutputStream( HttpConnection connection) throws IOException {

try { return connection. openDataOutputStream();

} catch (IOException ioe) { throw ioe; } }

/*** apre un DataInputStream sulla * connessione, controlla che il response * code della connessione sia * HTTP_OK (200) e come prima cosa legge* un byte che indica se le operazioni * lato server sono andate a buon fine * (MessageConstants.ERROR_NONE).* In caso negativo lancia una Exception. * @param connection* @return DataInputStream* @throws Exception*/protected DataInputStream openConnectionInputStream( HttpConnection connection)

throws Exception {

try { int responseCode = connection. getResponseCode();

if (responseCode == HttpConnection.HTTP_OK || responseCode == HttpConnection.HTTP_CREA-TED) {

DataInputStream inputStream = connection.openDataInputStream();

byte returnCode = inputStream.readByte();

switch (returnCode) {

case MessageConstants.ERROR_NONE: { return inputStream; }

default:{ throw new Exception( “Errore nel server”); } } }//if

throw new Exception(“Impossibile effettuare la connessione”);

} catch (IOException ioe) { throw ioe; } }//openConnectionInputStream

/*** Chiude la connessione e gli eventuali * stream catturando eventuali Exception* @param connection* @param outputStream* @param inputStream*/protected void closeConnection( HttpConnection connection, DataOutputStream outputStream, DataInputStream inputStream) {

if (outputStream != null) { try { outputStream.close(); } catch (IOException ioe) {} }

if (inputStream != null) { try { inputStream.close(); } catch (IOException ioe) {} }

if (connection != null) { try { connection.close(); } catch (IOException ioe) {} }

return;}

}

Page 29: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 2007 29

Java Micro Edition speciale

LISTATO 14 La classe MessageConstants che contiene i codici che identificano le operazioni da fare sul server

public final class MessageConstants {

public static final byte OPERATION_SAVE_USER_LIST = 0; public static final byte OPERATION_GET_USER_LIST = 1; public static final byte ERROR_NONE = 101; public static final byte ERROR_UNKNOWN_OPERATION = 101; public static final byte ERROR_SERVER_ERROR = 102;

}

LISTATO 15

String serverUrl = this.

getAppProperty(“serverurl”);

if (serverUrl != null &&

serverUrl.length() > 0)

RemoteModel.serverURL = serverUrl;

else

RemoteModel.serverURL =

“http://localhost:8080/bpserver/

RemoteContactService”;

Page 30: j2007 01 jj2
Page 31: j2007 01 jj2

JAVA Journal

31n.2 - gennaio/febbraio 2007

educational

Apache AntIl progetto Apache mette a disposizione una notevole quantità di software open source, in partico-lare possiamo trovare parecchie librerie e software utili (e forse indispensabili) allo sviluppo Java. Uno dei più importanti software di Apache è Ant, la formichina software che lavora per voi

>> di Michele Ferretti ([email protected])

A nt si basa sui concetti di uno strumento di sviluppo molto vecchio: Make. Make è, ed è stato per anni, uno strumento fondamen-tale per gli sviluppatori software. Make ese-

gue una sequenza di operazioni per ottenere un ri-sultato finale, che di solito è il pacchetto software da distribuire. Ant fa lo stesso, ma con maggiore elegan-za e con un framework a plug-in, che permette di ag-giungere funzionalità codificandole in classi Java.

Make

Per capire a fondo cosa è e cosa fa Apache Ant, bisogna capire quali sono le esigenze che hanno portato gli sviluppatori a crearsi strumenti per velocizzare operazioni ridondanti e noiose. Make è un software che processa dei file di testo denominati Makefile. I Makefile sono file che contengono direttive raggruppate per target. I target sono dei gruppi di comandi che permettono di compilare i sorgenti, creare archivi dei binari, ecc. Make è nato per essere utilizzato in ambiente C ma con il tempo è stato utilizzato per parecchi altri compiti.

La filosofia di Ant

Quando James Duncan Davidson cominciò a sviluppare Tomcat si trovò di fronte un sacco di sorgenti e parecchie operazioni da eseguire per compilarli. Decise quindi che era arrivato il momento di creare un nuovo strumento di sviluppo. Ant nacque proprio da questa esigenza: poter eseguire una serie di operazioni indipendentemente dal sistema operativo. Java, per definizione, è multipiattaforma e quindi richiede un tool e un formato portabile per la compilazione delle applicazioni.Ovviamente il linguaggio più adatto per creare lo strumento è Java stesso, e il formato portabile per

eccellenza è XML. A mio parere, l’uso del formato XML è la vera forza di questo progetto. La sintassi di XML è universalmente condivisa e semplice da apprendere. Nei vecchi Makefile la sintassi era particolarmente complessa. Ovunque c’è una virtual machine Java c’è la possibilità di utilizzare Ant. Make invece è più difficile da installare proprio per la sua natura C.Make è a tutti gli effetti uno standard in tutti i sistemi operativi della famiglia Unix, comprese le centinaia di distribuzioni Linux. Utilizzare Make su Windows è un dramma perché siamo costretti a crearci un ambiente particolare per eseguirlo. Ant è molto più semplice, più flessibile e portabile. Per compilare un software Java su Linux, Mac OSX o Windows si utilizza sempre lo stesso file XML.In Ant il nome predefinito per un file di build è build.xml. Se si lancia il comando “ant” in una directory, il programma cerca un file chiamato build.xml e lo processa. Se non viene specificato il target, Ant esegue quello che viene dichiarato come predefinito all’interno del progetto.

Il formato dei file build.xml

Come già accennato il formato dei file di build è XML e la sua struttura può essere riassunta in breve con questo esempio:

<project name=”ProjectName” default=”dist” basedir=”.”> <target name=”dist”> <!—Lista di task --> </target></project>

Il tag project è il contenitore generale di un file build.xml e al suo interno racchiude diversi blocchi target. Ogni target al suo interno può contenere

Page 32: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 200732

educational

diversi tag chiamati task. Project richiede un attributo “name” dove viene specificato il nome del progetto, un attributo “default” che contiene il nome del target che deve essere considerato predefinito, e l’attributo “basedir” che contiene la directory corrente quando si inizia la compilazione.Il nome del progetto è importante, perché può essere riutilizzato come variabile all’interno del build.xml per creare pacchetti per la distribuzione. Ad esempio, potreste dichiarare il nome del software come “Pippo” e poi in fase di creazione del pacchetto, aggiungere a questo nome il numero di versione e l’estensione del file. Il risultato sarà “Pippo-1.0.zip”.Il nome del progetto sarà utilizzabile in una qualunque parte del file build.xml come una sorta di variabile stringa Java. La sintassi da utilizzare per richiamare queste “variabili” è ${nomevariabile}. Nel caso del nome del progetto si utilizza ${ant.project.name}. Questa sorta di variabili si chiamano property. Ant ne definisce diverse a cui possiamo facilmente accedere. Contengono informazioni come il classpath, il nome del sistema operativo, la versione della virtual machine ecc. Ecco un esempio banale di come si può richiamare una property:

<target name=”build”> <zip destfile=”${ant.project.name}-1.0.zip” basedir=”classes” /> </target>

In questa maniera è possibile creare un archivio con il contenuto di una directory chiamata “classes”. Questo comando è disponibile in tutti i sistemi operativi ed è veramente molto potente. Il tag zip è chiamato task. I task sono delle direttive di Ant che implementano operazioni. Esiste una lista di task già disponibili in Ant, e questi rappresentano la libreria standard. Inoltre, per chi non si accontenta, è disponibile un numero notevole di task di terze parti per le funzionalità più diverse.

I target

Il cuore di ogni build.xml sono i target: tutto avviene al loro interno. Sono questi che definiscono il flusso delle operazioni, seguendo le dipendenze. Ogni target ha un nome, una lista di altri target da cui dipende,

TABELLA 1 Una lista dei più importanti task

Ant Esegue un altro build file.

AntCall Chiama l’esecuzione di un altro target.

Gzip Comprime un file con l’algoritmo Bzip o Bzip2.

Chmod Cambia i permessi di un file in sistemi Unix.

Copy Copia una file o una lista di file.

Copydir Copia una directoty.

Cvs Esegue una serie dicomandi su CVS.

Delete Elimina un file o una directory ricorsivamente.

Echo Stampa a video un messaggio.

Exec Esegue un comando di sistema o un programma esterno.

Import Importa un altro build file in quello corrente.

Input Permette l’iterazione con l’utente durante la fase di build.

Jar Crea un archivio Jar.

Java Esegue codice Java.

Javac Compila sorgenti Java.

Mail Invia messaggi di posta elettronica.

Mkdir Crea una directory.

Move Sposta un file.

Tar Crea un archivio Tar.

Unzip Decomprime un archivio Zip.

Xslt Processa uno o più file XSLT.

Zip Crea un archio Zip.

Page 33: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 2007 33

educational

una descrizione e due attributi “if” e “unless” che ne determinano l’esecuzione. Un esempio di target completo è il seguente:

<target name=”compile” depends=”init,setup” description=”descrizione” if=”a” unless=”b”></target>

Questo target si chiama “compile”, dipende dall’esecuzione di altri due target “init” e “setup” (nella seguenza indicata nell’XML) e sarà eseguito se la variabile “a” sarà settata e se la variabile “b” non sarà settata. Pensate quindi ad un target specifico, che deve essere eseguito solamente in un determinato caso; potete valorizzare una variabile che rappresenta lo stato booleano di quel caso. Nel caso in cui quella particolare variabile sarà valorizzata, i task presenti all’interno del target saranno eseguiti.Raramente ho utilizzato gli attributi if e unless. Anche perché è possibile richiamare esplicitamente l’esecuzione di un altro target, e di conseguenza condizionarlo. Solamente il nome è obbligatorio, gli altri attributi possono essere tralasciati, oppure inseriti in varie combinazioni in base alle caratteristiche che dovrà avere il target. Per esempio, if e unless compaiono di rado insieme.La pratica comune è quella di creare innanzitutto target che racchiudono comandi destinati ad uno scopo preciso. Il passo successivo è inserire le dipendenze fra questi target, in modo che l’esecuzione di un target causi a cascata l’esecuzione di tutti gli altri. Un probabile target “init” potrebbe contenere una lista di task “mkdir”: questi creano la struttura del file system di partenza per la compilazione e la creazione del pacchetto software da distribuire. Esempio:

<target name=”init”> <mkdir dir=”build”/> <mkdir dir=”dist”/></target>

Nella directory “build” andranno a finire i file compilati e nella directory “dist” verrano spostati i file binari e gli altri file che andranno a far parte del pacchetto finale da distribuire. Più avanti presenterò un file di build da utilizzare per un progetto software Java.

La libreria standard dei task

La libreria standard dei task di Ant è molto vasta e comprende strumenti basilari per la manipolazione di codice Java ma anche del file system e per altri migliaia di scopi. Non si possono elencare qui tutti i task, pertanto nella Tabella 1 è riportata una lista dei più importanti con a fianco una breve descrizione.Con i task racchiusi in questa piccola lista è possibile fare già parecchie cose. Nella maggior parte dei casi infatti, ci si limita a creare directory, compilare, spostare e archiviare. Quasi tutto il lavoro si basa su operazioni specifiche su file system. Ovviamente è interessante inserire task

che ci permettono di caricare in automatico l’ultima versione del software su un server FTP e magari segnalare automaticamente via mail della sua creazione.Per queste funzionalità esistono già dei task, ma se proprio non vi accontentate potete crearvi un task personalizzato. L’architettura di Ant è flessibile e permette l’introduzione di altri task personalizzati. Per creare un proprio task basta estendere la classe Task di Ant. Comunque reputo remota la necessità di dover scrivere un proprio task.Fra i più importanti task di terze parti ricordo: JUnit, FTP e MS Visual SourceSafe. Alcune di questi sono, ovviamente, utilizzabili solo su piattaforme specifiche. Ad esempio, il task per lavorare con SourceSafe non può essere utilizzato su Linux proprio perché il software di Microsoft non è disponibile per questa piattaforma.

Ant nelle applicazioni Java

Ant è nato dall’esigenza di sviluppatori Java; infatti i primi target a nascere sono stati:

• Javac• Java• Javadoc• Jar

Questi infatti sono presenti fin dagli albori del progetto. Ant è un ottimo strumento di sviluppo per un progetto Java e può essere utilizzato per eseguire le varie fasi. Le fasi standard di un progetto Java, possono essere riassunte in:

1. Inizializzazione dell’ambiente (init)2. Compilazione dei sorgenti (compile)3. Creazione dell’archivio jar (build)4. Generazione della documentazione Javadoc

(doc)5. Creazione del pacchetto per la distribuzione

(dist)6. Rimozione dei file generati (clean)

La generazione della documentazione non è necessaria ma in un progetto che si rispetti lo sviluppatore rilascia sempre la documentazione autogenerata da Javadoc. Ad ogni voce di questo elenco va associato un target specifico. Prendiamo come esempio la preparazione di un pacchetto di rilascio per una classe HelloWord.java. All’interno di questi target farò uso di proprietà che inizializzerò con visibilità globale nel progetto.

Inizializzazione dell’ambiente

Preparare un ambiente per il rilascio di un pacchetto, significa creare le directory che conterranno i file compilati e il resto dei file che andranno a far parte della distribuzione. Questo target lo chiameremo “init” e non avrà dipendenze con altri target. All’interno di questo target metteremo i task che creeranno le directory.

<!-- Inizializzazione --><target name=”init”> <mkdir dir=”${build}”/>

Page 34: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 200734

educational

<mkdir dir=”${build}/classes”/> <mkdir dir=”${dist}”/> <mkdir dir=”${doc}”/></target>

Compilazione dei sorgenti

La compilazione dei sorgenti Java è una pratica fondamentale e si basa tutta sul task Javac, che riproduce le funzionalità del programma javac, distribuito con il JDK. Questo task permette di compilare le classi Java partendo da una directory base, dove sono presenti i sorgenti. Si possono specificare i package e molti altri parametri, come ad esempio il target della JVM.Questo target dipende dall’esecuzione del target precedente. Al suo interno è dichiarato il task Javac con i parametri “srcdir” e “destdir” che contengono, rispettivamente, la directory dei sorgenti e la directory di destinazione delle classi compilate.

<!-- Compilazione dei sorgenti --><target name=”compile” depends=”init”> <javac

srcdir=”${src}” destdir=”${build}/classes” /></target>

Creazione dell’archivio jar

A questo punto, dobbiamo creare l’archivio jar della classe. Come per la compilazione, la creazione di un archivio jar si basa su un unico task che accetta diversi parametri di configurazione. Si può creare un archivio partendo dal contenuto di una intera directory, oppure si possono specificare i file, utilizzando anche caratteri wildcard. Nel caso specifico, creiamo l’archivio dal contenuto dell’intera directory “classes” presente all’interno della directory “build”.

<!-- Creazione dell’archivio jar --><target name=”build” depends=”compile”> <jar destfile=”${ant.project.name}.jar” basedir=”${build}/classes”/></target>

LISTATO 1 Scheletro di un file build.xml

<project name=”HelloWord” default=”dist” base-dir=”.”>

<!-- proprieta’ utilizzate all’interno dei target --> <property name=”src” value=”src” /> <property name=”build” value=”build” /> <property name=”dist” value=”dist” /> <property name=”doc” value=”doc” /> <property name=”version” value=”1.0” /> <!-- Inizializzazione dell’ambiente --> <target name=”init”> <mkdir dir=”${build}”/> <mkdir dir=”${build}/classes”/> <mkdir dir=”${dist}”/> <mkdir dir=”${doc}”/> </target> <!-- Compilazione dei sorgenti --> <target name=”compile” depends=”init”> <javac srcdir=”${src}” destdir=”${build}/classes” /> </target>

<!-- Creazione dell’archivio jar --> <target name=”build” depends=”compile”> <jar destfile=”${ant.project.name}.jar” basedir=”${build}/classes”/> </target>

<!-- Generazione della documentazione ja-

vadoc --> <target name=”doc”> <javadoc packagenames=”com.*” sourcepath=”${src}” destdir=”${doc}” author=”true” version=”true” /> </target> <!-- Creazione del pacchetto per la di-stribuzione --> <target name=”dist” depends=”build,doc”> <zip destfile=”${dist}/${ant.project.name}-${version}.zip”> <fileset dir=”.” includes=”${ant.project.name}.jar” /> <fileset dir=”.” includes=”${doc}/**/*” /> </zip> </target> <!-- Rimozione dei file e directory gene-rati --> <target name=”clean”> <delete dir=”${build}”/> <delete dir=”${dist}”/> <delete dir=”${doc}”/> <delete file=”${ant.project.name}.jar”/> </target> </project>

Page 35: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 2007 35

educational

la compressione, la lista dei file da includere ecc.La sua esecuzione dipende da quella del target “build” e poi del target “doc”.

<!-- Creazione del pacchetto per la distribuzione --><target name=”dist” depends=”build,doc”> <zip destfile=”${dist}/${ant.project.name}-${version}.zip”> <fileset dir=”.” includes=”${ant.project.name}.jar” /> <fileset dir=”.” includes=”${doc}/**/*” /> </zip></target>

Rimozione dei file e directory generati

Questo target non fa parte di una procedura di build ma definisce tutte le operazioni necessarie affinché l’ambiente ritorni nello stato iniziale. Ciò garantisce che in una nuova procedura di build non si trovi “sporcizia” lasciata dalla precedente. Questo target si chiama “clean” e va lanciato prima di rieseguire nuovamente il file di build.

<!-- Rimozione dei file e directory generati --><target name=”clean”> <delete dir=”${build}”/>

Generazione della documentazione javadoc

In Java possiamo documentare il codice mentre lo scriviamo. In un secondo tempo, con lo strumento javadoc, è possibile generare la documentazione in formato HTML delle classi. In questo target, chiamato “doc” si genera la documentazione per la classe HelloWord e per tutte quelle classi che appartengono ad un package che inizia per “com”.

<!-- Generazione della documentazione javadoc --><target name=”doc”> <javadoc packagenames=”com.*” sourcepath=”${src}” destdir=»${doc}» author=»true» version=»true»/></target>

Creazione del pacchetto per la distribuzione

Il pacchetto per la distribuzione sarà un file zip contenente l’archivio jar appena creato e la directory con la documentazione. Il task per compiere questa operazione è zip e, come gli altri, ha una serie di parametri per definire

LISTATO 2 Targets che genera un archivio zip

# antBuildfile: build.xml

init: [mkdir] Created dir: /Users/michele/Desktop/Articolo Ant/esempi/build [mkdir] Created dir: /Users/michele/Desktop/Articolo Ant/esempi/build/classes [mkdir] Created dir: /Users/michele/Desktop/Articolo Ant/esempi/dist [mkdir] Created dir: /Users/michele/Desktop/Articolo Ant/esempi/doc

compile: [javac] Compiling 1 source file to /Users/michele/Desktop/Articolo Ant/esempi/build/classes

build: [jar] Building jar: /Users/michele/Desktop/Articolo Ant/esempi/HelloWord.jar

doc: [javadoc] Generating Javadoc [javadoc] Javadoc execution [javadoc] Loading source files for package com.acme... [javadoc] Constructing Javadoc information... [javadoc] Standard Doclet version 1.4.2_09 [javadoc] Building tree for all the packages and classes... [javadoc] Building index for all the packages and classes... [javadoc] Building index for all classes...

dist: [zip] Building zip: /Users/michele/Desktop/Articolo Ant/esempi/dist/HelloWord-1.0.zip

BUILD SUCCESSFULTotal time: 4 seconds

Page 36: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 200736

educational

Note Biografiche

Michele Ferretti è un giovane programmatore che si occupa di sviluppo Java per lavoro e nel tempo libero si diletta con Python ed ultimamente con il mondo Ruby e Ruby On Rails

<delete dir=”${dist}”/> <delete dir=”${doc}”/> <delete file=”${ant.project.name}.jar”/> </target>

Assemblando tutti questi target assieme, otteniamo il file di build definitivo, riprodotto nel Listato 1. Il primo target ad essere chiamato sarà dist e il flusso di esecuzione sarà: init, compile, build, doc, dist. In cima, prima della dichiarazione del target init, sono presenti cinque dichiarazioni di property:

<!-- proprieta’ utilizzate all’interno dei target --><property name=”src” value=”src” /><property name=”build” value=”build” /><property name=”dist” value=”dist” /><property name=”doc” value=”doc” /><property name=”version” value=”1.0” />

Si tratta di quattro directory e della stringa che identifica la versione del software. In genere, qui si inseriscono anche eventuali informazioni su: autore, data di creazione, copyright, ecc. Vediamo ora se il file appena creato funziona. Per convenzione lo rinominiamo “build.xml” e dal prompt di comando digitiamo il relativo comando. Nel Listato 2 è mostrato un esempio di compilazione.L’output di Ant permette di capire se è andato tutto bene o se ci sono stati dei problemi, ed eventualmente la causa di questi problemi. La maggior parte dei task di Ant, se va in errore, blocca l’esecuzione dell’intero file. Questa caratteristica di Ant è molto importante, perché è inutile continuare nella compilazione di una classe se non si è riusciti a creare la sua directory di destinazione.

Se abbiamo bisogno di ricreare di nuovo il pacchetto, perché nel frattempo abbiamo aggiunto un metodo alla nostra classe, prima dobbiamo ripulire l’ambiente. Il target “clean” è stato creato proprio a questo scopo. Per eseguirlo digitiamo:

# ant clean

e il risultato sarà la cancellazione di tutti file

In generale se volete richiamare l’esecuzione di un target particolare, potete farlo specificando il suo nome come primo dei parametri del comando ant. Quindi se volete solo rigenerare la documentazione potete usare:

# ant doc

Ant per usi diversi da Java

Apache Ant è un progetto nato per uno scopo preciso: la compilazione dei programmi in Java. Con gli anni però, ci si è resi conto che risulta adatto ad eseguire un’enorme quantità d’operazioni. Ci sono persone che lo utilizzano per creare siti Internet oppure per sostituire operazioni ridondanti che prima descrivevano in file batch. Personalmente, lo utilizzo per creare il pacchetto

di distribuzione di una estensione che ho realizzato per Mozilla Firefox.Per esempio, volendo creare le pagine HTML di un sito Internet si potrebbe partire dal contenuto di un file XML, applicare un file XSLT a questo XML e avere in uscita uno o più file HTML. Sempre con Ant sarebbe possibile caricare, via FTP, quell’HTML sul server del provider del nostro servizio di hosting. Il task XSLT potrebbe essere usato anche per generare versioni distribuibili di documenti scritti con DocBook.

Conclusione

Ant è uno strumento di lavoro estremamente utile e completo. Viene utilizzato in ambito open source nei progetti della fondazione Apache e in moltissimi progetti commerciali. Alcune delle aziende più influenti nel panorama Java, come Bea, rilasciano task per l’integrazione con i loro prodotti. Viene supportato da quasi tutti gli IDE Java in circolazione. Ne cito solamente alcuni: JBuilder, Eclipse e NetBeans. Non vorrei sbagliarmi, ma credo che, NetBeans lo utilizzi come motore per eseguire azioni all’interno dell’ambiente di sviluppo. Alcuni di questi IDE sono provvisti di wizard grafici per la creazione di file Ant.Non si tratta di un progetto Java sconosciuto ma di uno standard de facto apprezzato e riconosciuto da tutti gli sviluppatori Java. Non a caso sono nati alcuni progetti “cloni” come Nant (http://nant.sourceforge.net/) che cercano di riprodurre le funzionalità per ambienti diversi da Java, come .NET.

Bibliografia

[1]_Home page del progetto Apache Ant (http://ant.apache.org/)[2]_Manuale ufficiale di Ant (http://ant.apache.org/manual/index.html)[3]_Alcuni tutorial su Ant (http://blog.ideoplex.com/software/java/#ant)[4]_Wiki_su_Ant_(http:/ /en.wikibooks.org/wiki/Programming:Apache_Ant)

Ant nacque proprio

da questa esigenza:

quella di poter eseguire

una serie di operazioni

indipendentemente dal

sistema operativo

Page 37: j2007 01 jj2
Page 38: j2007 01 jj2

JAVA Journal

38 n.2 - gennaio/febbraio 2007

educational

Java 5, le buone nuove e le novità meno buoneseconda parte

>> di Ugo Landini ([email protected])

C ontinuiamo la nostra carrellata sulle novi-tà di Java5 (linguaggio e piattaforma). Nel-la prima parte abbiamo trattato i Generics, l’enhanced for, l’autoboxing, gli static im-

port, la covarianza e il class sharing. In questa secon-da parte vedremo le concurrent utilities, i typesafe enums, i varargs e le annotations, ed in più vedremo cosa bolle in pentola per Java6. Per tutti coloro che avessero perso la prima parte, riportiamo la tabella (riassuntiva e molto ad alto livello) dei più importanti cambiamenti introdotti in Java5 (Tabella 1).

Varargs

Come dice il nome stesso, con Java5 si può ora dichia-rare un metodo con numero variabile di argomenti, cosa che farà felice qualche programmatore C e farà probabilmente inorridire i puristi della programma-zione ad oggetti. Questa caratteristica apre anche la porta alla printf, nel più puro stile C. Sia come sia, è una feature che può sicuramente tornare utile. Un costruttore con questa signature (notare l’ellipsis, ossia i 3 puntini)

public Phone (String manufacturer, String name, String... features)

può essere invocato con:

new Phone(“Nokia”, “6310i”);new Phone(“Nokia”, “6310i”, “Bluetooth”);new Phone(“Nokia”, “6310i”, “Bluetooth”, “MIDP 1.0”);

e così via. Di fatto il compilatore sostituisce l’ellipsis con un array (di String in questo caso, ma potrebbe ovviamente essere un array di Object o di quello che volete), ma vi permette anche di non passare nessun parametro, cosa molto comoda. Iterare sulla lista di argomenti è quanto di più semplice si possa pensare, ovviamente usando il nuovo ciclo for:

for (String feature: features) { System.out.println(feature);}

Essendo di fatto un array, potete ovviamente memo-rizzare i varargs tramite un semplice assegnamento:

String[] myFeatures = features;

o anche, perché no, passare un array come parame-tro:

new Phone(“Nokia”, “6310i”, new String[]{“Bluetooth”, “MIDP 1.0”});

Attenzione però alle conversioni! In qualche (raro) caso, potrebbero interferire con le vostre vere inten-zioni. Supponiamo di voler stampare l’indirizzo di un array di oggetti, magari per fare debugging, con questo codice:

Object[] array = new String[] {“qualche”, “stringa”, “a caso”}out.printf(“ Indirizzo dell’array di oggetti: <%s> “, array);

Essendo il secondo parametro di printf in realtà un va-rarg, verrà passato il primo parametro dell’array a %s,

Page 39: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 2007 39

educational

e non l’indirizzo come vi aspettereste. Per forzare la mano a java, bisogna indicare esplicitamente le nostre intenzioni:

out.printf(“ Indirizzo dell’array di oggetti: <%s> “, (Object)array);

Typesafe Enums (o Enumerated Types)

I typesafe enum sono un caso di idioma entrato diretta-mente in una nuova release del linguaggio: qui è necessa-ria un po’ di storia per capire meglio.La keyword enum del C era stata volontariamente tenu-ta fuori dal linguaggio Java, poiché non era altro che un modo per dare un nome ad un set di costanti intere, senza nessun concetto di type safety. In java un idioma (idioma = pattern a livello di linguaggio) standard era quello di dichiarare delle costanti direttamente nelle classi o in un’interfaccia, ma in fondo non era molto meglio del-l’equivalente enum in C.

// in Ctypedef enum {CLUBS, DIAMONDS, HEARTS, SPADES} suit;// in Java: NON USAREpublic class Card { public static final int SUIT_CLUBS = 0; public static final int SUIT_DIAMONDS = 1; public static final int SUIT_HEARTS= 2; public static final int SUIT_SPADES= 3;}

Joshua Bloch propose allora una soluzione più o meno standard a questo problema, soluzione che potete trovare nel suo eccellente libro Effective Java [BLOCH], e chiamò questo idioma “type safe enum”

// idioma typesafe enum: ottimo fino al JDK 1.4public class Suit { public static final Suit CLUBS = new Suit(“clubs”); public static final Suit DIAMONDS = new Suit(“diamonds”); public static final Suit HEARTS = new Suit(“hearts”); public static final Suit SPADES = new Suit(“spades”); public String toString() {return name;} private Suit(String name) {this.name = name;} private final String name; }

Questo idioma permette ovviamente una maggiore type safety: non c’è modo di usare oggetti di tipo Suit che non siano quelli elencati come static all’interno della classe stessa: la classe non espone costruttori e non può quindi neanche essere estesa (se non sapete perché, vi consiglio di ripassare i costruttori!), per cui l’idioma è sufficientemente robusto. Esistono diverse varianti ed estensioni di questo idioma, per aggiungere feature come la serializzabilità, l’estendi-bilità, o per gestire tipi ordinali (si noti che nella forma mostrata utilizza delle stringhe), che complicano legger-mente il tutto. Con Java5 si è deciso dunque di inserire questo idioma direttamente all’interno del linguaggio, in

TABELLA 1 Sintesi delle novità introdotte in Java5

Cambiamento Costrutto/Tool Scopo

Linguaggio Generics• Semplificazione della gestione delle Collections• Sicurezza del codice

Linguaggio Nuovo ciclo for • Semplificazione della gestione delle Collections

Linguaggio Autoboxing • Semplificazione del codice

Linguaggio Enumeration• Gestione standardizzata di insiemi di oggetti• Sicurezza codice

Linguaggio Varargs • Soprattutto semplificazione nel porting di codice C

Linguaggio Static import • Migliorare la leggibilità del codice

Linguaggio Covarianza • Più espressività, migliore modellabilità

API printf • Soprattutto semplificazione nel porting di codice C

API java.util.scanner • Semplificare l’IO

API xml bundling• Maggiore integrazione con XML• Supporto web services integrato in Java

API Concurrent API• Semplificare la creazione di codice multithreaded• Aumentare le performance del codice multithreaded

Sistema Class sharing• Migliorare lo startup della virtual machine• Diminuire il footprint

Sistema Troubleshooting • Semplificare il troubleshooting di applicazioni java attraverso l’inserimento nella distribuzione Java di nuovi tool e/o tool che precedentemente dovevano essere scaricati separatamente

Page 40: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 200740

educational

modo da semplificarne un po’ il suo utilizzo, inserendo la parola chiave enum:

enum Suit {CLUBS, DIAMONDS, HEARTS, SPADES}

che si usa esattamente come il “vecchio” idioma di Bloch.

Suit current = Suit.DIAMONDS;if (current == Suit.HEARTS) ...

Andando più a fondo, una enum non è altro che una classe un po’ speciale:

• usa la keyword enum al posto di class• come prima cosa deve dichiarare tutte le sue costanti,

prima di qualsiasi attributo o metodo aggiuntivo• è implicitamente final, ma non può essere dichiarata

final (poiché non può essere direttamente estesa, ma le costanti sono di fatto delle classi anonime che la esten-dono!)

• non può estendere da altre classi perché implicita-mente estende java.lang.Enum e di conseguenza

o ha un toString() di default (che può ovviamente essere overridden) che ritorna il nome della costante

o ha un metodo name() che ritorna il nome della costan-te, e non può essere overridden

o è implicitamente Comparable e implementa corretta-mente hashCode e equals

o è impossibile da clonare, clone() è final e inoltre ritorna l’eccezione CloneNotSupportedException

o ha un metodo ordinal() che ritorna il numero ordinale corrispondente alla costante

• è implicitamente Serializable• ha due metodi statici automaticamente generati dal

compilatore:

o public static E[] values() che torna la lista delle co-stanti

o public static E valueOf(String name) che ritorna la costante associata al nome

• non può fare l’override di finalize• non può essere dichiarata abstract, ma può avere metodi

abstract (questa è lunga da spiegare!)• le costanti possono essere usate in uno switch, a diffe-

renza dei normali oggetti

Dopo tutte queste piccole differenze, può dichiarare me-todi e membri come una qualsiasi altra classe. Il codice generato per una enum è molto simile a quello dell’idioma type safe enum di Bloch, ed usa ampiamente i generics.A mio modo di vedere le Enum sono un ottimo costrutto se usate in maniera molto semplice, come nell’esempio riportato: se si comincia ad usarle aggiungendo comporta-mento alle costanti e cose simili si ottiene solo un sostituto “povero” per la normale programmazione ad oggetti, con molte più limitazioni (fra l’altro spesso poco intuitive). Anche qui il consiglio è: prendete le enum, ma a piccole dosi. Se vi trovate a complicarvi la vita, forse è meglio pen-

sare a soluzioni alternative con “normali” classi. Se avete invece bisogno di una lista di costanti e volete essere sicuri che il compilatore controlli su eventuali abusi, enum è la soluzione perfetta.

Concurrent Utilities

Le concurrent utilities sono, a mio modo di vedere, la no-vità in assoluto più importante di Java5. Ma solo chi ha bi-sogno di scrivere codice multithreaded ne può apprezzare la portata, molto spesso passa inosservata.Anche qui è d’obbligo un po’ di storia. Java, da sempre, ha avuto un ottimo supporto alla programmazione concorren-te, tramite le feature implicite in Object (wait() e notify()/no-tifyAll()), le keyword synchronized e volatile e la classe Thread (e le altre classi/interfacce correlate).

Come è risaputo, questi meccanismi base permettono – per chi li conosce e li sa usare correttamente, cosa più facile a dirsi che a farsi – di gestire la maggior parte delle situazio-ni. Il problema è proprio che questi meccanismi, seppur sufficienti, sono ad un livello di astrazione molto basso. Creare una classe thread-safe può essere molto più difficile di quello che può sembrare, e creare un’applicazione con-corrente senza deadlock e che sfrutti al massimo le risorse disponibili è invece appannaggio di pochi.Per questo Doug Lea, professore specializzato nella pro-grammazione concorrente, cominciò nel 1995 a mettere insieme una libreria avanzata di componenti ad alto livello di astrazione sulla sua homepage [LEA]. La libreria ebbe un notevole successo e diverse revisioni, fino alla pubblica-zione nel 1999 del libro “Concurrent programming in Java” [CONCURRENT], best seller che - sfortunatamente - non è ancora stato aggiornato. Le idee di Doug Lea trovarono poi spazio nel JSR 166, che ebbe proprio il compito di inserire le librerie ed i costrutti avanzati direttamente all’interno di Java. Lavorando con il supporto dell’engineering, l’expert group del JSR 166, guidato da Doug Lea, modificò addirit-tura il memory model di Java e la Vm stessa per sfruttare al meglio alcune idee. Il frutto di questi sforzi congiunti si trova ora soprattutto nel package java.util.concurrent, è perfettamente integrato con tutte le altre librerie ed è a disposizione di tutti.Di fatto, non esiste più ragione per utilizzare i “vecchi” meccanismi di sincronizzazione. Le nuove concurrent API permettono prestazioni spesso superiori, sono più facili da usare, e risolvono in maniera diretta diversi problemi noti

I vararg sono di fatto

un array: si possono trat-

tare in modo ordinario, ma

attenti alle conversioni

Page 41: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 2007 41

educational

nascondendo la complessità necessaria per risolverli. A questo punto molti nostalgici sosterranno che in fondo le concurrent Api non fanno altro che usare correttamente il supporto multithreaded “base” di Java, ma sarebbe come dire che in fondo le JVM sono scritte in C, che a sua volta è compilato in linguaggio macchina! Eppure le differenze fra lo scrivere un middleware in Java ed uno in linguaggio macchina sono abbastanza evidenti...Inoltre, la maggior parte del codice nel package java.util.concurrent non utilizza affatto synchronized: le nuo-ve VM, proprio in seguito agli sforzi del JSR166, espongo-no ora primitive hardware di compare and swap o load-linked/store-conditional (CAS su Sparc/Intel, LL/SC su PowerPC) che permettono ad alcune classi di utilizzare complessi algoritmi lock-free, ossia capaci di proteggere risorse senza utilizzare i classici mutex. È inutile dire che questi algorit-mi permettono di realizzare codice decisamente più perfor-mante e scalabile di prima.Considerate poi che uno degli obiettivi dichiarati del JSR 166 era quello di realizzare per la programmazione multi-thread quanto le Collection API di Java 2 avevano realizzato per le strutture dati, e che inizialmente si era addirittura pensato a modificare e/o inserire parole chiave nel linguag-gio, ipotesi poi scartata.Dopo questa lunga introduzione, facciamo una breve panoramica di quanto possibile con le nuove concurrent utilities: una classe Lock ad alte performance (un lock, ma implementato in lock-free!) per sostituire la parola chiave synchronized. Fra le varie implementazioni c’è anche un ReadWriteLock bello e pronto, che permette di gestire efficientemente i casi in cui sono possibilità di letture simultanee ma scritture esclusive. In generale i nuovi lock si utilizzano in un modo leggermente più complesso perché costringono ad un finally per il rila-scio:

Lock l = ...; l.lock();try { // access the resource protected by this lock} finally { l.unlock();}

Ma sono infinitamente più flessibili: si possono acquisire in maniera non-blocking con tryLock(), in maniera “inter-ruttibile” con lockInterruptibly() o anche con un timeout con tryLock(long, TimeUnit).

• una classe Condition per rappresentare diversi set di con-dizioni d’attesa, che sostituisce il meccanismo di wait(), notify() e notifyAll().

• variabili Atomiche, ossia variabili per cui è garantito l’assegnamento in una unità di tempo indivisibile, senza l’ausilio di lock. Possono essere usate per costruirsi i pro-pri algoritmi lock-free

• granularità a livello di nanosecondi (per i Sistemi operati-vi che ne sono capaci!), tramite la nuova classe TimeUnit.

• un Executor framework per il dispatching di task, com-prensivo di un Thread pool e di un servizio di schedu-ling

• Utilità generiche di sincronizzazione ad alto livello

o Semaphoreo CountDownLatcho CyclicBarriero Exchanger

• Nuove Collection thread-safe, in generale lock-free

o ConcurrentHashMapo CopyOnWriteArrayListo le interfacce Queue e BlockingQueue, ed alcune imple-

mentazioni: ArrayBlockingQueue, PriorityBlockingQueue, ConcurrentLinkedQueue, ecc.

La classe ConcurrentHashMap, per esempio, è molto diver-sa da una normale HashMap sincronizzata, che si ottiene utilizzando il metodo Collections.synchronizedMap(), ed è ottimizzata per la scalabilità e le performance: può infatti gestire un numero infinito di letture contemporanee, e fino a sedici scritture contemporanee, nella maggioranza dei casi in maniera lock-free.Se vi dovete occupare della scrittura di codice server ad alte prestazioni, le concurrent utilities sono sicuramente il punto da cui partire! Se dovete semplicemente costruire codice thread safe, vi potete accontentare delle nuove Lock/Condition e magari dell’Executor.In generale, è possibile (ed auspicabile) mandare in pen-sione i vecchi sistemi e scrivere codice più performante e meno soggetto ad errori con queste nuove API.

Annotations

Le annotation sono una di quelle novità di cui inspiega-bilmente si è parlato moltissimo, ma che in realtà hanno una portata molto più limitata: tipicamente sono molto importanti per i produttori di tool e non certo per i comuni sviluppatori.È comunque importante sapere perlomeno di cosa si tratta, poiché come vedremo è molto facile incontrare del codice annotato, ed inoltre l’argomento è di per sé molto affascinante.

I typesafe enum

sono un idioma che

permette una maggiore

type safety

Page 42: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 200742

educational

che indica che il metodo è deprecato, esattamente come il tag @deprecated di javadoc. La differenza è che l’override di questo metodo genererà un warning a livello di compila-zione, ed un warning più dettagliato sul particolare meto-do usando questa sintassi:

javac -Xlint:deprecation *.java

Dichiarare un metodo @Override farà invece in modo che il compilatore controlli effettivamente che il metodo effettui l’override di qualche altro metodo, ed è utile per control-lare i classici errori di battitura come tostring(), equal(), o hashcode() che spesso portano via prezioso tempo allo sviluppatore.Anche creare un nuovo Annotation Type è semplicissimo, tramite il costrutto @interface:

public @interface TODO { String nota();}

per cui si potrà usare l’annotazione in questo modo, pas-sando una stringa alla proprietà nota

@TODO(nota=”Questo metodo va riscritto, è troppo complesso”)public void faiQualcosa() { //... }

Per concludere velocemente questa carrellata: si possono creare annotazioni con più di un parametro, si può omet-tere il nome della proprietà se questa si chiama value, si possono usare dei valori di default per i parametri e si pos-sono anche annotare le annotazioni (meta-metadati) per definirne meglio il comportamento.A runtime, si può poi interrogare un class file ed ot-tenere informazioni sui metadati e sui valori delle proprietà associate tramite nuovi metodi delle reflection API. (vedere l’interfaccia java.lang.reflect.AnnotatedElement per i dettagli). Dopo di che, l’uso che ne farete è completamente a vostro carico!Per chiudere il cerchio, avrete a disposizione il tool apt, che potete trovare nella solita $JAVA_HOME/bin. Brevemente, questo tool serve per generare, di solito a build time, dei file che contengano delle informazioni derivate da quelle presenti nelle annotazioni, ed è dunque fondamentale per mantenere in modo automatico la sincronia fra le annota-zioni ed il mondo “esterno”. Vi rimando alla guida ufficiale per i dettagli [APT] Un uso intelligente e non invasivo delle annotations è per esempio fatto nei Testing framework (TestNG, Junit 4). Gli unit tests devono infatti essere eseguiti da un ambiente esterno, ed hanno bisogno di semantiche particolari come “questo metodo è un testcase”, “questo metodo deve esse-re eseguito prima di ogni testcase” che, seppur esprimibili correttamente in Java, sono sicuramente più chiari e con-cisi se espressi tramite annotazioni come @test, @before e @after.

Java 6

Mustang, (ovvero Java 6) questo il nome in codice del successore di Tiger (Java5), ha beneficiato di un supporto

Le annotation permettono di associare delle informazioni ulteriori a del codice (metadati), informazioni che possono poi essere trattate da altri tool per gli scopi più vari.Un esempio di annotazioni ante-litteram sono i javadoc. @deprecated ,per esempio, è una forma di metadato, che esprime la semantica “usatelo a vostro rischio e pericolo, dalla prossima release potremmo cancellarlo”, semantica ovviamente non esprimibile in Java! Ovviamente un nor-male commento non è una forma di metadato (o se lo è molto primitivo), poichè non ha una struttura e dunque non può essere gestito da un tool, mentre il tag @deprecated viene gestito dal tool javadoc, che lo inserisce automatica-mente nella documentazione di progetto.Le annotations sono una generalizzazione di questo mec-canismo che permette di definire ed usare dei nuovi meta-dati, per qualsiasi motivo ne abbiate bisogno (ovviamente non per la documentazione, per quella c’è già javadoc) . Per ottenere ciò, le annotations consistono di:

• una sintassi per dichiarare annotation types• una sintassi per annotare il codice• API per leggere le annotazioni• modifiche alla rappresentazione dei class files per

“contenere” le annotazioni• un tool per processare le annotazioni

le annotations non influiscono direttamente sul runtime, ossia non modificano la semantica del codice, ma in realtà possono farlo indirettamente, per esempio influenzando l’eventuale ambiente in cui il codice è eseguito a runtime: un application server potrebbe eseguire un particolare componente utilizzando diversi cicli di vita a seconda di come questo sia annotato, o decidere sulla base di un’an-notazione se persistere l’oggetto e come.È per questo che le annotazioni possono semplificare mol-to la vita dei produttori di tool: non è un caso che le nuove specifiche EJB 3.0 utilizzeranno pesantemente questo meccanismo. Pensandoci bene, le informazioni contenute nei file XML di un EJB non sono infatti altro che metadati, rappresentati tramite file XML in assenza di altri mecca-nismi Java. Se a questo punto vi viene in mente Xdoclet [XDOCLET], siete sulla strada giusta. Se poi vi viene in mente di eliminare tutti i file di configurazione del vostro software e sostituirli con delle annotazioni da distribuire insieme al sorgente, siete probabilmente sulla strada sba-gliata: le annotations sono un’altra medicina da prendere a piccole dosi, altrimenti è velenosa.

Esistono tre annotazioni standard predefinite in Tiger (Java 5):

@Override, indica che il metodo effettua l’override di un altro metodo @Deprecated, equivalente al @deprecated per i javadoc@SuppressWarnings, “spegne” gli warning del compilatore per una porzione di codice

Usare le annotations è quanto di più semplice si possa pensare:

@Deprecated public void faiQualcosa() { //... }

Page 43: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 2007 43

educational

della comunità molto più ampio. L’intero ciclo di sviluppo di questa release è stato infatti all’insegna della collaborazione e della trasparenza, con sorgenti, nightly build, forum, blog delle persone dell’engineering e paper vari a disposizione sul sito del progetto [MUSTANG]. “Enter the Participation Age”, per dirla con i nuovi slogan del marketing Sun.Il nuovo ciclo di sviluppo di Java fa sì che le novità verranno da ora introdotte in maniera graduale, poi-ché i rilasci fra una versione e l’altra saranno sempre compresi fra 1 anno e mezzo e due anni, e quindi molto probabilmente non assisteremo più a “salti” come quel-lo fra Java 1.4 e Java5. Inoltre la quantità e la qualità delle informazioni a disposizione degli sviluppatori è molto migliorata: qualsiasi software house ha ora la possibilità di pianificare correttamente la transizione da una release all’altra, anticipando i possibili problemi ed avendo un canale diretto con l’ingegneria del prodotto molto prima che sia effettivamente disponibile. Ovvia-mente questo non va interpretato come un consiglio di

andare in produzione con le nightly build di Java6, mi raccomando!Mustang è sicuramente una release dedicata al Desktop: la maggior parte degli sforzi si sono concentrati proprio in que-sto settore. È infatti noto che Java è ormai lo standard defacto per il server side computing, poiché – oltre alla portabilità - offre eccellenti caratteristiche a chi si occupa di middleware di qualsiasi tipo. Nel settore desktop però ci sono sempre stati diversi problemi, alcuni insiti nella natura stessa di linguag-gio multipiattaforma, altri sicuramente dovuti al fatto che in questi anni in Java si è fatto soprattutto altro.Ma vediamo (Tabella 2) alcune (elenco assolutamente non esaustivo!) delle novità di Java6. La tabella 2 non è ovviamente comparabile con quella generale su Java5, poi-ché qui il livello di granularità è molto più basso. Se avessi dovuto elencare a questo livello di dettaglio i cambiamenti in Java5, mi ci sarebbero volute 10 pagine!È evidente che questa release, a differenza della preceden-te, è più orientata a caratteristiche “bells and whistles”.

TABELLA 2 Le principali novità di Java6

Cambiamento Nome Scopo

Desktop Splash screenÈ possibile mostrare uno splash screen, senza aspettare che la virtual machine sia completamente “up”.

DesktopSwing enhancements

• Drag and drop avanzato• miglioramenti delle performance e soprattutto del look

and feel per le varie piattaforme, molto più integrato con il sistema operativo ospite

• miglioramenti per la gestione dei layout (finalmente!)

Desktop System traySupporto ad eventi per icone, tool-tips e menu nelle System bar

Desktop Desktop APILe java.awt.Desktop permettono di interagire con il browser di sistema, con il client email di default e con altre applicazioni in maniera standard (ad eventi)

DesktopJava 2d enhancements

Diversi miglioramenti nelle performance, nel double buffering e nell’antialiasing dei font, anche questo molto più integrato con il sistema operativo ospite

SistemaApplication management

È ora possibile far lanciare alla VM uno script quando l’heap è pieno, oltre ad avere uno stack trace molto più completo nei casi di OutOfMemory. Integrazione delle VM solaris con Dtrace.

Sistema Web servicesAncora migliorie per il supporto nativo di XML e standard annessi. JAX-WS 2.0, Streaming Api per XML (StaX), Digital Signatures API, JAXB 2.0

Sistema SecuritySupporto per GSS/Kerberos, un modulo JAAS per LDAP e nuove API per la gestione delle Smart card (JSR 268, anche se probabilmente queste slitteranno a Java7)

Sistema Java scripting(JSR 223)

Un framework standard per eseguire linguaggi di scripting dalla JVM, permettendogli anche di avere accesso alle API java. Insomma, un modo per avere linguaggi dinamici (alla Ruby) senza rinunciare alle caratteristiche di Java.

SistemaJava Compiler API (JSR 199)

Accesso da Java al suo compilatore. Ora potrete (in maniera standard, si intende, quindi indipendentemente dal fornitore della VM) compilare dinamicamente dei sorgenti java provenienti dalle fonti più disparate. Niente che serva tutti i giorni, sicuramente!

Page 44: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 200744

educational

Non ci sono cambiamenti nel linguaggio, nuove keyword o sostanziali nuovi modi di fare le stesse cose, escludendo forse il supporto standard allo scripting Java (JSR 223). Però ora si può fare un’applicazione Java che – oltre ad avere un gui con look nativo e prestazioni di prima fascia - lascia l’iconcina nel System tray, può reagire al click e lan-ciare per esempio il browser di default aperto su una pagi-na di amministrazione, e può automaticamente accorgersi di anomalie di sistema (e con l’integrazione con Dtrace si possono raggiungere – purtroppo per ora solo su Solaris 10 - risultati incredibili)Insomma, sicuramente un passo avanti per la maturità della piattaforma, e, fortunatamente, niente che richieda giorni e giorni di studio. A meno che non stiate usando ancora Java 1.4, si intende!

Conclusioni

Siamo così giunti alla conclusione di questa mini-rassegna su Java5 e Java6, che, se il tempismo è corretto, dovrebbe aver visto la luce nella sua prima release FCS (First Custo-mer Shipment) proprio in queste settimane.Ho riportato i link della prima parte, oltre al materiale nuovo specifico per gli argomenti trattati in questa secon-da parte, sperando che siano di utilità anche a chi non ha potuto leggere l’articolo per intero.In ogni caso, cominciate ad usare (e non abusare) le nuove feature della nostra piattaforma preferita, e vedrete che ne beneficeranno le performance, la chiarezza e la leggibilità, migliorando sia l’esperienza dei vostri clienti che il vostro lavoro quotidiano. E se pensate che qualcosa può essere ulteriormente migliorato, sottoponete le vostre riflessioni al team di Dolphin (Java7)!

Riferimenti e bibliografia

[APT]_http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html[GOF94] Iterator pattern[BLOCH] Effective Java[CONCURRENT] Concurrent programming in Java: Desi-gn principles and patterns[ENHANCEMENTS] http://java.sun.com/j2se/1.5.0/docs/relnotes/features.html[FLANAGAN] Java 1.5 Tiger, a developer’s notebook[GOSLING] The Java Programming Language, 4th edition[ISOLATION]_http://www.jcp.org/en/jsr/detail?id=121[LEA] http://gee.cs.oswego.edu/dl/[MUSTANG] https://mustang.dev.java.net/[PIZZA] http://www.cis.unisa.edu.au/~pizza/[SHARING]_http://java.sun.com/j2se/1.5.0/docs/guide/vm/class-data-sharing.html[TROUBLESHOOTING] http://java.sun.com/j2se/1.5/pdf/jdk50_ts_guide.pdf[XDOCLET]_http://xdoclet.sourceforge.net/xdoclet/index.html

Note Biografiche

Ugo Landini ha più di 10 anni di esperienza nel mondo Object Oriented, e segue Java dalla nascita. Si ostina ancora a voler pro-grammare e ha velleità di centravanti, con scarsi risultati in en-trambe le attività. È CTO in Pronetics, dove si è occupa preva-lentemente di architetture distribuite e di tutto ciò che riguarda il mondo Java, XML e in generale l’integrazione. È stato Senior Java Architect in Sun Microsystems.

Page 45: j2007 01 jj2
Page 46: j2007 01 jj2

SPEECH Login TopicFREE

L’OpenJDK ha bisogno di aiuto, in quanto alcune parti di Java non possono essere Open Source. La comunità Classpath ci può venire in aiuto a riscrivere quelle parti. Il punto principale è che la piattaforma Java sia disponibile come standard. Penso che per loro (i progetti Classpath e Harmony, nda) sia bene che siano delle implementazioni complete. Sono molto contento che Harmony dia il benvenuto a Java Open Source So che le persone che lo sviluppano ci daranno una mano amichevole. E sono molto felice di supportare il loro lavoro

JJ: È corretto dire che Java ME sarà rilasciato senza la classpath exception? Perché?

Per la piattaforma Java SE abbiamo bisogno della Classpath Exception perché alcune persone, quando distribuiscono una applicazione , distribuiscono anche la piattaforma Java.Per esempio, senza la classpath exception, non sarebbe possibile distribuire sia OpenJDK che Tomcat nello stesso pacchetto.Per quanto riguarda il JavaME, invece, non sei nella situazione di dover distribuire sia l’applicazione che la piattaforma. I soli che hanno bisogno di farlo sono i produttori del dispositivo.

JJ: Non siete preoccupati che possa esserci un “fork” (versione divergente indipendente) di Java?

SP: Non ho paura, perché penso che per il mercato sarebbe una cattiva cosa creare un fork incompatibile. E non credo che vedremo un fork incompatibile.Abbiamo parlato con le comunità di Classpath e di Harmony, ed entrambe sono convinte della necessità di essere compatibili.

Java è diventato definitivamente Open Source e Free Software da poco tempo. Due settimane dopo questo importante avvenimento, abbiamo avuto l’opportu-nità di intervistare, su invito di Sun Microsystems stessa, uno dei principali artefici di questa trasforma-zione: Simon Phipps.

Simon è il direttore delle iniziative Open Source di Sun (Chief Open Source Officer). Abbiamo incontrato Phipps in visita in Italia, a Milano, e lo abbiamo sottoposto a un vero fuoco di fila di domande.

JJ: Perchè avete scelto la GPL come nuova licenza per i sorgenti di Java?

SP: È molto semplice: la GPL ci offre le maggiori opportunità per la piattaforma Java. Selezionando la GPL diventa disponibile nelle versioni free di Unix, dove Java non era prima disponibile.

JJ: Può spiegarci perché il nuovo OpenJDK (la versione Open Source del JDK) è GPL con la eccezione “classpath”. Che cosa è questa eccezione?

SP: Abbiamo semplicemente scelto di utilizzare la licenza con cui le versioni open di Java erano distribuite. La più evidente era il progetto Classpath, quindi abbiamo deciso di distribuire OpenJDK con la stessa licenza del progetto Classpath. Che è appunto la licenza GPL con una eccezione che consente di distribuirlo insieme a un prodotto proprietario, senza dover rilasciare l’intero prodotto come GPL.

JJ: Siccome ci sono già due progetti Java Open Source, il progetto Classpath e il progetto Apache Harmony. Il rilascio del “vero” Java come Open Source li renderà obsoleti?

SP: Non credo che diventeranno obsoleti. La comunità di classpath ci è stata molto di aiuto, e credo che uniremo le forze con loro.

COMMUNITY

n.2 - gennaio/febbraio 200746

Intervista a Simon Phipps

Ü di di Michele Sciabarrà ([email protected])

Page 47: j2007 01 jj2

SPEECH Login TopicFREE COMMUNITY

JJ: Ma cosa farà Sun se qualcuno, per qualsiasi ragione, decidesse di creare una versione indipendente e incompatibile?

SP: Gli ricorderemo che non può chiamare il prodotto “Java” se non passa il TCK (il TCK è il Kit di Test di Compatibilità che assicura che una implementazione di Java sia conforme alle specifiche, nda). Noi abbiamo rilasciato il codice sorgente, ma il codice sorgente non diventa “Java” se non passa i test del TCK.

JJ: Ci sarà mai un Java “leggero”, piccolo e semplice come il Flash Player? Cosa ne pensa se qualcuno usa il sorgente per costruire un simile applet player leggero?

SP: Penso che non potrà chiamarlo Java se non passa il TCK.

JJ: Non esiste ancora una versione Sun di JavaME per la piattaforma Pocket PC. Il rilascio di Java come Open Source favorirà l’apparire di una versione Sun per PocketPC?

SP: Una volta che i sorgenti sono stati rilasciati ognuno può farci quel che vuole. Non vedo ostacoli al fatto che qualcuno faccia il porting.

JJ: È ben noto che IBM ha avuto benefici da Linux vendendo hardware e servizi software. Java e Solaris Open Source permetteranno a Sun di vendere più hardware?

SP: Sì.

JJ: IBM è da molto tempo una azienda di servizi software. Sun sta diventando anche una azienda di servizi software, che rilascia software open source e supporta i clienti nell’adattare il software alle loro esigenze di business?

SP: Recentemente IBM si è ri-registrata al NY Stock Exchange (la borsa di New York) come una azienda di servizi. Non è questa l’intenzione di Sun. Sun intende rimanere una azienda di sistemi. Abbiamo un business di sistemi, storage, software e servizi, e Sun fa il suo meglio combinando queste cose insieme in sistemi. Ci sarà un incremento nel numero di soluzioni per sistemi che forniamo.

JJ: Quale sarà il processo per poter contribuire ai miglioramenti della piattaforma?

SP: È ancora in corso di definizione. Stiamo studiando alcune interessanti opinioni della community, ci aspettiamo di poter dire di più in primavera. Intendiamo realizzare un processo trasparente ed equo.

JJ: Perché è stato scelto Mercurial come strumento per la gestione del codice sorgente?

SP: Avevamo un albero di decisioni davanti a noi. Prima di tutto, internamente, per mantenere il codice sorgente usiamo TeamWare, che è molto vecchio e i cui codici sorgenti non sono disponibili pubblicamente. Per cui abbiamo dovuto scegliere un diverso sistema di gestione dei codici sorgenti. E abbiamo dovuto confrontare i sistemi di controllo di versione centralizzati, come subversion, e quelli distribuiti. TeamWare è un sistema di controllo di versione distribuito, e per come è sviluppato Java, abbiamo scelto un sistema di controllo di versione distribuito.La comunità di OpenSolaris aveva sperimentato diversi sistemi di controllo di versione, come Subversion, Mercurial, SCCS e altri. La conclusione è stata che il sistema di controllo più adatto era Mercurial.

JJ: Ma non è molto conosciuto …

SP: Nessuno dei sistemi di controllo di versione distribuiti è ben conosciuto. Ciononostante ci siamo convinti che Mercurial era la scelta migliore. Per il rilascio iniziale di Javac e Hotspot useremo Subversion. Renderemo anche il codice disponibile come package di Netbeans in modo che si possa utilizzare un sistema familiare.

JJ: Quali parti della piattaforma Java non possono essere rilasciati perché contengono codice proprietario non posseduto da Sun?

SP: Due parti: il Gestore dei Colori, e il rasterizzatore dei font True Type.

JJ: La comunità può aiutare a rimpiazzare queste parti con una implementazione open?

SP: Abbiamo parlato con la comunità Classpath e siamo molto lieti di collaborare con loro. In particolare in Classpath c’è già una implementazione alternativa del Gestore dei Colori. E sono stati molto lieti di contribuire. Per quanto riguarda il rasterizzatore dei font, è un altro problema perché l’architettura di Classpath è diversa dall’architettura del JDK di Sun. Per cui c’è bisogno di maggior lavoro, ma c’è già un team nel gruppo di

n.2 - gennaio/febbraio 2007 47

Note Biografiche

Michele Sciabarrà Dopo aver scritto di Java e averlo insegna-to per dieci anni, attualmente è il direttore esecutivo di Java Journal. Quando non cerca di imparare le ultime API o di-squisisce della superiorità del modello Open Source, si dilet-ta a programmare cellulari Symbian in C++ e amministra-re sistemi Linux.

Page 48: j2007 01 jj2

JAVA Journal

n.1 - novembre/dicembre 2006 35

focus

Page 49: j2007 01 jj2

JAVA Journal

49n.2 - gennaio/febbraio 2007

focus

Resource Management con JMX

La gestione di una risorsa software e/o hardware è la possibilità di poter misurare, a runtime e nel tempo, informazioni relative allo stato della risorsa. È anche la possibilità di po-ter alterare il comportamento della

risorsa a fronte delle evidenze riscontrate.Il deploy in un ambiente di produzione di una applicazione software ben progettata, realizzata e testata, non è la conclusione di un progetto. Tipicamente, per un software in produzione, vi è la necessità di monitorare informazioni a livello di business: per esempio il numero degli ordini inevasi in un sistema di commercio elet-tronico. Oppure informazioni a livello di siste-ma: per esempio, il consumo di memoria heap, il numero di thread attivi, il tempo medio per la garbage collection e così via. Queste misure sono indicatori diretti del corretto funzionamento della applicazione. In modo analogo, la gestione della rete e dei dispositivi hardware è sempre più critica. Gli amministratori di rete impiegano un’ampia varietà di strumenti per il controllo del sistema, al fine di assicurare la continuità e l’affidabilità delle operazioni.Il resource management è la disciplina che orga-nizza la gestione delle risorse hardware e sof-tware di una organizzazione ed è caratterizzato dalla necessità di:

• recuperare informazioni relative alla risorsa;• misurare nel tempo tali informazioni;• alterare a runtime il comportamento della

risorsa a fronte delle evidenze riscontrate.

Per l’hardware management esistono una serie di standard tra i quali il più noto è il protocollo SNMP [1], acronimo di Simple Network Ma-nagement Protocol, usato nella gestione delle reti TCP/IP; mentre per l’application management esistono, per ogni prodotto, soluzioni proprie-tarie costruite ad hoc. In questo contesto la specifica Java Management eXtension [2], o più brevemente JMX, si presenta come una so-luzione standard e globale al tema del resource management in ambiente Java: infatti definisce una architettura, dei design pattern, una serie di API ed una serie di servizi per la gestione ed il monitoraggio delle applicazioni. Gli obiettivi ed i benefici dell’architettura JMX si riassumono nei seguenti punti:

• Scalabilità: l’approccio basato a componenti consente di scalare nella gestione ed ammi-nistrazione da decine a decine di migliaia di risorse;

• Integrazione con soluzioni legacy di mana-gement come SNMP, WBEM (Web-Based Enterprise Management), CIM (Common In-formation Model, ossia il modello di dati che descrive gli oggetti presenti in un ambiente di gestione) e TMN (Telecommunications Ma-nagement Networks);

• Semplicità nell’instrumentation, ossia nel ren-dere accessibile via JMX, risorse software o hardware.

Le informazioni esposte via JMX possono essere recuperate e visualizzate da sistemi di manage-ment evoluti come IBM Tivoli, HP Openview o da semplici tool come l’HTML protocol adapter

Java Management eXtension (JMX) è una specifica che offre una API ed una architettura per la gestione ed il monitoraggio delle risorse in Java

>> di Fabio Staro

Page 50: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 200750

focus

della SUN [3] o la Jconsole [4] presente dal Java De-velopment Kit 5.0.

L’architettura JMX

La specifica JMX definisce una architettura per la ge-stione di risorse. I livelli nell’architettura JMX sono tre, illustrati nella Figura 1:

• Instrumentation level;• Agent level;• Distributed level.

L’instrumentation level fornisce le regole attraverso le quali una risorsa può essere gestita da una piattafor-ma compatibile col modello JMX. Fondamentalmen-te, una risorsa è resa accessibile alle applicazioni di management dopo averla incapsulata in un managed bean, o più brevemente MBean, che espone attributi della risorsa indicativi del corretto funzionamento. In definitiva quindi un MBean rappresenta una risorsa hardware o software controllata e amministrata dal sistema di management. Nella specifica, un MBean è una classe Java concreta che implementa la sua corrispondente interfaccia di gestione o l’interfaccia DynamicMBean, nel qual caso l’interfaccia di gestione è definita a runtime. L’interfaccia di gestione di un MBean può essere composta da: • Attributi in lettura e/o modifica per conoscere lo

stato della risorsa; • Costruttori per creare istanze della risorsa;• Metodi per eseguire operazioni sulla risorsa;

• Parametri per i costruttori ed i metodi;• Notifiche emesse dalla risorsa in broadcast attra-

verso l’infrastruttura JMX.

Ovviamente, la classe MBean può avere più attributi e/o metodi di quelli definiti nella relativa interfaccia di gestione, ma questi, anche se dichiarati con il co-strutto public, sono funzioni non disponibili al siste-ma di management. L’instrumentation level definisce anche un meccanismo di notifica. Questo consente agli MBean di generare e propagare eventi di notifica ad altri MBean ed in generale ai componenti presenti presso gli altri livelli della architettura. Il modello di notifica si poggia sui seguenti componenti:

• Classe Notification: è il messaggio di notifica più le informazioni relative al messaggio stesso;

• Interfaccia NotificationListener: è implementata dal ricevente i messaggi;

• Interfaccia NotificationEmitter: è implementata dal-l’oggetto che emette messaggi di notifica;

• Classe NotificationBroadcasterSupport: classe di sup-porto che implementa l’interfaccia NotificationE-mitter;

• Interfaccia NotificationFilter: consente di filtrare i messaggi di notifica di un listener.

La specifica JMX definisce quattro tipi di MBeans: Standard, Dynamic, Open e Model MBeans. Si veda la Tabella 1 per maggiori dettagli. L’Agent level fornisce una specifica per implementa-re agenti JMX. Un agente JMX controlla direttamente le risorse e le rende disponibili alle applicazioni re-mote di management. Un agente JMX è costituito da

FIGURA 1 I livelli in una architettura JMX

Page 51: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 2007 51

focus

un managed bean server, da una serie di agent services e almeno un protocol adapter e/o un connettore. Il managed bean server, o più brevemente MBean server, è il componente fondamentale nell’architettura JMX poichè tutte le operazioni di management su-gli MBean sono mutuate dal server che si propone come un broker fra le applicazioni di management e le risorse gestite. Gli MBean sono registrati nel managed bean server che pertanto funge da registro centralizzato. Attraverso il concetto di object name, oggetto che identifica univocamente il managed bean registrato nel managed bean server, si realizza il disaccoppiamento tra applicazione di management e risorsa. Una applicazione di management può in-teragire con un managed bean fornendo il suo nome all’agente JMX per ogni operazione invocata. Il meccanismo di invocazione remota non si basa sul modello stub-skeleton, come accade per esempio nel-le architetture RMI (Remote Method Invocation) ed EJB (Enterprise JavaBean), ma solo sull’uso

dell’object name attraverso il quale si individua un oggetto (operazione detta di name lookup), e se ne invoca un suo metodo. Pertanto, una eventuale mo-difica introdotta nell’interfaccia del managed bean non richiede la redistribuzione della medesima. Questa scelta, se da un lato semplifica la gestione di strutture complesse, costituite per esempio da un alto numero di oggetti, dall’altro elimina il type checking sui parametri e sui valori restituiti (cfr. Fi-gura 2).La specifica JMX prevede come obbligatori quattro agent services mentre altri possono essere aggiunti dalle varie implementazioni proprietarie. Gli agent services definiti dalla specifica sono il Dynamic Class Loading service, il Timer service, il Relation service ed il Monitoring service. Il primo servizio consente di cari-care e di instanziare dalla rete nuove classi o librerie native; il Timer service fornisce un meccanismo di schedulazione degli eventi e consente di eseguire operazioni a determinati intervalli di tempo; il Rela-

TABELLA 1 Gli MBeans nella specifica JMX

Tipo Descrizione

Standard MBean

Sono gli MBean più semplici da definire ed implementare. Tutti gli attributi, le operazioni e gli eventi associati ad uno Standard MBean sono specificati staticamente nella sua interfaccia di gestione. Gli Standard MBean devono implementare una interfaccia che è dichiarata seguento ben precise regole di coding convention (regole di codifica indicate con il termine lexical design pattern nella specifica JMX 1.2). Per esempio per instrumentare una classe chiamata com.xyz.MyClass la interfaccia di gestione si deve chiamare com.xyz.MyClassMBean. Un agente JMX attraverso un processo di introspezione scopre la interfaccia di gestione di uno Standard MBean.

Dynamic MBean

I Dynamic MBean devono implementare l’interfaccia javax.management.DynamicMBean. Tutti gli attributi, le operazioni e gli eventi associati con il managed bean, ossia i suoi metadati, sono determinati a runtime attraverso l’interfaccia DynamicMBean. Pertanto una risorsa da gestire può avere attributi, operazioni ed eventi che possono variare nel tempo.

Model MBean

Un Model MBean è un Dynamic MBean infatti l’interfaccia ModelMBean estende l’interfaccia DynamicMBean. La specifica JMX impone che tutte le implementazioni della specifica devono fornire un istanza di Model MBean chiamata RequiredModelMBean presente nel package javax.management.modelmbean. Questo è un generico, pre-fabbricato e dinamico MBean pronto all’uso per instrumentare le risorse.

Open MBean

Un Open MBean è un Dynamic MBean dove tutti gli attributi del managed bean appartengono ad un insieme ben definito di tipi di dati Java (per esempio String, Integer, Boolean, etc.). Un Open MBean non estende nessuna particolare interfaccia, a parte l’interfaccia DynamicMBean, e l’agente JMX capisce che è in presenza di un Open MBean perché riceve un oggetto che implementa la interfaccia OpenMBeanInfo presente nel package javax.management.openmbean descrittiva dei metadati del bean.

Page 52: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 200752

focus

tion service consente di creare relazioni fra gli MBean e, per concludere, il Monitoring service misura ad in-tervalli regolari di tempo la variazione del valore di un attributo di una risorsa e in base al valore della variazione invia notifiche ai listener associati. Sono disponibili tre tipi di monitor:

• Counter monitor;• Gauge monitor;• String monitor.

Il Counter monitor osserva in continuazione gli in-crementi non negativi degli attributi di tipo intero che si comportano come un contatore, inviando una notifica quando il valore dell’attributo raggiunge o supera un certo valore di soglia (threshold). È possi-bile impostare un valore di offset che viene aggiunto al valore di soglia ogni volta che questo è raggiunto e superato: in tal modo è possibile seguire l’anda-

mento incrementale nel tempo del-l’attributo di tipo contatore (cfr. Figu-ra 3). Il Gauge monitor osserva la varia-zione del valore di attributi numerici inviando una notifica quando il valore della variazione supera in eccesso un limite superiore o in eccesso un limite inferiore (cfr. Figura 4). È fornito un meccanismo (hysteresis mechanism) per evitare l’invio continuo di notifiche quando vi sono piccole variazioni intorno al valore di soglia. Lo String monitor osserva gli attributi di tipo stringa inviando una notifica quando la variazione è uguale o differisce da una stringa predefinita.

Il Distributed level contiene almeno un protocol adapter o un connettore attraverso i quali gli agenti JMX sono accedibili remotamente. Gli adapter forniscono una vista degli agenti JMX attraverso un dato protocollo (per esempio HTTP o SNMP). Questi mappano la se-mantica delle interazioni sugli MBean e sullo MBean server in una rappresentazione adatta ad un dato protocollo e ad un modello informativo differente come, per esempio, il protocollo SNMP. Nell’esem-pio che segue useremo l’HtmlAdaptorServer [3] con il quale l’interfaccia di gestione è visibile attraverso un browser web. I connettori, viceversa, consentono di connettere un agente JMX con un’applicazione remota compatibile al modello JMX usando una tec-nologia distribuita, ad esempio RMI. Questo tipo di comunicazione prevede un connettore che funge da server nell’Agent level e un connettore con il ruolo di client installato nella JVM dell’applicazione di ma-nagement (cfr. Figura 5).

FIGURA 2 Il modello di invocazione JMX

FIGURA 3 Counter monitor: figura tratta dalla JMX Instrumentation and Agent Specification, v1.2

Page 53: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 2007 53

focus

Management di un’applicazione software

L’applicazione software che desideriamo ammini-strare e gestire è volutamente semplice al fine di approfondire l’API e l’architettura JMX, eliminan-do complessità applicative legate ad un particolare

dominio di interesse. L’applicazione di esempio è costituita dalle classi Java Esecutore e Configurazione ed è stata sviluppata con la J2SE versione 5.0 la quale integra le API di management e monitoring [5]. La classe Esecutore è un thread che ogni 10 se-condi circa invoca il metodo getValore() della classe

FIGURA 4 Gauge monitor: figura tratta dalla JMX Instrumentation and Agent Specification, v1.

FIGURA 5 Protocol Adapter e connettori: figura tratta dalla JMX Instrumentation and Agent Specification, v1.2

Page 54: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 200754

focus

Configurazione scrivendo sulla console di output il valore restituto dal metodo. Il metodo getValore() consente di recuperare il valore di due parametri, denominati conf_1 e conf_2, memorizzati inizial-mente in un file XML. Di seguito riportiamo uno stralcio del codice dalla classe Esecutore esplicativo di quanto descritto:

…public void run(){ while(true) {

out.println(“CONF_1:” +Configurazione.INSTANCE .getValore(“conf_1”)); out.println(“CONF_2:” +Configurazione.INSTANCE .getValore(“conf_2”)); try { Thread.sleep(10000L); } catch (InterruptedException e) {…} }}…

La classe Configurazione legge i parametri di configu-razione, conf_1 e conf_2, dal file XML durante la fase di inizializzazione e li memorizza in un oggetto di tipo java.util.Properties. La classe è un singleton (cfr. il pattern Singleton [GoF]) e pertanto è presente una sola instanza in memoria della classe referenziata at-traverso l’attributo pubblico e statico INSTANCE (cfr. il Listato 1 per i dettagli).La risorsa software che desideriamo amministrare è la classe Configurazione. In particolare, attraverso una console di amministrazione JMX, deve essere possibile:

• Eseguire a caldo, ossia senza fermare e far ripartire l’applicazione, la rilettura del file XML di configu-razione (metodo reload());

• Visualizzare il valore di un parametro di configura-zione presente nel file XML (metodo getValore());

• Visualizzare la data in cui è avvenuta l’ultima lettura dei valori presenti nel file XML (attributo lastUpdate).

Come è stato precedentemente descritto, l’instru-mentation level definisce le regole per “instrumen-tare” le risorse in modo che queste possano essere gestite da un agente JMX. L’instrumentazione delle risorse avviene attraverso gli MBean. Ini-ziamo con intrumentare la classe Configurazione attraverso uno Standard MBean. Pertanto, definia-mo l’interfaccia di gestione tramite la quale un oggetto della classe Configurazione potrà essere manipolato. L’interfaccia deve esporre i metodi da invocare per eseguire una operazione sulla risorsa ed i metodi per la lettura e la modifica di un attributo. La regola sui nomi degli attributi segue la naming convention dei JavaBean e pertanto in presenza dei metodi set () e get () l’agente JMX deduce che vi sia una proprietà in lettura e modi-fica. L’assenza di uno dei due metodi implica che l’attributo sia di sola lettura o di sola scrittura. I nomi dei metodi da invocare, ossia le operazio-ni sulla risorsa, non seguono nessuna regola e ogni metodo che non inizia per get o per set è per l’agente JMX un metodo di esecuzione e non un metodo per la manipolazione diretta di un attri-buto della classe. L’interfaccia di management deve avere un nome formato dal nome della clas-se da gestire con l’aggiunta del suffisso MBean. Per esempio la classe Java it.articolo.Configurazione

LISTATO 1 La classe Configurazione

public final class Configurazione {

private static final String NOME_FILE = “/configurazione.xml”; private Date lastUpdate = null; private final Properties prop = new Properties();

// Unica istanza della classe Configurazione // presente ed invocabile da parte di altre classi. public static Configurazione INSTANCE = new Configurazione(); // Costruttore privato private Configurazione(){loadConfigurazione();}

private void loadConfigurazione() { InputStream is = Configurazione.class .getResourceAsStream(NOME_FILE); try { prop.clear(); prop.loadFromXML(is); lastUpdate = new Date(); } catch (Exception e) { err.println(“Eccezione nella lettura del file:” +e.toString()); }} public synchronized String getValore(String nomeProprieta){ return this.prop.getProperty(nomeProprieta);} // Metodo che legge il file XML. public synchronized void reload() { loadConfigurazione(); }

// Metodo che restituisce // l’ultima data di lettura del file XML. public Date getLastUpdate() { return lastUpdate; } }

Page 55: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 2007 55

focus

deve implementare l’interfaccia it.articolo.ConfigurazioneMBean. Di seguito la definizione della inter-faccia ConfigurazioneMBean:

public interface ConfigurazioneMBean { // operazione per ottenere il valore // di un parametro di configurazione public String getValore(String nomeProprieta); // operazione di rilettura del file XML public void reload(); // attributo di sola lettura public Date getLastUpdate();}

Una volta che l’MBean è stato definito è necessario:

1. Creare o recuperare una istanza del managed bean server;

2. Registrare l’MBean all’interno del server;3. Collegarsi al server attraverso un protocol adapter

e interagire con la risorsa, per esempio attraverso una console HTML di amministrazione.

Tutte le operazioni sopra elencate sono racchiuse nella classe AgenteHelper (cfr. Listato 2). Un’istanza di MBeanServer può essere creata o recuperata at-traverso i metodi helper della classe javax.managemen

t.MBeanServerFactory. La riga di codice che segue crea un oggetto che implementa l’interfaccia javax.management.MBeanServer mantenendo internamente una reference all’oggetto appena creato. In tal modo attra-verso il metodo findMBeanServer(), sempre presente nella classe MBeanServerFactory, è possibile ricercare e ritornare la reference all’istanza precedentemente creata.

MbeanServer server = MBeanServerFactory.createMBeanServer();

Lo stralcio di codice che segue mostra come regi-strare un managed bean all’interno dello MBeanServer in modo tale che possa essere acceduto attraverso chiamate JMX:

ObjectName name = new ObjectName( “Articolo:name=ConfigurazioneStandardMBean”); server.registerMBean( Configurazione.INSTANCE ,name);

Ogni MBean deve essere univoco all’interno dello stesso MBeanServer e il suo nome è gestito tramite la classe javax.management.ObjectName. Un ObjectNa-me è composto da un domain name, che rappresen-

FIGURA 6 Console di amministrazione fornita dall’adapter HTTP della implementazione JMX della Sun

Page 56: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 200756

focus

ta il namespace all’interno del JMX Agent, seguito da una lista di proprietà che identificano univoca-mente l’MBean e forniscono informazioni relative all’oggetto:

domain: key1 = value1 , key2 = value2

nel nostro esempio il domain name è Articolo e l’unica proprietà che impostiamo è il nome del managed bean.Infine, per accedere al managed bean utilizziamo l’HTML protocol adapter fornito dalla SUN [3]. Le seguenti righe di codice permettono la creazione, la registrazione e l’avvio dell’adapter sulla porta 9092:

HtmlAdaptorServer adapter

= new HtmlAdaptorServer();adapter.setPort(9092);ObjectName name = new ObjectName(“Articolo:name=HTMLAdapter”);server.registerMBean(adapter ,name);adapter.start();

Osservando le righe di codice sopra riportate, possia-mo notare come l’HTML adapter sia esso stesso un MBean registrato nel managed bean server. Attraverso un qualsiasi browser è ora possibile collegarsi sulla porta 9092 all’host dove è in esecuzione l’agente JMX ed interagire con gli MBean (Figura 6 e Figu-ra 7).Con l’esempio precedente è possibile notare come instrumentare una classe Java attraverso uno Standard MBean sia un’operazione sostanzialmen-te semplice. Infatti, uno Standard MBean dichiara

FIGURA 7 Console di amministrazione fornita dall’adapter HTTP della implementazione JMX della Sun

Page 57: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 2007 57

focus

esplicitamente la propria interfaccia di gestione e questa è costruita seguendo delle naming conven-tion. Tuttavia, uno Standard MBean è ideale per la gestione di risorse statiche, ossia per risorse la cui interfaccia di gestione non cambia nel tempo e che può essere definita per esempio già durante il processo di sviluppo. Di contro, un Dynamic MBean è ideale per la gestione di risorse che evolvono nel tempo o per risorse esistenti che non seguono il paradigma dei JavaBean. Un Dynamic MBean defi-nisce l’interfaccia di gestione a runtime attraverso meta-classi descrittive. Se con gli Standard MBean i metadati sono ricavati dall’agente JMX tramite un processo di introspezione (la reflection Java), con i Dynamic MBean lo sviluppatore è responsabi-le di definire i metadati della risorsa. Un Dynamic MBean deve implementare l’interfaccia javax.management.DynamicMBean. I metodi dichiarati nell’in-terfaccia consentono di:

• Recuperare il valore di uno o più attributi (metodi getAttribute() e getAttributes());

• Impostare il valore di uno o più attributi (metodi setAttribute() e setAttributes());

• Invocare una operazioni esposta nella interfaccia di gestione (metodo invoke());

• Recuperare dinamicamente la interfaccia di gestio-ne (metodo getMBeanInfo()).

La classe MBeanInfo, presente nel package javax.management, descrive l’interfaccia di gestione (ossia l’insieme di attributi e metodi che sono di-sponibili per operazioni di management) esposta dallo MBean. Come osservato in precedenza, una istanza della classe MBeanInfo è restituita dal me-todo getMBeanInfo() per i Dynamic MBean, e quindi anche per i Model MBean e per gli Open MBean che

FIGURA 8 Diagramma delle classi che descrivono i metadati di un Mbean

Il resource manage-

ment è la disciplina che

organizza la gestione

delle risorse hardware e

software

Page 58: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 200758

focus

sono dei tipi di Dynamic MBean, mentre è ricavata con la reflection Java dallo MBean server per gli Stan-dard MBean. Il diagramma delle classi della Figura 8 riporta tutte le classi che descrivono i metadati di un MBean.Nell’esempio che segue instrumentiamo sempre la classe Java Configurazione ma in questo caso attraver-so un Model MBean. Le righe di codice che seguono mostrano come registrare il Model MBean nel managed bean server:

name = new ObjectName( “Articolo:name=ConfigurazioneModelMBean”);RequiredModelMBean model = new RequiredModelMBean( ConfigurazioneInfo.getInfo());model.setManagedResource( Configurazione.INSTANCE, “objectReference”);server.registerMBean(model ,name);…

Il Model MBean che abbiamo registrato nel managed bean server è una istanza della classe RequiredMo-delMBean presente nel package javax.management.modelmbean. Una implementazione del Required-ModelMBean è presente in ogni agente JMX com-

patibile con la specifica JMX. Al costruttore della classe RequiredModelMBean passiamo una istanza della classe ModelMBeanInfo. È opportuno osservare come un Model MBean debba esporre i suoi metada-ti descrittivi attraverso un’istanza della classe Mo-delMBeanInfoSupport che estende la classe MBeanIn-fo e implementa l’interfaccia ModelMBeanInfo. Nel nostro caso, la classe Java ConfigurazioneInfo (cfr. il Listato 3 per i dettagli) restituisce un oggetto ModelMBeanInfoSupport. Per terminare, definiamo per l’instanza della classe RequiredModelMBean l’oggetto da gestire e sul quale eseguire tutte le operazioni di management:

model.setManagedResource( Configurazione.INSTANCE, “objectReference”);

In modo analogo all’esempio precedente possiamo amministrare il bean attraverso la console HTML dello adapter SUN.

Conclusioni

In questo articolo abbiamo introdotto la specifica JMX per la gestione ed il monitoraggio delle risor-se in Java ed abbiamo instrumentato una risorsa

LISTATO 2 La classe AgenteHelper

class AgenteHelper{ private MBeanServer server = null;

AgenteHelper() { server = MBeanServerFactory.createMBeanServer(); }

// Metodo per la registrazione nel server // dello HtmlAdaptorServer e suo avvio sulla porta // 9092 void attivaHTMLAdapter() { HtmlAdaptorServer adapter = new HtmlAdaptor-Server(); try { ObjectName name = new ObjectName(“Articolo:name=HTMLAdapter”); server.registerMBean(adapter ,name); out.println(“REGISTRATO MBEAN:”+name.getCanonicalName()); } catch (Exception e) { err.println(“ECCEZIONE:”+e.toString()); } adapter.setPort(9092); adapter.start();}

// Metodo per la registrazione nel server

// dello Standard e del Model MBean. void recordMBean() { try { // Registra lo StandardMBean ObjectName name = new ObjectName( “Articolo:name=ConfigurazioneStandardMBean”); server.registerMBean(Configurazione.INSTANCE ,name); out.println(“REGISTRATO MBEAN:”+name.getCanonicalName()); // Registra il Model MBean name = new ObjectName( “Articolo:name=ConfigurazioneModelMBean”); RequiredModelMBean model = new RequiredModelMBean( ConfigurazioneInfo.getInfo()); model.setManagedResource( Configurazione.INSTANCE, “objectReference”); server.registerMBean(model ,name); out.println(“REGISTRATO MBEAN:”+name.getCanonicalName()); } catch (Exception e) { err.println(“ERRORE NEL REGISTRARE IL BEAN:”+ e.toString()); } }} // fine della classe

Page 59: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 2007 59

focus

software Java attraverso uno Standard ed un Model MBean. Ovviamente, la ricchezza degli argomenti da trattare è tale da non considerare esaurito l’ar-gomento del resource management in Java. Tuttavia per gli esempi portati è chiaro che implementare la gestione delle risorse attraverso la specifica JMX è un processo sostanzialmente semplice. L’integra-zione della API JMX nella J2SE 5.0 sottolinea come il progettare e realizzare la gestione delle risorse attraverso una soluzione standard debba essere una fase integrata nello sviluppo di un pro-getto software.

Riferimenti

[1]_Protocollo_SNMP:_http://it.wikipedia.org/wiki/SNMP[2]_Home page JMX : [3]_Adapter_HTML_(presente nella JMX refer-ence_implementation,_archivio_jmxtools.jar) :

http://java.sun.com/products/JavaManagement/download.html[4]_Jconsole:_http://java.sun.com/j2se/1.5.0/docs/tooldocs/share/jconsole.html [5]_J2SE_5.0_API:_http://java.sun.com/j2se/1.5.0/docs/api/index.html

Bibliografia:

[GoF]: Gamma, Erich, Richard Helm, Ralph John-son, and John Vlissides. 1995. Design Patterns:Elements of Reusable Object-Oriented Software. Pubblicato da Addison-Wesley.

LISTATO 3 La classe ConfigurazioneInfo

// Classe helper per ottenere un oggetto// ModelMBeanInfo con i metadati // della risorsa da gestirepublic final class ConfigurazioneInfo { public static ModelMBeanInfo getInfo() { return new ModelMBeanInfoSupport( “it.articolo.Configurazione”, “Gestore della configurazione”, getAttributesInfo(), getConstructorsInfo(), getOperationsInfo(), null); }

private static ModelMBeanOperationInfo[] getOperationsInfo() { ModelMBeanOperationInfo[] info = new ModelMBeanOperationInfo[3]; MBeanParameterInfo[] pInfo = new MBeanParameterInfo[1]; pInfo[0] = new MBeanParameterInfo( “keyProperty”, “java.lang.String”, “chiave della propietà”); info[0] = new ModelMBeanOperationInfo( “getValore”, “Ritorna il valore di un param di config”, pInfo, “String”, ModelMBeanOperationInfo.INFO); info[1] = new ModelMBeanOperationInfo( “reload”, “Ricarica il file di configurazione”, null, “void”, ModelMBeanOperationInfo.ACTION);

info[2] = new ModelMBeanOperationInfo( “getLastUpdate”, “Ritorna la data di modifica della config “, null, “java.util.Date”, ModelMBeanOperationInfo.INFO); return info; }

private static ModelMBeanConstructorInfo[] getConstructorsInfo() { return null; }

private static ModelMBeanAttributeInfo[] getAttributesInfo() { ModelMBeanAttributeInfo[] info = new ModelMBeanAttributeInfo[1]; Descriptor oDesc = new DescriptorSup-port(); oDesc.setField(“name”, “lastUpdate”); oDesc.setField(“descriptorType”, “attri-bute”); oDesc.setField(“displayName”, “Data ultimo reload della config”); oDesc.setField(“getMethod”, “getLastUpda-te”); info[0] = new ModelMBeanAttributeInfo( “lastUpdate”, “java.util.Date”, “Data ultimo reload della config”, true, false, false, oDesc); return info; }

} // fine della classe

Note Biografiche

Fabio Staro, dottore in Fisica è Responsabile Tecnico per i pro-getti Java presso la Direzione Ricerca ed Innovazione di Engi-neering Ingegneria Informatica S.p.A

Page 60: j2007 01 jj2

SPEECH Login TopicFREE

itinerante, un roadshow che tocca diverse città italiane in tutta la penisola, portando la tecnologia Java anche in ambiti geografici normalmente esclusi dai circuiti dei grandi eventi. Questa caratteristica fa di JavaDay un momento unico e qualificante, dove la tecnologia, le istituzioni, gli sviluppatori e le imprese si possono incontrare direttamente sul proprio territorio per confrontarsi, capire, conoscere e verificare tante opportunità”. Questa è la forza della community!

Avremo modo di “incontrare” i diversi gruppi nello spazio offerto da Java Journal. Segnaliamo brevemente due JUG che si sono distinti in modo particolare. Il JUG Sardegna ONLUS è stato premiato da James Gosling durante la JavaOne dello scorso maggio, ricevendo il prestigioso Duke Award per il progetto “JUG Sardegna per l’AVIS” (http://jug-avis.dev.java.net), destinato a supportare le attività di reperimento di donatori di sangue condotte dall’Associazione Volontari Italiani Sangue (http://www.avis.it). È doveroso, poi, un riferimento al JUG Torino, che ha (coraggiosamente!) dato inizio al JavaDay: l’evento nel capoluogo piemontese è stato un successo, grazie all’eccellente lavoro svolto dal team organizzatore.

Quando si presenta Java (linguaggio, piattaforma, tecnologia, librerie, applicazioni...) è inevitabile sottolineare che attorno ad esso vive una comunità numerosa e operosa di programmatori e aziende. Non è, dunque, la sola “potenza tecnologica” a decretarne il successo, ma anche lo spirito di collaborazione e condivisione che nelle community (reali e virtuali) trova la sua massima espressione. Questa pagina di Java Journal vuole offrire una panoramica sulle community (nazionali e non), dove poter segnalare eventi, raccontare cosa accade nel mondo Java, cercare di trasmettere ed esaltare lo spirito di collaborazione di cui si è detto.

La community italiana e gli eventi

In Italia esistono numerose realtà, a partire dai Java User Group (JUG), che hanno respiro locale, fino alla Java Italian Association (JIA); o comunità come Java Italian Portal (JIP, uno dei gruppi più vivaci della penisola!). Community significa mailing list, forum, risorse condivise ma soprattutto meeting, seminari, giornate di approfondimento e supporto agli eventi nazionali. Se, infatti, gli incontri a livello locale mantengono viva l’attività all’interno dei singoli gruppi, quelli di più ampio respiro costituiscono un’occasione per conoscersi, scambiare esperienze, avviare nuove collaborazioni. Oltre ai forum e alle liste, dunque, una opportunità per aprire nuovi contatti. La Java Conference, evento nazionale per eccellenza organizzato da Sun Microsystems, si avvale della collaborazione dei JUG e delle altre community già dal 2005; e il legame è stato rafforzato nell’edizione di quest’anno. Nasce, invece, dalle community il JavaDay, il primo evento itinerante su Java. Grazie allo sforzo congiunto dei JUG, di JIA e JIP, come indicato sul sito ufficiale dell’iniziativa (http://www.javaday.it), “JavaDay è una manifestazione

COMMUNITY

n.2 - gennaio/febbraio 200760

COMMUNITY WATCH Ü di Stefano Sanna ([email protected])

Note Biografiche

Stefano Sanna si occupa di sviluppo di applicazioni multi-mediali per piattaforma Savaje presso Beeweeb Technolo-gies (http://www.beeweeb.com). Ha lavorato per sette anni nel gruppo Network Distributed Applications del CRS4, oc-cupandosi di mobile computing e applicazioni network-orien-ted per cellulari e PDA. Collabora attivamente con JIA, JUG Sardegna e Java Mobile Developers Forum. Il sito sito web personale è http://www.gerdavax.it

Page 61: j2007 01 jj2

SPEECH Login TopicFREE

• una volta che il bug è apparentemente risolto, sincro-nizzare solo gli accessi in scrittura, perché le letture tanto sono in read only e così si ottimizza un po’;

• al riapparire di bug che sembravano risolti, prenderse-la con la VM o con l’application server.

Seriamente, ecco un semplice test per verificare se si è capito il meccanismo di sincronizzazione di Java:

• I metodi statici e quelli non statici, se sincronizzati, sono fra loro in mutua esclusione?

• Un costruttore può essere dichiarato synchronized?• Se un thread A esegue lo statement a = 42 all’istante

T0, è sicuro che il thread B in un istante successivo T1 > T0 “veda” il valore 42 in a?

Per ogni sì risposto, un punto di penalizzazione!La parola chiave synchronized fa in realtà tre cose di-verse:

• Si occupa dell’atomicità di una operazione: sempli-cemente fa sì che la VM cerchi di acquisire il lock dell’oggetto prima di “eseguire” il metodo (o il blocco di codice), acquisendolo se è libero o mettendosi in attesa se non lo è. Ma, badate bene, il lock è dell’og-getto: non del metodo, né della classe. È un attributo della classe Object, per cui ogni istanza ha il suo lock. E questo implica diverse cose:

o Due metodi sincronizzati di una stessa classe sono in mutua esclusione fra loro, se eseguiti sullo stesso oggetto.

o Un metodo statico sincronizzato ed uno non statico sincronizzato non sono fra loro in mutua esclusione, perché usano lock diversi. Il metodo statico (che, ri-cordiamoci, non ha il this), si sincronizza sul lock dell’oggetto di classe Class, quello non statico sul-l’istanza relativa.

o Metodi sincronizzati non statici, eseguiti su istanze diverse, non sono fra loro in mutua esclusione.

o Un costruttore non ha senso che sia sincronizzato,

Sincronizzazioni che non si sincronizzano, o che si sincronizzano troppo!

Agli albori di Java, una delle funzionalità più importanti per chi decideva di passare a questo nuovo linguaggio, era la capacità di poter gestire in maniera relativamente semplice (e ovviamente multipiattaforma) la program-mazione concorrente.Java aveva, ed ha tuttora, un supporto eccellente per la gestione di Thread multipli di esecuzione: dalla parola chiave synchronized alle concurrent utilities (inserite in Java 5), il supporto alla programmazione concorrente in Java è sempre stato (almeno in teoria) alla portata di tutti.Ma la realtà è che molti pensano di sapere cosa faccia realmente synchronized: e, di solito, si sbagliano di gros-so... Personalmente in questi anni ne ho sentite di tutti i colori, e spesso anche da persone apparentemente molto preparate.Il cammino tipico di un programmatore ignaro delle in-fide trappole che si celano dietro alla programmazione concorrente è più o meno questo:

• non sincronizzare nulla, ignari di essere in un am-biente inerentemente multithread (servlet/jsp, per esempio);

• all’apparire di bug misteriosi, sincronizzare metodi più o meno a caso;

• al persistere dei bug, sincronizzare tutti i metodi, tan-to per sicurezza;

IDIOMATICA

n.2 gennaio/febbraio 2007 61

Codice orribile, antipattern e pratiche discutibili

In questa rubrica analizzeremo i più comuni errori di codifica, di progettazione ed i tranelli in

cui è facile cadere. Programmatore avvisato, mezzo salvato!

La massima di questo mese è: “What we do not understand we do not possess”

Goethe

>> di Ugo Landini ([email protected])

Page 62: j2007 01 jj2

FREELINUXSPEECH Login TopicFREE IDIOMATICA

perché l’oggetto ancora non esiste (lo si sta co-struendo), e dunque neanche il lock relativo. In ef-fetti, non compila neanche, ma questo è marginale

o Si occupa della visibilità delle operazioni eseguite fra diversi thread

o Si occupa di garantire l’ordinamento delle opera-zioni eseguite da un thread dal punto di vista di un altro thread che “osserva”.

Se il primo punto è più o meno conosciuto, anche se spesso male interpretato, gli ultimi due sono ai più del tutto misteriosi. In caso di insufficiente sincronizzazio-ne è perfettamente possibile che accadano le cose più strane, per esempio che il seguente codice abbia un loop infinito

class MyThread extends Thread { public void run() { while (!done) { // fa qualcosa ... } } public synchronized void setDone() { done = true; } private boolean done;}

Regola

Vanno sincronizzate anche le letture, e non solo le scrit-ture, altrimenti non è affatto assicurato che un thread che cerca di leggere la variabile done abbia visibilità di una precedente scrittura della stessa da parte di un altro thread. Il perché di questo comportamento, apparente-mente strano, va cercato nei meandri del Java Memory Model. Avendo esaurito lo spazio a disposizione in questa rubrica su questo numero, sarà bene rimandare ulteriori spiegazioni ad un prossimo approfondimento!

Esercizi

•_Andare a cercare eventuali servlet con il doGet() sin-cronizzato.

•_Andare a cercare eventuali servlet con il SingleThreadModel.

Conclusioni

Scrivere codice multithreaded è difficile ed è una cosa nota. Scrivere codice semplicemente thread safe non è così difficile, ma non è neanche così banale come po-trebbe sembrare ad una prima, superficiale analisi: il meccanismo di sincronizzazione va compreso a fondo per evitare gli errori più comuni ed ottenere il meglio da Java.

n.1 - novembre/dicembre 2006 62

Page 63: j2007 01 jj2

JAVA Journal

63n.2 - gennaio/febbraio 2007

focus

Accedere a file di Excel in Java

Entrate in ufficio e il vostro collega esperto in tecnologie Microsoft, è particolarmente sorridente e vi informa che “è arrivata la richiesta di automatizzare la generazione dei report amministrativi in Excel;

è lavoro delicato perché i report andranno all’amministratore delegato”, dice con tono ironico e un po’ beffardo. E aggiunge: “Mi sa che questa volta dovrò pensarci io”. Per circa 10 millisecondi temete abbia ragione lui; ma vi ri-prendete subito, sapendo di poter “competere” anche nell’automazione dei documenti Office Microsoft.Esistono numerosi prodotti per generare re-port aziendali (ad esempio, BusinessObjects, iReport), che si interfacciano direttamente ai database per recuperare i dati, e che producono dei report con grafici altamente professionali. Eppure molti utenti sono abituati ad utilizza-re Microsot Office: principalmente perché lo conoscono e non vogliono rinunciare a utiliz-zarlo. Ma anche perché l’elevata diffusione di Microsoft Office garantisce che i file prodotti siano facilmente accessibili e modificabili. Inoltre, l’installazione di software aggiuntivo è un’operazione non sempre vista di buon occhio dagli utenti ed è anche proibita in molti contesti aziendali.Automatizzare la generazione di questi docu-menti con .NET non presenta alcuna difficoltà. Fare altrettanto tramite Java è più complesso, ed ha delle limitazioni che dipendono dal pro-dotto che avete deciso di utilizzare per interfac-ciarvi a questi documentiEsistono principalmente due approcci, utilizza-

ti dalle librerie per accedere ai dati dei fogli di Excel:

• tramite uno strato API interamente Java • tramite un bridge per le API native di Micro-

soft.

I due prodotti che descriviamo in questo arti-colo sono Jakarta POI e JACOB: POI sfrutta l’ap-proccio Java mentre JACOB sfrutta l’approccio nativo.

Jakarta POI

Il progetto nasce nel 2001 dall’esigenza di Andrew Oliver di accedere ai file di Excel con uno strumento Open Source in Java. Nel corso degli anni, le librerie POI sono diventate le più note e complete sul mercato dell’Open Source; oggi sono un punto di riferimento in materia. Quando Oliver si imbatté per la prima volta nel formato “Microsoft’s OLE 2 Compound “ dei documenti Office, si rese conto che questo formato non è altro che la trasposizione di par-te di un file system su singolo file. Da questa constatazione ebbe origine l’acronimo POI: Poor Obfuscation Implementation (che tradotto, al-l’incirca suona come “scarsa implementazione di una offuscazione”).

Il progetto POI è formato da un insieme di com-ponenti:

• POIFS è il cuore del progetto, ed è l’infra-struttura per leggere e scrivere OLE 2 Com-pound Documents; tutti i componenti fanno uso di questo componente. Tramite POIFS si possono accedere alle informazioni dello

Microsoft Excel è un applicativo molto usato, e quindi occorre “conviverci”, anche se siete program-matori Java e amate la portabilità e l’apertura. Leggendo questo articolo, se i clienti vi chiederanno l’importazione e l’esportazione di dati in formato Excel, saprete come fare

>> di Luigi Zanderighi

Page 64: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 200764

focus

pseudo-filesystem del formato OLE 2 Compond;• HSSF (che è poi l’acronimo di Horrible Spreadsheet

Format), è il porting Java delle librerie di accesso ad Excel. Sono supportate le operazioni di lettura e scrittura. Dal nome che è stato dato alle librerie risulta evidente che Oliver considerava il formato Excel assolutamente inadatto e ostico, di fatto or-ribile.

• HWPF è il porting Java delle API per accedere ai documenti Word. Sono supportate le operazioni base di lettura e scrittura. HWPF è l’acronimo di Horrible Word Processor Format.

• HPSF segue la scia degli acronimi precedenti ed è l’abbreviazione di Horrible Property Set Format. For-nisce le API di supporto per l’accesso ai summary dei documenti office

• DDF è l’ultimo componete del progetto POI è con-sente la decodifica dei formati Drawing di Office, in pratica fornisce il supporto per accedere ai gra-fici. Questa volta l’acronimo è più originale, anche se non nasconde una nota di biasimo nei confronti del formato: Dreadful (terribile) Drawing Format

Vediamo ora come si utilizzano queste librerie per risolvere il problema che avevamo in origine: leggere e scrivere un file excel.

Scrivere un file Excel

Iniziamo a creare un documento Excel, così da poter-lo leggere in seguito. Le API sono molto intuitive ed autoesplicative. Non è necessario assegnare subito un nome al file in fase di creazione, quest’operazione può essere rimandata all’atto del salvataggio del file su disco:

HSSFWorkbook wb = new HSSFWorkbook();

A questo punto possiamo creare i worksheet, le righe e le celle del documento Excel. Ad ogni foglio, riga e cella è associato un indice che equivale alla posizione in cui si vuole creare l’oggetto:

HSSFSheet sheet = wb.createSheet();HSSFRow row = sheet.createRow((short)0);

//prima creiamo la cella HSSFCell cell = row.createCell((short)0); //poi impostiamo il valore cell.setCellValue(1); //eseguiamo l’impostazione del // valore contestualmente alla creazione row.createCell((short)1).setCellValue(1.2); row.createCell((short)2).setCellValue(“Testo libero”);row.createCell((short)5).setCellValue(true);

Supponiamo di voler formattare e celle in modo par-ticolare: anche in questo caso le librerie POI fornisco-no un supporto semplice da usare:

//creiamo lo stile da applicare //e impostiamo le sue proprietàHSSFCellStyle style = workbook.createCellStyle();style.setDataFormat( HSSFDataFormat.getBuiltinFormat(“($#,##0_)”));style.setFillBackgroundColor(HSSFColor.AQUA.index);style.setFillPattern(HSSFCellStyle.BIG_SPOTS);//applichiamo lo stile su una cellacell.setCellStyle(style);

L’ultima cosa che rimane da fare è scrivere il file su disco:

FileOutputStream fileOut = new FileOutputStream(“c:\\temp\\test.xls”);wb.write(fileOut);fileOut.close();

Leggere il file di Excel

Il file appena creato può essere letto anche con Microsoft Office, ma noi proviamo a leggerlo utiliz-zando nuovamente le librerie POI. La lettura risulta ancora più semplice della scrittura, e fino all’accesso a livello di cella non c’è bisogno di alcun commento, il codice parla da sé:

FileInputStream fis = new FileInputStream(“c:/temp/test.xls”);HSSFWorkbook wb = new HSSFWorkbook(fis);HSSFSheet sheet = wb.getSheetAt(0);HSSFRow row = sheet.getRow(0);HSSFCell cell = row.getCell(0);

Per leggere il valore della cella occorre qualche spiegazione sul formato di memorizzazione: infatti, Excel prevede una tipizzazione dei valori delle celle, che è diverso da quello a cui siamo abituati nei data-base relazionali o in Java. Questi tipi sono codificati nell’oggetto HSSFCell e sono:

• formula• numeric • boolean• string• blank• error

Java ha le carte in

regola per competere

anche nell’automazione

dei documenti Microsoft

Office

Page 65: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 2007 65

focus

Una corrispondenza con i tipi Java è riportata nella Tabella 1. Per ottenere il contenuto della cella oc-corre prima verificare il tipo Excel del contenuto, in modo da richiamare il metodo corretto. Nel Listato 1 è presente il codice che mostra il contenuto delle celle di una riga. L’output che otterremo sarà del tipo:

Il contenuto della cella 2 è :This is a stringIl contenuto della cella 4 è :trueIl contenuto della cella 1 è :1.2Il contenuto della cella 0 è :1.0

JACOB

Le librerie JACOB seguono un approccio diame-tralmente opposto a quello di POI. Infatti vengono sfruttate le librerie native per accedere ai documenti Office: JACOB è infatti l’acronimo di JAva COrba Bri-dge, progetto ideato e mantenuto da Dan Adler.Il progetto è composto da due componenti:

• la libreria nativa jacob.dll, che va copiata in una cartella nel path di Java (ad. es nella directory bin sotto la JAVA_HOME),

• la seconda libreria è un classico jar.

Come si può notare, l’approccio JACOB non è por-tabile: le applicazioni sviluppate con JACOB girano solo su macchine Windows su cui è installato Micro-soft Office. Il vantaggio di JACOB risiede proprio nel fatto di appoggiarsi alle librerie native, disponendo quindi di una maggior completezza in termini di funzionalità e di compatibilità di formato (soprat-tutto per il supporto di macro o di grafici complessi). Tramite JACOB, infatti, si possono invocare macro presenti nei file; mentre le librerie POI, nelle mie prove, spesso corrompono le macro durante le ope-razioni di lettura/scrittura.Chi ha avuto esperienze con la programmazione in Visual Basic si trova leggermente avvantaggiato ad utilizzare JACOB: infatti, vengono riproposti alcuni concetti ben noti, come l’oggetto di tipo Variant. I metodi che possono essere invocati sono gli stessi delle librerie Microsoft. A questo punto non resta che provare le librerie creando un file Excel. Il primo passo è aprire in modalità nascosta l’applicazione Excel tramite il controllo ActiveX:

ActiveXComponent xls = new ActiveXComponent(“Excel.Application”);

TABELLA 1 Informazioni relative all’HSSFCell

Tipo Excel Tipo Java

date java.util.Date

boolena boolean

numeric double

string java.lang.String

error byte

formula java.lang.String

LISTATO 1 Breve programma che mostra il contenuto delle celle di una riga

Iterator it=row.cellIterator();

while (it.hasNext()) {

cell = (HSSFCell) it.next();

System.out.print(“Il contenuto della cella “+cell.getCellNum()+ “ è :”);

switch (cell.getCellType()) { case HSSFCell.CELL_TYPE_BOOLEAN: System.out.print(cell.getBooleanCellValue()); break; case HSSFCell.CELL_TYPE_NUMERIC: System.out.print(cell.getNumericCellValue()); break; case HSSFCell.CELL_TYPE_STRING: System.out.print(cell.getStringCellValue()); break; case HSSFCell.CELL_TYPE_BLANK: break; case HSSFCell.CELL_TYPE_ERROR: System.out.print(cell.getErrorCellValue()); break; case HSSFCell.CELL_TYPE_FORMULA: System.out.print(cell.getCellFormula()); break; }

System.out.println(“”);}

Page 66: j2007 01 jj2

JAVA Journal

n.2 - gennaio/febbraio 200766

focus

Una volta aperto Excel dobbiamo creare il nuo-vo Workbook. Questo lo facciamo recuperando l’elenco dei workbook aperti da Excel e aggiun-gendone uno:

Dispatch workbooks = xls.getProperty(“Workbooks”).toDispatch();Dispatch workbook = Dispatch.get(workbooks,”Add”).toDispatch();

A questo punto, soffermiamoci su queste righe di codice appena scritte. Come possiamo notare dall’ultima riga, l’invocazione dei metodi è molto particolare. Per eseguire le operazioni sulle API na-tive di Office, si deve infatti passare, praticamente sempre, attraverso i metodi statici della classe Di-spatch. Questi metodi prendono sempre come primo parametro l’oggetto Dispatch su cui si sta agendo; e come secondo parametro il nome del metodo o della proprietà su cui si agisce. Infine i parametri suc-cessivi verranno passati alle librerie native. L’even-tuale valore restituito dai metodi è di tipo Variant, come nella migliore delle tradizioni Visual Basic. Se si vuole interagire con il Variant, per richiamare metodi o leggere/impostare proprietà, occorre recu-perare il Dispatch di quel Variant con il metodo to-Dispatch(). I principali metodi messi a disposizione dal Dispatch sono i seguenti:

public static Variant call( Dispatch dispatchTarget, String methodName,Objecti obji,...);

public static Variant get( Dispatch dispatchTarget,String name);

public static put( Dispatch dispatchTarget, String name,Object val);

Il metodo call permette di invocare il metodo nativo methodName sull’oggetto dispatchTarget, passandogli n parametri definiti dagli oggetti obji. I due metodi put

e get consentono di leggere e scrivere le proprietà di un oggetto.L’ultimo metodo che vediamo è il metodo invoke, che consente di chiamare i sottostanti metodi call,get e put passando un numero arbitrario di parametri:

public static Variant invoke( Dispatch dispatchTarget, String name,int wFlag, Object[] args,int[] errors);

I parametri vengono passati attraverso args, mentre il flag wFlag consente di selezionare la tipologia di operazione da invocare: Dispatch.Get, Dispatch.Put e Dispatch.Method.Con le nozioni appena acquisite siamo in grado di creare uno foglio di calcolo e di popolarne le celle (Listato 2).

Riesaminiamo il codice che abbiamo scritto. La prima riga recupera la proprietà ActiveSheet; dal workbook, sullo sheet recuperato, viene invocato il metodo Range, passando come parametro il nome della cella da modificare. Nelle ultime due righe impostiamo la proprietà Value della cella con il valore 123.456 men-tre nella cella a2 impostiamo la proprietà Formula.Ora possiamo chiudere il documento Excel appena creato:

Variant f = new Variant(false);Dispatch.call(workbook, “Close”, f);xl.invoke(“Quit”, new Variant[] {});

Come si può vedere, il codice scritto per JACOB è poco intuitivo e leggibile; soprattutto, in fase di com-pilazione non può essere fatta alcuna verifica sulla correttezza delle chiamate ai metodi nativi COM. Tutto “passa” attraverso metodi molto generici. Vi-ceversa, da un punto di vista funzionale il supporto JACOB è molto più completo e la compatibilità dei documenti è totale.

Conclusioni

A questo punto è bene capire quando usare Jakarta POI e quando usare JACOB. Sicuramente l’approc-cio POI è portabile e semplice, ma pone delle forti limitazioni. Va quindi usato quando non ci sono di mezzo macro, formule complesse o grafici. Inoltre, se sono presenti delle macro, i file possono corrompersi. L’approccio JACOB vi lega all’ambiente: spesso non è un grosso problema, se l’utente ha necessità di otte-nere file in formato Excel lo fa proprio perché lavora con Office, e la portabilità non è un prerequisito a cui è interessato.

Note Biografiche

Luigi Zanderighi Ingegnere del software esperto nella progetta-zione di architetture distribuite e software Java

LISTATO 2 Come creare uno spreadsheet e

popolare le sue celle

Dispatch sheet = Dispatch.get(workbook,”ActiveShee

t”).toDispatch();

Dispatch a1 = Dispatch.invoke(sheet, “Range”,

Dispatch.Get,

new Object[] {“A1”}, new int[1]).toDispatch();

Dispatch a2 = Dispatch.invoke(sheet, “Range”,

Dispatch.Get,

new Object[] {“A2”}, new int[1]).toDispatch();

Dispatch.put(a1, “Value”, “123.456”);

Dispatch.put(a2, “Formula”, “=A1*2”);

}

Page 67: j2007 01 jj2
Page 68: j2007 01 jj2