COSTRUISCI IL TUO FIREFOX
PERSONALIZZA FACILMENTE IL BROWSER PIÙ IN
VOGA DEL MOMENTO CON IL LINGU
Rivista + Le Grandi Guide di ioProgrammo n° 1 o n° 2 a € 14,90 in più
RIVISTA+CD €6,90
ROGRAMMO
PER ESPERTI E PRINCIPIANTI
IL CELLULARE RISPONDE PER TE E.,
REGISTRA
LA VOCE
5j
! CREA UNA SEGRETERIA CHE FUNZIONA
I A TELEFONO ACCESO, NON TI FA
TELEFONATE E NON HA COSTI!
Intercetta lo squillo e risponde
in modo automatico
Registra le voce di chi chiama
in formato wav
Ti fa ascoltare con il proprio player
il messaggio registrato
VISUAL BASIC
TE LE SUONA!
Sviluppa il tuo player MP3 o il tuo lettore DVD
e dotalo di funzioni che gli altri non hanno
UN ROBOT PER IRC
Crealo in Java, programmalo, e fagli prendere
il tuo posto nelle chat quando non ci sei
INTERFACCE
MULTIPIATTAFORMA
Programma applicazioni sia per Linux
sia per Windows usando le WxWidgets
JAVA
INTELLIGENZA
ARTIFICIALE
Emula il cervello umano con il PC
istruendo una rete neurale
ile • LUGLIO/AGOSTO 2005 •
IOPROGRAMMO WEB
WEBSERVICE
USALI CON PHP
Sfrutta servizi esistenti,
aggiungili al tuo sito e
impara a crearne di nuovi
PAGINAZIONE
DA RECORD
Rendi veloce il tuo sito
anche quando il numero
di dati è elevato
VIDEOGAMING
PIXEL SHADER
Quando la modellazione
non è un gioco, ecco come i
personaggi diventano veri
GARBAGE
COLLECTION
Sfrutta al massimo la
memoria per rendere la tua
applicazione velocissima
VB.NET
OCR FAI DA TE
Scansiona un documento
e riconosci il testo usando
i componenti di Office
DATAGRID
SU MISURA
Fai diventare le griglie
dei contenitori di
immagini e testo
DATABASE
PERSISTENZA
DEI DATI
Sfrutta la semplicità di
Castor per trasformare
i DB in Classi Java
ALGORITMI
SFRUTTA IL SISTEMA
Concorrenza e semafori,
come non intasare le risorse
INTERVISTA ESCLUSIVA: CHRIS ATWOOD
RESPONSABILE DEL TEAM DI SUN JAVA STUDIO
CI ILLUSTRA LE CARTE VINCENTI DELL'AMBIENTE
EDIZIONI
MASTER
www.edmaster.it y
771128"594009
50093
9 IX - N.ro 7 (93) - Luglio/Agosto 2005 - Periodicità N
Reg. Trib. di CS al n.ro 593 del 11 Febbraio 1997
Cod. ISSN 1128-594X
E-mail: ioprogrammo@edmaster.it
http://www.ioprogrammo.it
Direttore Responsabile: Massimo S
Questo mese su ioProgrammo
.. Buono, D. De Michelis, A. Galeaz
Responsabile grafico: Paolo Cristiano
Illustrazioni: M. Veltri
"Rispettare Tue
parte di tutto ciò che facciamo e di ogni decisione che prendiamo
miglioramento delle performance ambientali e sulla prevenzione
Realizzazione Multimediale: SET S.r.l.
Coordinamento T
Realizzazione CD-Rom: P<
Pubblicità: Master Advertising s.r.l.
Via C. Correnti, 1 - 20123 Milano
Tel. 02 831212 - Fax 02 83121207
e-mail ai '
Sales Director: Max Scortegagna
Editore: Ecjiz.iui un
Sede di Milano: Via Ariberto, 24 - 20123 Mi
Tel. 02 831213 -Fax 02 83121330
Sede di Rende: C.da Lecco, zona industriale - 87036 Rende (CS)
" ' '-'-- ito:Mas : -- -
ABBONAMENTO E ARRETRATI
nmo (11 numeri) €59,90
j 20% sul prezzo di copertina di €75,90
Offerte valide fino al 30/09/05
ia + €5.32
x allo 02 83121206, oppure v
ZIONI MASTER via C. Correnti, 1 - 20123 Milano, dopo av<
• cc/p n.16821878 o vaglia postale (inviando copia della ricevuta del
• assegno bancario non trasferibile (da inviarsi in busta chiusa insieme
-'la richiesta);
• carta di credito, circuito VISA, CARTASI', MASTERCARD/EUROCARD, tiri-
la Vs. sottoscrizione insieme alla richiesta).
S.p.a. c/c 01 000 000 5000 ABI 03032 CAB 80880 CIN Q (inviando copia
SI PREGA DI UTILIZZARE IL MODULO RICHIESTA ABBONAMENTO POSTO
NELLE PAGINE INTERNE DELLA RIVISTA. L'abbonamento verrà attivato sul
la e nei punti <
postale di i
' wiare il CD-Rom difettoso in busta chiusa a:
ster - Servizio Clienti - Via C. Correnti, 1 - 20123 Milai
Assistenza tecnica: ioprogrammo@edmaster.it
02 831212
servizioabbonati@edmaster.it
Stampa: Rotoeffe Via Variante di Cancelliera, 2/6 - Ariccia (Roma)
Bisignano (CS)
Distributore esclusivo per l'Italia: Parrini & C S.p.A.
Via Vitorchiano, 81 - Roma
autorizzazione scritta della Edizioni Master. Manoscritti e foto originali.
in alcun caso responsabile per i danni diretti e/o indiretti derivanti
allegato alla rivista e/o per eventuali anomalie degli stessi. Nessuna
bilità è, in - ' ■-»-'■■■■ ■■•»« ,...•..
derivanti da virus informatici non riconosciuti dagli antivirus ufficiali
citati senza indicare i relativi brevetti.
Edizioni Master edita: Computer Bild, Idea Web,
DVD Magazine, Office Magazine, La mia Barca, ioProgrammo,
m WÈBB SU
Win Extra, Home entertainment, Digital Japan, Digital Music, Horror
V '^ujO^ J L'Universo Tecnologico I
ficaio n.5330 Ilei 2/1 2/2004 www.itpartal.it
T Programmazione
Embedded
A molti non sfuggirà la presentazione della
scheda Fox Micro Linux System che vi
proponiamo nelle pagine di questo stesso nu-
mero. Non voglio parlarvene ancora nell'edi-
toriale per sottolinearne il valore assoluto, che
pure rimane alto, ma per puntare l'indice su
quello che è il futuro della programmazione.
Fino ad ora abbiamo visto un'evoluzione del
software dalle interfacce visuali a quelle grafi-
che e infine al Web. È tempo di un ulteriore
passo avanti, a farsi largo sono i sistemi em-
bedded. Non parliamo solo di telefonini, pal-
mari e pocket PC, ma di tutta una serie di pe-
riferiche che a vario titolo contengono un
processore programmabile. Parliamo di pro-
grammazione di controlli per l'automazione
industriale come di domotica, parliamo di
automobili come di aeroplani, parliamo di
robot come di applicazioni per il controllo dei
dati. È un nuovo modo di programmare verso
cui dobbiamo tendere, è un mercato verso il
quale noi programmatori dobbiamo indiriz-
zarci se vogliamo restare al passo con i tempi.
Perciò iniziate a sviluppare piccole applica-
zioni non standalone e non dedicate al web,
sicuramente in breve tempo sarete in grado di
proporre ai vostri clienti tutta una serie di so-
luzioni che al momento sono impensabili.
Noi di ioProgrammo non mancheremo, come
sempre, di supportarvi lungo questa strada,
mostrandovi come essa sia percorribile, così
come abbiamo fatto quando era tempo di
aiutarvi a sviluppare interfacce grafiche, così
come abbiamo fatto quando vi abbiamo aiu-
tato a programmare il Web, così come ancora
faremo ogni volta che una nuova sfida si pro-
porrà al mondo in cui tanto crediamo: quello
della programmazione.
Fabio Farnesi
All'inizio di ogni articolo, troverete un simbolo che indicherà la presenza di
codice e/o software allegato, che saranno presenti sia sul CD (nella posizione
di sempre \soft\codice\ e \soft\tools\) sia sul Web, all'indirizzo
http://cdrom.ioprogrammo.it.
Per scaricare software e codice da Internet, ogni mese indicheremo una
password differente. Per il numero che avete fra le mani la combinazione è:
Username: mare
Password: barca
SPECIALE CELLULARE
RISPONDE PER TE
E REGISTRA LA VOCE
CREA UNA SEGRETERIA CHE FUNZIONA
A TELEFONO ACCESO, NON TI FA PERDERE
LE TELEFONATE E NON HA COSTI!
Intercetta lo squillo
del chiamante e rispondi
in modo automatico
Registra le voce di chi ti chiama
in formato wav sul cellulare
Ascolta il messaggio registrato
con il player del telefonino
pag. 14
Questo mese su ioProgrammo
IL PIXEL CHE TI CAMBIA
I CONNOTATI
Alla scoperta del Pixel Shading, per realizzare animazioni
vicine alla qualità cinematografica
MMMmmmi*
Usiamo i Web
services da PrlP Pa9 20
Cosa sono e come funzionano i web
services? Impariamo a utilizzarli
e creiamone uno
SISTEMA
Una super cache
per i nostri dati pag. 24
Aggiriamo i colli di bottiglia imposti dai
limiti d'accesso ai database, tramite
meccanismi di caching dei dati in Java
Firefox, il browser
estensibile pag. 28
Creare un'estensione per Firefox con XUL il
linguaggio derivato da XML utilizzato per
interfacce grafiche di grande effetto
Un File System portatile
in Java pag. 32
Costruiamo un'applicazione che utilizza
internet come un grande disco virtuale.
Creiamo un OCR
con Visual Basic .NET pag. 38
Usiamo alcune librerie di Office 2003 per
creare un modulo per il completo
riconoscimento del testo
NETWORKING
Java PircBot, la chat è fatta! . pag.
Costruiamo un robot da utilizzare nella
gestione di un canale IRC usando Java
42
GRAFICA
Le immagini le faccio
alla griglia pag. 52
Creiamo una galleria di thumbnail mostrando
le immagini nelle celle di un DataGrid
RUBRICHE
Gli allegati di ioProgrammo
// software in allegato alla rivista
pag. 6
News pag. 8
le più importanti novità del mondo della program-
mazione
La posta dei lettori pag. 10
L'esperto risponde ai vostri quesiti
Il meglio dei newsgroup pag. 12
ioProgrammo raccoglie per voi le discussioni
più interessanti della rete
Paginazione su milioni di record
in SQL Server pag. 56
Costruiremo la pagina di ricerca di un sito
e-commerce che può contare su di un'anagra-
fica di un milione di articoli.
VISUAL BASIC
Un player audio Pag .6o
Vi guideremo all'uso del controllo
"media control interface" per la
gestione di suoni e filmati. Infine
realizzeremo un'applicazione
completa per la riproduzione di CD
edMP3
BACKSTAGE
Reti Neurali PC
che pensano pag. 68
In questo articolo "addestreremo" un
computer a pensare...
ADVANCED EDITION
Far funzionare bene
la memoria pag. 72
La gestione della memoria, con l'avvento
dei meccanismi di gestione automatica,
non è più un problema, sarà il garbage
collector a fare il lavoro sporco
Castor il gemello minore
di Hibernate pag. 79
Realizziamo applicazioni Java che fanno
uso di database. Affidiamo tutta la gestione
a Castor per poterci dedicare agli oggetti
Codice C++ per Windows
e Linux pag.
Grazie a wxWidgets è possibile scrivere
codice che funzioni su tutti i sistemi
84
Tips & Tricks pag. 106
Trucchi per risolvere i problemi più comuni
Express pag. 108
le guide passo passo per realizzare applicazioni
senza problemi
Software pag. 116
/ contenuti del CD allegato ad ioProgrammo.
Corredati spesso di tutorial e guida all'uso
Biblioteca pag. 130
/ migliori testi scelti dalla redazione
pag. 48
CORSI
Visual Basic.NET • Una form per
ogni finestra pag. 89
Impariamo a utilizzare al meglio le Windows
Formai di quali proprietà godono e come
gestirle senza problemi
Asp.NET • I web controis . . . pag. 94
Scegliere il web control, senza conoscerne da
vicino le funzionalità, può essere un'operazio-
ne non priva di difficoltà. Con una guida come
questa, districarsi tra tutte le possibilità può
diventare più semplice
Javascript • Usare gli Array alle
basi del codice! pag. 98
Diremo qualcosa su una delle strutture dati
che costituisce le fondamenta di ogni linguag-
gio di programmazione, impareremo come
utilizzarla all'interno di JavaScript
Symbìan • BlueTooth il re
della comunicazione pag. 102
Sfruttiamo il Nokia SDK per creare applicazio-
ni che dialogano fra loro
SOLUZIONI
Processi concorrenti
con i semafori pag. 126
Cosa succede quando due applicazioni
tentano di accedere alla medesima risorsa?
SPECIAL
Sun Java Studio
Enterprise m .n 5
Abbiamo intervistato Chris Atwood il
team manager di Sun Java Studio,
a proposito delle novità del nuovo
ambiente .
Qj5
http://forum.ioprogrammo
D
QUALCHE CONSIGLIO UTILE
I nostri articoli si sforzano di essere
comprensibili a tutti coloro che ci
seguono. Nel caso in cui abbiate
difficoltà nel comprendere esattamente
il senso di una spiegazione tecnica, è
utile aprire il codice allegato all'articolo
e seguire passo passo quanto viene
spiegato tenendo d'occhio l'intero
progetto. Spesso per questioni di spazio
non possiamo inserire il codice nella sua
interezza nel corpo dell'articolo. Ci
limitiamo a inserire le parti necessarie
alla stretta comprensione della tecnica.
Gli allegati di ioProgrammo
i(M
COSTRUISCI IL JTUO FIRE FOX
PERSONALIZZA FACILMENTE, L BgOWSERPIU M
ROGRAMMO
r j-| i i^-jj : ..H -j^'.'ijNM,'ni B
IL CELLULARE RISPONDE PER TEE.
REGISTRA
LA VOCE
mzzmEmm
WEBSERVICE
USALI CON PHP
Sfrutta se '" '
CREA UHI» stime i cn.« *™- ■ -
A TELEFONO ACCESO. NON TI FA PERDERE
LE TELEFONATE E NON HA COSTI!
■ Intercetta lo squillo e risponde
in modo automatico
■ Registra le voce di chi chiama
in formato wav
■ Ti fa ascoltare con il proprio player
il messaggio registrato
VISUAL BASIC
E SUONA!
jlayer MP3 o il tuo lettore DVD
che gli altri non hanno
SOBOT PER IRC
%. programmalo, e fagli prendere
■ t quando non ci sei
PERSISTENZA
DEI DATI
Sfrutta la semplicità di
Castor per trasformare
i DB in Classi Java
RIVISTA + CD-ROM
in edicola
IL SOFTWARE DEL MESE
Questo mese con
ioProgrammo trovi Sybase
PocketBuilder, la soluzione
per lo sviluppo RAD
di applicazioni dedicate
al mondo Mobile
Si tratta di un prodotto innovativo, da provare per
quanti intendono dedicarsi ai nuovi device, intesi come
Pocket PC
o Smartphone.
Il nome del
produttore: Sybase
é una garanzia
di affidabilità,
e certifica anche
un'alta integrazione
con applicazioni
di databse.
Se pensate
che il Mobile sia
un settore emergente
non potete fare
a meno di provarlo.
Sm«r| SYBASE
POCKETBUILDER
I contenuti
del CD-Rom
Apache 1.3.33/2.0.54
Il Web Server più usato al mondo
Directory: /Apache
Castor 0.9.6
Un tool di persistenza dei dati
leggero
Directory: /Castor
Commons Collection 3. 1
Aumenta la velocità di sviluppo
delle applicazioni Java
Directory /CommonCollections
Commons HttpCIient 3.0
Accedere al web via Java
Directory /CommonHttpCIient
Commons Net 1.4.0
Internet lato client sotto con-
trollo
Directory /CommonNet
Commons Oro 2.0.8
Espressioni regolari anche
in Java
Directory /CommonsOro
Commons VFS
Supporto universale
a qualunque tipo di File System
Directory /CommonsVFS
DbaMGR2k
Il gestore di database MSDE
Directory /DBAmgr2K
Dev C++ 4.9.9.2
Il più amato dai programmatori
C++
Directory /DevCPP
DevPHP
Programmazione PHP facilitata
Directory /DevPHP
Eclipse Sdk 3.0.2
L'I DE di programmazione uni-
versale
Directory /Eclipse
Encache 1.1
Una cache per le applicazioni
Java
Directory /encache
Irrlicht0.9
La libreria per lo sviluppo rapi-
do di VideoGames
Directory /irrlicht
J2SE 1.5.0_03
L'sdk essenziale per program-
mare in Java
Directory /J2SE
Jakarta SLIDE WebDavCLient
Le librerie Java per il supporto
a Web Da v
Directory /jakartaslide
JSH 0.1.2.0
Una shell per Java.
Directory /JSH
Samba e CIFS in un un'unica libreria
Una libreria per accedere
a Samba da Java
Directory /J e ifs
Lazarus 0.9.6
Quasi come Delphi ma Gratis
Directory /Lazarus
MySQL 4.11/5.0.4
Il principe dei database
Directory /MySQL
PHP 4.3.1 1-5.0.4
Il linguaggio di internet
Directory /PHP
Ultimate ++
L'IDE più innovativo per C++
Directory /Ultimatepp
Python 2.4.1.
Il linguaggio emergente
Directory /Python
SharpDevelop 1.0.3
L'ambiente alternativo per la
programmazione C#
Directory /SharpDevelop
Pircbot
L'ire programmabile in Java
Directory /Pircbot
ThingO.1
L'editor di Thinlet
Directory /Thing
Tomcat 5.5.9
L 'application server per JSP
Directory /Tomcat
Jakarta Commons Logging
Le api essenziali per il
Debugging
Directory /logging
Snarli
La libreria Java per lo sviluppo
di reti neurali
Directory: /Snarli
Xerces 1.4.4
Il parser XML per Java e C+
Directory: /Xerces
Xalan
Il processore XSLT per Java e
C++
Directory: /xalan
1.1.1 beta Win32
Un sistema dì reportistica
completa ed OpenSource.
Directory: /openrpt
Dev C++ Pack
Tre package per estendere Dev
C++
directory: /devpack
wxDevCpp
un Dev C++ potenziato
Directory: /wxWidgets
* 6 /Luglio-Agosto 2005
http://www.ioprogrammo.it
NEWS T
WINDOWS
MEDIA CENTER
IMELLXBOX 360
La prossima console di
Microsoft, FXBox 360,
integrerà il sistema opera-
tivo Windows Media Center
2005. L'integrazione di que-
sto strumento con la già po-
tente console di Microsoft
rappresenta un ulteriore
passo avanti nella conver-
genza dei mercati destinati
all'Home Entertainment. In
realtà una prima integra-
zione con il Windows Me-
dia Center è stata già ten-
tata con la versione at-
tuale, ma il passaggio alla
nuova release del sistema
dovrebbe garantire presta-
zioni più elevate e una mag-
giore interattività.
F-SECURE
PORTA
L'ANTIVIRUS
SUI CELLULARI
La grande diffusione di
sistemi mobili ha porta-
to, parallelamente, alla dif-
fusione di virus e sistemi di
intrusione dedicati a cellu-
lari e Smartphone.
Fra i primi a scendere in
campo con un prodotto de-
dicato alla protezione di
questi dispositivi da attac-
chi esterni è stata la finlan-
dese F-Secure. Il mobile an-
tivirus si installa sugli
smartphone proteggendoli
da contenuti dannosi, inol-
tre è facilmente aggiornabi-
le tramite un sistema auto-
matico. F-Secure dispone
così di una gamma comple-
ta di prodotti dedicati alla
sicurezza per tutte le piatta-
forme, dai sistemi dedicati
alle aziende, all'uso per-
sonale e ora anche sistemi
per la protezione del mobile
sia lato provider che utente
finale, entrando così alla
grande in un mercato tutto
da scoprire.
nSTO IL GOSSIP DI IIS7
DIECI ANNI
DI JAVA
i è svolta il 22 e il 23 Giu-
gno 2005 a Milano la de-
cima "Java Conference",
evento annuale promosso
da Sun e rivolto ai profes-
sionisti dell'IT e ai vertici
aziendali che desiderano
stare al passo con le ten-
denze di settore. I numeri di
Java sono impressionanti:
Il à lava
~ Conference
HAPFYBKIHDnfl
quattro milioni e mezzo
sono gli sviluppatori che uti-
lizzano il linguaggio di Sun.
Java è assolutamente il lea-
der indiscusso del settore
della telefonia mobile, dei
palmari Java enabled e in
generale di tutto il seg-
mento dei sistemi embed-
ded. Per quanto riguarda il
Web la stima giornaliera di
utilizzo di Java dai parte dei
Surfer è di circa il 90%.
Questi numeri testimoniano
un successo spesso poco gri-
dato ma che nei fatti assu-
me proporzioni notevoli. Il
tema della Conference di
quest'anno è stato l'Open-
Source, segno evidente del-
l'attenzione che Sun pone
nei confronti della di-
sponibilità del codice sor-
gente, nella creazione degli
standard e nella volontà di
mettere in grado gli svi-
luppatori di poter cooperare
fra loro.
La posizione di Sun rispetto
all'OpenSource è chiara: si
tratta del propulsore più ef-
ficiente per promuovere
l'innovazione e lo sviluppo
non solo da un punto di
vista strettamente tecnolo-
gico ma anche da un punto
di vista economico.
7'nternet Information Server
è il prodotto di Microsoft
che aspira a conquistare posi-
zioni di rilievo nel settore dei
prodotti dedicati alla gestione
di Applicazioni Web. Come
già il suo rivale Apache, US si
presenta come un prodotto
completo che non contiene
solo la logica di gestione delle
pagine HTML, piuttosto si
propone come container di
applicazioni il cui sottostrato
naturale è la rete. A differenza
di Apache, US focalizza le sue
caratteristiche su applicazio-
ni basate sull' ormai arcinota
piattaforma .NET.
I programmatori, soprattuto
quelli orientati alla program-
mazione di Web Application
sono rimasti freddi di fronte
all'arrivo di .NET prima e di
IIS6 suo naturale container
dopo. Nonostante questo,
Microsoft sta già lavorando
alla versione 7 di Internet In-
formation Server, il cui core
sembra essere proprio una
maggiore integrazione con
Asp .NET. L'altra novità che
sembra emergere dai pette-
golezzi circolanti nei labo-
ratori della società di Bill Ga-
tes è relativa ad una maggiore
modularizzazione di US, che
dovrebbe diminuire il rischio
di attacchi esterni. Queste
due novità nella loro sempli-
cità potrebbero non rappre-
sentare un salto sufficiente a
giustificare un cambio di ver-
sione così marcato, ci sono
dunque altre caratteristiche
che al momento non trapela-
no dalle bocche cucite dei
progettisti di casa Microsoft.
Tuttavia alcune altre indiscre-
zioni sembrano indicare un
cambio anche nel sistema di
EXCEL SOTTO ACCUSA
Il sig. Carlos Amado
Amado, ex studente di
Stanford, avrebbe sporto
denuncia contro Micosoft
per l'uso indebito di un
brevetto da lui depositato
nel 1990 per collegare
Excel ad Access attraverso
un unico foglio di calcolo.
Secondo quanto riportato
da Amado nel 1992 avreb-
be tentato senza successo
di vendere il brevetto a
Microsoft, che invece se
ne sarebbe appropriata
indebitamente poco più
tardi. Nel caso di accerta-
ta violazione, la valu-
tazione del danno am-
monterebbe a circa 2 euro
per copia di Office ven-
duta, per un totale di circa
cinquecento milioni di
dollari. Naturalmente Mi-
crosoft ha negato di avere
in alcun modo utilizzato il
brevetto di Amado, al con-
trario non si è per niente
scomposta. Il legale del
gigante di Redmond: Joel
Freed ha dichiarato che
Microsoft aveva iniziato a
lavorare a questa tec-
nologia già tre anni prima,
cioè nel 1989 in tempi
assolutamente non so-
spetti. D'altra parte la so-
cietà di Gates è abituata a
questo genere di contro-
versie legali. Con questa
di Amado, salirebbero a 34
le contese a cui il gruppo
sta facendo fronte in rela-
zione a violazione di bre-
vetti software.
A margine di questa
vicenda c'è da sottolinea-
re che in casi del genere é
davvero difficoltoso stabi-
lire una regola certa sulla
base della quale emettere
un giudizio.
y 8 /Luglio-Agosto 2005
http://www.ioprogrammo.it
NEWS T
configurazione di IIS7. Se fino a IIS6
eravamo abituati a fare i conti con
una sorta di file registry piuttosto
complesso, in IIS7 tutta la gestione
sembra essere demandata a file XML
ai quali però verrà affiancato un
sistema di criptazione dei dati che li
proteggerà da occhi indiscreti. L'idea
comunque è quella di fornire agli svi-
luppatori dei metodi che gli con-
sentano di settare le applicazioni se-
condo le loro esigenze senza per que-
sto dovere passare attraverso l'ammi-
nistratore del sistema.
Non si hanno ancora notizie certe
circa l'arrivo sul mercato di questo
nuovo prodotto della famiglia Micro-
soft, ma alcune altre informazioni
potrebbero venire fuori per la fine
dell'anno. Se da un lato IIS7 sicura-
mente presenterà delle novità impor-
tanti rispetto al suo predecessore, la
vera battaglia si gioca sulla diffusione
della piattaforma .NET. ovvero sulla
volontà dei programmatori di com-
piere una migrazione che si sta ri-
velando più dolorosa di quanto non
ci si aspettasse.
GOOGLE DIVENTA
PERSONALIZZATO
E disponibile all'indirizzo www.google
.com/ig il nuovo servizio di Google
che consente di personalizzare l'home
page con dei contenuti prelevati dai ser-
vizi più diffusi. Al momento è possibile
visualizzare nella propria Home Page
un'anteprima della Gmail se ne possede-
te una, le ultime notizie da news.goo-
gle.com, oppure gli ultimi aggiornamenti
di Wired oppure Slashdot. La parte eco-
nomica/finanziaria e comunque più di
cronaca è salvaguardata dalla
presenza del New York Times e di
BBC News, nel tempo probabil-
mente verranno aggiunti ulterio-
ri servizi. E questo è l'ennesimo
servizio che gli uomini del Mar-
keting di Google prima e i loro
sapienti programmatori dopo,
mettono a disposizione del pub-
blico della rete. La continua ere-
PRIME INDISCREZIONI
SU OFFICE 12
Il Gossip di stanpo Microsoftiano
ha iniziato a mormorare circa il
rilascio di Office 12, l'attesa nuova
versione della suite più diffusa al
mondo. Sembrerebbe che il nuovo
nato debba vedere la luce nell'esta-
te del 2006, anche se nessuna con-
ferma ufficiale è arrivata dallo staff
di Microsoft. Tuttavia si mormora
che secondo il calendario interno
del team di sviluppo la prima beta
dovrebbe essere disponibile per la
fine di Agosto 2005, le successive
beta release sono previste per
Dicembre 2005 e Maggio 2006 per
poi arrivare al rilascio definitivo per
Luglio 2006. Nessuna dichiarazione
rispetto a queste date è stata rila-
sciata da Microsoft, che si limita sol-
tanto ad affermare che Office 12
segue un ciclo di vita diverso rispet-
to a Longhorn la cui uscita è previ-
sta proprio per il 2006, inoltre la
nuova versione della suite per l'uffi-
cio di Microsoft sarà progettata per
funzionare nel range di sistemi ope-
rativi che va da Windows 2000 fino
a Windows server 2003 e superiori,
anche se probabilmente un nuovo
service pack dovrà essere rilasciato
per consentire ai vecchi sistemi di
far funzionare Office 12. Nessuna
indiscrezione è fuoriuscita circa le
nuove funzionalità di cui il prodotto
sarà dotato. Alcune voci sembrereb-
bero indicare, però, che la novità
principale sarà la presenza di un
nuovo tool per la gestione dei dia-
grammi, che dovrebbe fare capo ad
Avalon che a sua volta è parte di
Longhorn. Questo implicherebbe
una sterzata decisa di Office verso
un target "manageriale" che usereb-
be il sistema come un prodotto inte-
grato per l'intera gestione del ciclo
di business aziendale.
scita del leader fra i motori di ricerca è
davvero notevole ed è forse l'unico colos-
so informatico in grado nel tempo di of-
frire una gamma di servizi completi come
quelli offerti ad esempio da Microsoft.
I due partono da poli diversi, il primo tro-
va le fondamenta in una massiccia pre-
senza sulla rete, il secondo nello sviluppo
del software, ambedue migrano i loro
contenuti verso il polo di attrazione del-
l'altro. Quando avverrà lo scontro?
Google
-■• Getsterted
ìomepage.
Gmail
Google News
Weather
68° F / 20° C
iriz:;
zr::z
h ....„, _,.,,..- ,n n..i,,i. , j
<
Stock Market
j^LL. !:'.' J:: i'. '. : i'.'UJ
STOCKS 3.48 40 03 lO CU 14.1
HOTMAIL
CORTEGGIA
I POSTMASTER
E ancora una volta lo Spam sotto accusa.
Tutti lo combattono, lo reputano a ragio-
ne un male oscuro che rallenta la rete inter-
net intasandola senza ragione. MSN Hotmail,
storico pioniere dei servizi di posta elettroni-
ca gratuiti ha implementato un tale numero
di filtri e protezioni che adesso ha sentito il
bisogno di creare un sito centrale che suppor-
ti i postmaster fornendogli informazioni su
come sia possibile inviare posta agli utenti
hotmail senza incorrere in problemi. Sul sito
vengono spiegate le caratteristiche dei filtri
utilizzati da MSN contro lo spam e i virus, e
contemporaneamente vengono messi a di-
sposizione degli ISP strumenti per il monito-
ring del traffico di posta da e verso MSN al
fine di ottimizzare gli scambi con Hotmail.
Al di la di quanto utile realmente si dimostre-
rà questo servizio, è interessante notare come
ormai proteggere troppo equivalga quasi a
non proteggere per niente, di fatto l'istituzio-
ne di un servizio del genere indica che buona
parte dei messaggi rivolti a utenti Hotmail
generano dei falsi positivi e finiscono per
essere filtrati anche quando non necessario.
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 9 ►
INBOX
Le risposte alle domande dei lettori
Box
l'esperto risponde...
Chiarezza sulle licenze
Buongiorno, mi chiamo Marco
e sono da sempre un appas-
sionato di tutto ciò che è tecno-
logia. A questa mia passione non
poteva non corrispondere un in-
teresse per la programmazione
che è il motore e il "cervello" di
tutti gli apparati meccanici/elet-
tronici che fanno da spina dorsa-
le alla tecnologia. Come appas-
sionato del progresso ho sempre
creduto nel valore della GPL che
rendendo il sorgente di un soft-
ware disponibili a tutti, mette in
grado, chi ha le capacità, di con-
tribuire in modo massiccio alla
sua evoluzione. Recentemente
però sono nate centinaia di licen-
ze OpenSource o similari.
Potreste fare un po' di chiarezza
su tutte queste licenze?
Marco da Treviso
Gentile Marco, prima di parlare di
licenze è importante fare una
differenza fondamentale più sottile
ma su cui ognuno dovrebbe riflettere
per poi effettuare una scelta di cam-
po, stiamo parlando della differenza
fra il modello Free Software e il
modello OpenSource. Spesso i due
termini sono usati l'uno in sostitu-
zione dell'altro, eppure le differenze
sono tali da influire sulla concezione
della società che ciascuno di noi
dovrebbe formarsi. Tutte e due i
modelli condividono i seguenti punti
basati sulla licenza GPL
• Il software prodotto sotto licenza
GPL può essere copiato e distri-
buito a condizione che ne sia reso
disponibile il codice sorgente
• Chiunque può modificare il codi-
ce di un programma e ridistribui-
re il programma modificato pur-
ché il prodotto così modificato
sia distribuito ancora una volta
sotto licenza GPL, siano evidenti
le modifiche apportate, siano
mantenuti i riferimenti all'autore
e al software originale.
La licenza GPL è sufficientemente
più complessa, una copia integrale è
disponibile all'indirizzo: http://www
.fsf.org/licensing/licenses/gpl.htmly
tuttavia per sottolineare la differenza
fra OpenSource e FreeSoftware i due
punti in questione sono più che suf-
ficienti. È evidente che rispettando la
GPL si ha un effetto a catena che
contrappone un modello di diffusio-
ne del software "libero" a un modello
"proprietario" dove per ottenere il di-
ritto d'uso del software è necessario
corrispondere un compenso all'au-
tore. Inoltre il software proprietario
non può essere modificato in alcun
modo, riprodotto e ridistribuito. Il
movimento denominato Free Soft-
ware utilizza la GPL per una questio-
ne di libertà sostenendo che il soft-
ware aiuta l'umanità a progredire;
pertanto, dovrebbe potere essere
usato da tutti senza limitazioni e do-
vrebbe essere possibile modificarlo
perché questo consente a chi ne ha le
capacità di migliorarlo e contribuire
così al progredire della qualità della
vita. Per il Free Software ci sono delle
basi filosofiche, per cui produrre
applicazioni che adottino la GPL.
Queste basi corrispondono a un mo-
dello di società più libera e non basa-
ta sugli attuali vincoli di "business",
ma su una visione che vede alla base
la solidarietà tra le persone e la capa-
cità di aiutarsi a vicenda. L' Open-
Source viceversa sostiene l'utilizzo
della GPL per un motivo opposto e
cioè perché produrre software utiliz-
zando questo modello è un buon
modo di fare business. L' OpenSour-
ce sostiene che questo tipo di licenza
può tranquillamente sostenere l'eco-
nomia di un'azienda. Pertanto pur
partendo dalle stesse basi, i due mo-
vimenti abbracciano ideali diversi.
Su questa onda si sono prodotte tan-
tissime licenze, molte delle quali de-
rivate dalla GPL. Alcune aderenti alla
filosofia del Free Software, altre a
quella dell'OpenSource. Una lista
abbastanza completa di tutte le li-
cenze è disponibile all'indirizzo
www.fsf.org/licensing/licenses/index_
htmWGNUGPL. In questa pagina le
licenze vengono divise in:
• licenze sul software;
• licenze sulla documentazione.
Queste due tipologie vengono a loro
volta divise in
• licenze compatibili con la GPL.
• licenze incompatibili con la GPL
ma comunque aderenti al Free
Software.
• licenze incompatibili con Free
Software.
In questo modo scopriamo ad esem-
pio che la LGPL (lesser general public
licence) consente di linkare al software
dei moduli non GPL o che la licenza
di Eclipse è incompatibile con la GPL
perché ha al suo interno alcune limi-
tazioni che non la rendono comple-
tamente "Libera". Riassumendo: al di
là delle sigle e dei singoli punti di cia-
scuna licenza, è importante capire se
si vuole aderire a un modello di
società "libero" abbracciando la fi-
losofia del "Free Software" oppure se
si crede comunque in un modello di
società per cui il "Business" ha una
valenza importante. Nel secondo ca-
so abbracciare un modello Open-
Source o proprietario, significherà
semplicemente scegliere un modello
di marketing differente per i vostri
prodotti, nel primo caso invece signi-
ficherà investire in un modello di so-
cietà differente, forse utopico ma si-
curamente diverso. È utile dire a que-
sto proposito, che se Richard Stal-
► 10 /Giugno 2005
http://www.ioprogrammo.it
Le risposte alle domande dei lettori H T INBOX
lmann non avesse creduto in questo
modello di società investendo gran
parte della sua vita in Free Software
Foundation, forse oggi il mondo,
almeno quello informatico, sarebbe
meno alla portata di tutti e più con-
trollato da pochi. Il che forse avrebbe
reso qualche programmatore più
ricco, ma il mondo un po' peggiore.
Informazioni su SPE
Mi sono cimentato da poco
nell'apprendimento di
Python che mi pare un ottimo
linguaggio per software. Sono
in possesso dell'editor SPE sia
quello distribuito nel numero 90
della rivista che l'ultima versio-
ne scaricata dalla rete. Il proble-
ma è che una volta installato
non riesco ad avviarlo: si creano
diversi file che vengono avviati
dal command line di Python (per
altro senza successo) e non
trovo un file .exe. A questo
punto chiedo: sbaglio qualcosa
o devo scaricare l'exe?
Raiken
SPE una volta installato produce
dei file .py, è sufficiente lanciare
spe.py per avviare il programma.
Ovviamente bisogna avere installato
Python. Se dovesse apparire per un
momento il prompt di msdos e poi
scomparire, molto probabilmente
mancano alcune librerie essenziali.
Prima di tutto wxPython, è sufficiente
installarle scaricandole da httpj/www
.wxpython.org/.
Certificare la sicurezza
In qualità di responsabile della
rete di una grossa azienda mi
rendo conto di assumermi rischi
notevoli. Non che non abbia
fiducia nella mia preparazione,
tuttavia mi rendo conto che se
qualcuno violasse la mia rete,
sarebbe un grosso danno per la
mia azienda. Avrei intenzione di
A chi spedire la posta?
Alla nostra redazione arriva
spesso un considerevole nu-
mero di email riguardante temi
specifici. Per consentirci di rispon-
dere velocemente e in modo ade-
guato alle vostre domande ab-
biamo elaborato una FAQ - Fre-
quently Ask Question - o risposte
alle domande frequenti in italiano,
di modo che possiate indirizzare le
vostre richieste in modo mirato.
Problemi sugli
abbonamenti
Se la tua domanda ha a che fare
con una delle seguenti:
• Vorrei abbonarmi alla rivista,
che devo fare?
• Sono un abbonato e non ho
ricevuto la rivista, a chi devo
rivolgermi?
• Sono abbonato ma la posta
non mi consegna regolar-
mente la rivista, a chi devo
rivolgermi?
Contatta abbonamenti@edma-
ster.it specificando che sei inte-
ressato a ioProgrammo. Lascia il
tuo indirizzo email e indica il
numero dal quale vorresti far par-
tire l'abbonamento. Verrai contat-
tato al più presto. Oppure puoi
chiamare lo 02 831212
Problemi sugli allegati
Se riscontri un problema del tipo:
• Ho acquistato ioProgrammo
ed il Cdrom al suo interno
non funziona. Chi me lo
sostituisce?
• Ho acquistato ioProgrammo
ma non ho trovato il cd/dvd
all'interno, come posso otte-
nerlo?
• Vorrei avere alcuni arretrati
di ioProgrammo come fac-
cio?
Contatta servizioclienti@edma-
ster.it
Non dimenticare di specificare il
numero di copertina di
ioProgrammo e la versione: con
libro o senza libro. Oppure telefo-
na allo 02 831212
Assistenza tecnica
Se il tuo problema è un problema
di programmazione del tipo:
• Come faccio a mandare una
mail da PHP?
• Come si instanzia una varia-
bile in c++?
• Come faccio a creare una
paginaASP.NET
o un qualunque altro tipo di pro-
blema relativo a tecniche di pro-
grammazione, esplicita la tua
domanda sul nostro forum:
http://forum.ioprogrammo.it,
uno dei nostri esperti ti rispon-
derà. Le domande più interessan-
ti saranno anche pubblicate in
questa rubrica.
Problemi sul codice
airinterno del CD
Se la tua domanda è la seguente
• Non ho trovato il codice rela-
tivo all'articolo all'interno
del ed
Consulta la nostra sezione down-
load all'indirizzo
http://cdrom.ioprogrammo.it,
nei rari casi in cui il codice colle-
gato ad un articolo non sia pre-
sente nel cdrom, senza dubbio
verrà reso disponibile sul nostro
sito.
Idee e suggerimenti
Se sei un programmatore esperto
e vuoi proporti come articolista
per ioProgrammo, oppure se hai
suggerimenti su come migliorare
la rivista, se vuoi inviarci un truc-
co suggerendolo per la rubrica
tips & tricks invia una email a
ioprogrammo@edmaster.it
stipulare un'assicurazione che
mi copra da eventuali addebiti
nel caso che capiti qualcosa di
imprevisto. Per far questo,
credo però che qualcuno
dovrebbe certificare che il
lavoro da me svolto sia corretto.
Come posso fare?
Francesco
Il problema è sentito, allo stato at-
tuale procurarsi un'assicurazione
contro danni del genere potrebbe es-
sere un'idea ma francamente non
riesco a dire come la normativa assi-
curativa possa relazionarsi al proble-
ma. Posso invece suggerire di visitare
http:/ /www. iscom.gov. it/ocsi.htm ovvero
il sito dell' OCSI - Organismo di certi-
ficazione della sicurezza di sistemi e
prodotti nel settore della tecnologia
della comunicazione e dell'informa-
zione ICT - che si occupa appunto di
certificare la sicurezza delle reti.
Recentemente è anche stato pubbli-
cato un volume che spiega a quali
fattori attenersi per garantire che la
propria configurazione sia adeguata
alla rete da proteggere.
Che cos'è Pione?
Un mio amico mi ha detto che
il miglior sistema di Content
Management Systems al mondo è
Pione. Ho cercato un po' in rete
ma non ho trovato niente che mi
spiegasse come funziona. Mi date
qualche dritta?
Pione è un sistema di CMS basato
su Zope che a sua volta è un
application server basato su Python.
I vantaggi sono innumerevoli, legati
soprattutto alla gestione della sicu-
rezza, ma anche alla facilità di inseri-
mento delle informazioni, viceversa
l'altro lato della medaglia è dovuto
alla complessità del sistema che pre-
vede un'installazione di Zope e un
tempo di apprendimento di Zope
stesso. In reatà questo non deve spa-
ventare oltremodo, esistono infatti
degli installer semplificati che metto-
no in grado anche i meno esperti di
usare lo strumento. Per configurazio-
ni professionali l'impegno richiesto è
sufficientemente maggiore.
http://www.ioprogrammo.it
Luglio-Agosto 2005/11 ►
NEWSGROUP T ■ Forum ioProgrammo
GROUP
Le informazioni nella Rete
Direttamente dal forum di ioProgrammo http://forum.ioprogrammo, le discussioni più "Hot" del momento
Chiudere un'applicazione
Avrei la necessità di chiudere
un'applicazione in relazione
ad un evento, ad esempio se non
è possibile leggere un file di
configurazione. Come posso
fare?
Forevry
http://fotum.ioptogtammo.net/threatl.php
?thteadid=5734&boatdid=29
Risponde SalvatoteMeschini
Una soluzione percorribile è la seguente:
using System;
using System. Drawing;
using System. Windows. Forms;
namespace QuestoProgrammaVieneChiuso
{ public class MainForm :
System. Windows. Forms. Form
{ public MainForm()
{ InitializeComponentQ; }
[STAThread]
public static void Main(string[] args)
{ Application. Run(new MainForm());>
private void InitializeComponentQ {
this.Load += new System. EventHandler(
this.MainFormLoad); }
void MainForml_oad(object sender,
System. EventArgs e)
{ MessageBox.Show("Sto per chiudere
la finestra");
CloseQ; } }
Java
Interrogare le porte USB
Come posso fare a utilizzare
Java per pilotare periferiche
connesse alle porte usb?
manu7377
http://fotum.ioptogtammo.net/thtead.php7thtea
did=5733&boatdid=18
Risponde SalvatoteMeschini
Puoi controllare all'indirizzo http://
jushsourceforge.net/ dove viene rilasciata
una Java Api OpenSource che fa esat-
tamente al caso tuo.
e
C++
C++ problema
con ShellExecute
Salve a tutti. Vorrei utilizzare il
comando ShellExecute per
lanciare il programma HTTrack da
un mio personale programma
scritto in c++ per scaricare auto-
maticamente delle pagine Inter-
net. Il problema è che devo inse-
rire alcune opzioni oltre al nome
del file da aprire come l'indirizzo
della pagina e indicazioni per il
download (dove salvare etc.) ma
non funziona in nessun modo...
In altre parole mi serve l'equiva-
lente del comando DOS.
"C:\Programmi\web\WinHTTrack>httrack
"http://spaceflightnow.com/cassini
/050422titan.html" -O "c:\documenti
\astronomia\news\tent_automatico" in
c++. (Uso Dev c++).
Perché
http://fotum.ioptogtammo.net/thtead.php7thtea
did=5663&boatdid=20
Rispondono blustetNtg e Johnkoenig
Questo è un test eseguito con Wget
ShellExecute( NULL, "open", "c:\\wget.exe",
"http://www.google.it -r", NULL, 1);
devi aggiungere il percorso e l'esten-
sione al programma...
ShellExecute(NULL,"open","C:\\Programmi
\\HTTTrackWHTTrack.exe", "http:
//spaceflightnow.com/cassini
/050422titan.html -O c:\documenti
\astronomia\news\tent_automatico",NULL,l);
Riassumendo il comando giusto è:
ShellExecute(NULL,"open","C:\\Programmi
\\web\\WinHTTrack\\HTTrack.exe",
"http://spaceflightnow.com/cassini
/050422titan.html -O /documenti
/astronomia/news/tent_automatico
",NULL,1);
Quante vocali ci sono
in un file di testo?
Chi mi dà una mano ad imple-
mentare un algoritmo che
conti le sole vocali all'interno di
un file?
Leonardo12345
http:// forum, ioptogtammo. it/thtead.php 7thtea-
did=5696&boatdid=20
Risponde domtes
Un metodo che utilizza del codice
abbastanza leggibile è il seguente:
#include <algorithm>
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
/**
* Predicato che restituisce true quando
viene valutato su
* una vocale
V_
bool isVowel(char e) {
static string vowels("aeiou");
return (string: :npos ! =
vowels.find_first_of(tolower(c))); }
int main(int arge, char *argv[])
{ // Controllo parametri:
if(2 != arge) {
cerr << "Uso programma: cv file.txt\n";
return 1; }
► 12/ Luglio-Agosto 2005
http://www.ioprogrammo.it
Forum ioProgrammo
T NEWSGROUP
// Apertura file:
RichTextBoxl.Rtf = prova
cognome As String)
ifstream input(argv[l]);
if(Mnput) {
cerr << "Impossibile aprire il file "
<< argv[l] << '\n';
return 1; }
string line;
int vowelCount = 0;
// Legge una riga alla volta ed effettua il
conteggio:
while(getline(input, line)) {
vowelCount += std::count_if(
line.begin(), line.end(), isVowel); }
cout << vowelCount << '\n';
return 0;
VB.NET
IJJj] ^f
Clipboard con testo
da Word
Ciao, lo copio del testo da
Word o da un altro editor di
testo e quindi per recuperare
questo evento da codice faccio
come sotto:
Dim iData As IDataObject =
Clipboard.GetDataObjectQ
If iData. GetDataPresent(
DataFormats.Text) Then
zString = CType(iData.GetData(
DataFormats.Text), String)
end if
La mia domanda adesso è: se io
volessi recuperare oltre il testo
anche il font e il colore del
testo che ho copiato da word
esite un modo oppure no?
Grazie
AleTax
http:/ /forum, ioprogrammo. it/thread.php ?thtea-
did=5578&boardid=27
Risponde sadeness
Devi usare iData.GetData(DataFormats
.Rtf), puoi mandarlo come output ad un
richtextbox o metterlo in una variabile
object. Un esempio che funziona è il
seguente:
Dim prova As String = CStr(
iData.GetData(DataFormats.Rtf))
RichTextBoxl.SelectAIIQ
'Il colore
MsgBox(RichTextBoxl.SelectionFont.Name)
Font Size
MsgBox(RichTextBoxl.SelectionFont.Size)
Font Name - Size - ecc.
MsgBox(RichTextBoxl.SelectionFont.ToString)
Gestire i ComboBox
Ciao a tutti, premetto che arri-
vo da VB6... Ho bisogno di
aggiungere un elenco di persone
in una combobox.
Al momento del click sull'item
voglio ottenere un campo della
classe persona, il codice ad esem-
pio.(in VB6 usavo l'itemdata). Ho
creato una classe Persona con
campi Nome e Codice, per ogni
persona che inserisco creo una
nuova "Persona", setto i campi
ed eseguo:
combobox. items.add(NuovaPersona)
a livello di programma funziona,
inserisce l'intero oggetto nel
combo da cui con il click posso
recuperare i campi della classe...
con un piccolo particolare: nel
combo viene visualizzato un
elenco di "System. qualcosa...",
mentre sarebbe bello visualizza-
re uno dei campi, ad esempio il
nome. Se inserisco solo il "da
visualizzare" (Nome) anziché
tutto l'oggetto non ho più gli
altri campi, quindi non serve più
a nulla nemmeno la classe-
come posso fare?
Grazie a tutti.
marco881
http://fotum.ioprogtammo.net/thread.php7thtea
did=5713&boatdid=27
Risponde AmdBook
Devi impostare la proprietà Display -
Member del ComboBox su "Nome".
Un esempio di codice è il seguente:
Public Class persona
Private _nome As String
Private _cognome As String
Sub New(ByVal nome As String, ByVal
Me._nome = nome
Me._cognome = cognome
MsgBox(RichTextBoxl.SelectionColor.ToString) End Sub
' Font Name
Public Property nome() As String
Get
Return _nome
End Get
Set(ByVal Va lue As String)
_nome = Value
End Set
End Property
Public Property cognomeQ As String
Get
Return _cognome
End Get
Set(ByVal Value As String)
_cognome = Value
End Set
End Property
End Class
...codice associato al ComboBox:
ControlloComboBox.DisplayMember = "Nome"
ControlloComboBox.Items.Add(
New persona("Pinco", "Pallino"))
Arrotondare migliaia
Vorrei arrotondare un numero
come segue:
65893 — -> 60000.
Si può fare?
Nella classe math non ho trovato
nessun metodo adatto!
whirp
http://totum.ioptogtammo.it/thtead.php7thtea-
did=5681&boatdid=27
si tisponde da solo
Ho risolto molto semplicemente, senza
utilizzare metodi già pronti:
If mutuo >= 100000 Then
mutuo = mutuo / 1QQQQ
mutuo = Clnt(mutuo) * 10000
Else
mutuo = mutuo / 1000
mutuo = Clnt(mutuo) * 1000
End If
arrotonda solamente valori superiori
a mille, ad esempio
1244 = 1000
12433 = 12000
335400=330000
http://www.ioprogrammo.it
Luglio-Agosto 2005/13 ►
COVER STORY T ■ Audio sui cellulari
Segreteria telefònica
senza pagare un euro
Intercettiamo una telefonata, rispondiamo con un robot, chiudiamo
il microfono e salviamo il messaggio del chiamante in formato audio
sul telefono per riascoltarlo in un secondo momento!
approfondita
di Symbian e C++
^3 E^3 É^3 ^3
Tempo di realizzazione
M
Eccoci a parlare del sistema operativo più usa-
to sui dispositivi portatili: il Symbian. In que-
sto articolo il nostro obiettivo sarà la realizza-
zione di una segreteria telefonica che non abbia
costi d'ascolto, da utilizzare come servizio aggiunti-
vo nel nostro cellulare. Il funzionamento di questa
segreteria è piuttosto semplice. Viene intercettato lo
squillo, viene spento il microfono del cellulare, il
software risponde con un messaggio automatizzato,
il chiamante lascia un messaggio che sarà registrato
in formato audio sul cellulare. Per ascoltare il mes-
saggio sarà sufficiente attivare il player del cellulare
bypassando completamente il provider di servizi te-
lefonici per quanto riguarda i costi d'ascolto.
SCHEMA PRINCIPALE
DEL PROGRAMMA
Il nostro programma sarà composto da tre parti
principali. La prima sarà quella che si occuperà di:
intercettare la chiamata, aspettare un tot di squilli,
chiudere il microfono onde evitare che, l'utente
chiamante, capisca che siamo dall'altra parte del te-
lefono. La seconda, prowederà a: rispondere alla
chiamata, riprodurre un suono scelto dall'utente
contenente un messaggio di risposta automatizza-
to. La terza ed ultima parte, fondamentale e fulcro di
tutta l'applicazione, è la routine che si occuperà del-
la registrazione dell'audio proveniente esclusiva-
mente dalla linea. I passaggi elencati, non sono faci-
li da realizzare e soprattutto non sono facilmente
comprensibili. Perciò, per leggere quanto segue, do-
vrete dotarvi di pazienza e buona volontà, una volta
superate le difficoltà iniziali, i risultati saranno entu-
siasmanti.
ALL'INTERNO
DI SYMBIAN
Al solito il punto di ingresso dell'applicazione sarà
SimpleRecorderApp.cpp contenuto nella directory
src. Come in tutte le applicazioni Symbian che si ri-
spetti, questo file non deve fare altro che creare il
document dell'applicazione. Il document dell'appli-
cazione non è che un ponte verso l'interfaccia uten-
te. In questa sezione vengono tipicamente inserite
tutte le funzioni che servono alla gestione dei file. Il
tutto, nel nostro caso viene definito in SimpleRecor-
derDocument.cpp, è qui che viene richiamata la ge-
stione dell'interfaccia utente, contenuta nel file Sim-
pleAppRecorderULcpp, è questo file che include Re-
cordEngine.h. RecordEngine.cpp conterrà una intera
classe atta alla gestione della segreteria telefonica
così come l'abbiamo descritta ad inizio articolo.
COME INIZIARE
Fig. 1: Alcuni fra i telefoni Symbian più venduti
È necessario avere
installato il Nokia Sdk
relativo al proprio
cellulare. Previa
registrazione potete
ottenerlo all'indirizzo
http://www.activestate
.corri.
Infine è necessario
avere installato Micro-
soft Visual Studio
2003. L'articolo chiara-
http://www.forum.nokia.
com/main/0.6555.034-
4.00.html.
È necessario anche
avere installato una
mente presuppone
qualche base per
quanto riguarda la
programmazione dei
cellulari basati sul
copia di ActivePerl,
potete scaricarlo
all'indirizzo
sistema operativo
Symbian e del Nokia
SDK.
v
► 14 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Audio sui cellulari ■ T COVER STORY
^TTx
CApaApplication
H
CCoeAppUiBase
CEikApplication
• Create JocunentL::
LJ
CCoeControl
C:ApaC : : ..ir:: CEikDocumen
CEikAppUi
i-CreateAppUiLQ
*HandleCommanaLC-
m
CAknDocument
Kdic^HaÀpgc^G-.i.g.; j
CHelloWorldApplication
CHelloWorldDocumeiit
/QrldA[ipView.cpp|
CHelloWorldAppUi
+HandleCommandLO
CHellDWorldAppView
Fig. 2: Schema dell'applicazione Symbian HelloWorld
Per una comprensione più completa di com'è strut-
turata un'applicazione Symbian, vi rimandiamo alla
Figura 2, dove c'è lo schema basato sull'esempio
fornito nell'SDK, HelloWorld.
Diamo uno sguardo a RecordEngine.h:
class CRecorderEngine: public
MMdaObjectStateChangeObserver {
public:
virtual void MoscoStateChangeEvent(CBase*
aObject, TInt aPreviousState, TInt aCurrentState,
TInt aErrorCode);
public:
static CRecorderEngine * NewL(const TDesC&);
static CRecorderEngine * NewLC(const TDesC&);
-•long StartRecordLQ;
void Sto p Reco rd();
[■■■]
};
>
L'engine è basato su due funzioni principali che so-
no la kMSitlSS WtlWJj e la^jj^^
In questa prima fase ci interessa capire il funziona-
mento della StartRecordLQ in cui inseriremo le istru-
zioni per gestire la telefonata.
DUE PAROLE
SULL'EREDITARIETÀ
Quanto diremo qui di seguito potrebbe apparire
piuttosto astratto e concettualmente difficile da re-
cepire. Potete saltare questo paragrafo e leggerlo
successivamente se siete interessati alla pura prati-
ca, altrimenti con un po' di sforzo potete acquisire
qualche informazione in più su alcune scelte archi-
tetturali della programmazione Symbian. Per utiliz-
zare il sottosistema audio, dobbiamo rendere la no-
Ovviamente no. Questo non
perché alcune applicazioni
symbian non possano funzionare a
telefono spento. Vedi ad esempio
la sveglia o il calendario, ma
perché molto semplicemente
l'assenza della portante ci
impedisce di intercettare la
telefonata.
Alcuni lettori potrebbero far
notare che i cellulari di ultima
generazione, hanno la possibilità
di impostare la sveglia anche a
terminale spento, questo è
possibile perché l'alimentazione
minima, riesce ad fornire, in
parole povere, energia sufficiente
per far sì che un evento, come
quello della sveglia, accenda il
telefono. Per la nostra segreteria
questo risulterebbe impossibile
dato che l'unico evento che
interessa noi, è l'avvento di una
chiamata in arrivo e, come detto
prima, non essendo connessi alla
rete, non si verificherebbe mai.
stra classe, derivante dall'interfaccia MMdaObject-
StateChangeObserver. Questo è l'unico modo per
poter utilizzare il sottosistema audio, dato che il
Symbian gestisce in modo particolare l'ereditarietà
delle classi. Com'è possibile vedere dalla documen-
tazione ufficiale, esse derivano da un'unica classe e
cioè la CBase. La dicitura delle classi è praticamente
simile a quella utilizzata dal Java:
• Classe C - Classe derivata dalla CBase ed istan-
ziata in memoria.
• Classe T - Classi con assenza di oggetti esterni.
• Classe M - Classi di interfaccia.
• Classe R - Classi con collegamenti a risorse reali.
Ricordiamo che nel sistema dell'ereditarietà delle
classi in Symbian, la derivazione di una classe C,
dev'essere dichiarata per prima e che, da una classe
C, può derivare una sola classe C e nessuna o più
classi M Inoltre non possiamo derivare contempo-
raneamente da due classi M, una classe C, se la
prima classe M deriva direttamente dalla seconda.
Per capire meglio il concetto, riferitevi alla Figura 3.
Tornando all'interfaccia MMdaObjectStateChange-
Observer, dobbiamo sapere che c'è un solo metodo
al suo interno, e precisamente il MoscoStateChange-
EventQ, al quale vengono passati da Symbian, una
serie di parametri e di valori che non dobbiamo
usare direttamente.
• CBase* aObject - Il puntatore alla struttura
CBase {all'Audio Sample Object) che gestisce i
Classe 191 Classe M
Il programma è stato
testato e compilato con
il Microsoft Visual C++
e con l'SDK della Nokia
compatibile con il
Nokia 6670.
La scelta per il
Microsoft Visual C++ è
dovuta alla sua
diffusione ed anche per
permettere al lettore di
integrarsi alla
perfezione con il corso
iniziato sulle pagine di
loProgrammo.
I TUOI APPUNTI
Fig. 3: Regole di ereditarietà in Symbian
Utilizza questo spazio per
le tue annotazioni
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 15 ►
COVER STORY T ■ Audio sui cellulari
cambiamenti e i vari stati.
• TInt aPreviousState - Contiene lo stato prece-
dente.
• TInt aCurrentState - Contiene il codice di stato
attuale.
• TInt aErrorCode - Contiene il codice di errore
se, il suo valore, è false.
Quindi, riepilogando, per gestire un sottosistema
audio, bisogna passare un puntatore della classe
CBase al metodo MoscoStateChangeEventO. Per far
questo, la classe principale che sfrutta l'adattatore
audio, deve derivare dall'interfaccia MMdaObject-
StateChangeObserver che a sua volta, deriva dalla
classe principale CBase come vogliono le specifiche
Symbian. Questo vale per tutte le classi di interfaccia
contrassegnate dalla dicitura M.
INIZIAMO A REGISTRARE
Come già detto, all'interno della classe CRecorder-
Engine la funzione per noi più importante è la Start-
TRUTTURA DI UN'APPLICAZIONE SYMBIAN
STILIZZANDO IL WIZARD
aif - Sono contenute le
immagini bmp, le icone e il file
RSS delle risorse.
data - Altri file RSS che
comprendono la gestione dei
dati in ingresso e la posizione e
l'utilizzo delle voci che
compaiono nei menu standard
del telefono.
Engine - Contiene il codice e la
classe principale.
gratin - Contiene tutti i f iles per
la compilazione e la
generazione del package
ine - Contiene gli #include
principali e il file .loc, utilizzato
per la localizzazione del
programma e il file .hrh che
contiene la definizione delle
costanti.
• instali - Contiene il Package
(.pkg) e il .SIS una volta
ottenuto con il batch
• sre - Contiene i sorgenti
principali di un'applicazione
Symbian.
Per poter ottenere il tutto,
bisognerà entrare con la shell DOS
nella cartella group del nostro
progetto ed eseguire il Batch:
"bldmake bldfiles" e
successivamente "ebld build thumb
urei". Una volta fatto questo,
passeremo alla creazione del file
.SIS mediante l'esecuzione della
seguente linea: "makesis
simplerecord.sis"
/C++ compilerà
~* \ and linker j
h
= C++ Header File
rh
= Resource Header File
hrh
- C'JÌIilnUh Htf-Jljtft Fllf; \jit Pl:'JI:t ? P, '.'1 |l -"te
ini
= File di impietri i le funzioni Inline
epp
= C++ Source File
rss
uree File
rsc
Cornp leti Resource File
loc
= File di stringhe per la Localizzazione
mak
= Makefile
bmp
■vs Bitmap File
mbm
i Bitmap File
ait
- App catior i riformai on : : e
SIS
: - sckaqs di nsta azione sul ce u are
key
= Private Key
cer
= Certificate
bat
= Batch Dos File
Interconnessione tra i files
RecordL che si occupa di dare il via alla registrazione
della telefonata in arrivo. Questa funzione al suo
interno, utilizza varie API che fanno al caso nostro e
che il Symbian ci mette a disposizione, dando uno
sguardo a RecordEngine.cpp tutto sarà più chiaro.
All'interno di RecordEngine .cpp troviamo
CRecorderEngine : :StartRecordl_(){
m_line.NotifyIncomingCall(m_iStatus,m_newCallName);
User::WaitForRequest(m_iStatus);// waiting for
incoming cali
User: : LeaveIfError(m_call.OpenExistingCall(m_line,
m_newCallName));
User: : LeaveIfError(m_call.AnswerIncomingCall());
Come possiamo notare nel codice riportato, com-
paiono delle API che si occupano dell'attesa e della
risposta di una chiamata in arrivo. Utilizzando que-
ste API si riesce a gestire nei minimi particolari l'in-
tero telefono. Le API che andremo ad utilizzare, sono
usate dalla stragrande maggioranza dei telefoni che
utilizzano il Symbian come sistema operativo.
Fujfrsu
NOKIA
flrima
swm mena
Panasonic ('M; atotorola
Fig. 4: I colossi che si avvalgono del Symbian per il
loro cellulari
È anche importante sottolineare che quando rice-
viamo una chiamata, il nostro device viene conside-
rato Client, mentre il Device che effettua la chiama-
ta, viene considerato Server. Questo fa capire che la
gestione della chiamata avviene proprio come se si
stesse effettuando una connessione di rete tra un
Server ed un Client. La prima funzione notevole al-
l'interno del codice esposto è la Notifylncoming-
CallQ che si occupa di notificare al Client, la chia-
mata in arrivo. Questa funzione è definita nella clas-
se RLine che troviamo nel file di inclusione etel.h.
Seguendo linea per linea, troviamo un'altra funzio-
ne, e precisamente la WaitForRequestO. La funzione
citata, non fa altro che attendere la chiamata en-
trante in modo da generare un evento (asyncro-
nous request) appena la chiamata è stata ricevuta.
La funzione WaitForRequestO la si può trovare nella
definizione della classe User. Andando sempre avan-
ti nella lettura del codice, troviamo la funzione
OpenExistingCallQ che apre il registro chiamate del
Server, stabilendo così il canale di comunicazione.
* 16 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Audio sui cellulari ■ T COVER STORY
Scorrendo ancora il codice, arriviamo all'ultima fun-
zione riportata nelle righe precedenti, e cioè la fun-
zione AnswerlncomingCallO. Questa funzione si oc-
cupa di, una volta "svegliata" dal segnale di ricevi-
mento della chiamata, rispondere eseguendo un'al-
zata di "cornetta".
CLIENT APPLICATION
*--^
MMdaAudioOutputStreamCallback
CMdaAudi oOutputS tr eara
1
f
CKmf DevS cur. ci
;s=:und De vi e e;
Fig. 5: Interazione delle classi per lo streaming audio
La AnswerlncomingCallO è definita nella classe
RCall, sempre nel file di inclusione etel.h Continuan-
do ad analizzare il codice contenuto nella funzione
membro StartRecordLQ, ci troviamo di fronte alla se-
rie di istruzioni che eseguono la registrazione audio:
m_iMdaAudioRecorder->SetAudioDeviceMode(
CMdaAudioRecorderUtility::ETelephonyNonMixed);
m_iMdaAudioRecorder->SetGain(
m_iMdaAudioRecorder->MaxGain());
m_iMdaAudioRecorder->SetPosition(
TTimelntervalMicroSeconds(O));
m_iMdaAudioRecorder->Cropl_();
m_iMdaAudioRecorder->RecordL();
m_iMdaAudioRecorder->SetVolume(
m_iMdaAudioRecorder->MaxVolume());
return 0;
}
Riassumendo la StartRecordLQ esegue i seguenti
compiti che sono evidenti dal codice
• Notifica al sistema operativo che sta per arrivare
una chiamata.
• Si mette in attesa della chiamata in arrivo.
• Se tutto va a buon fine apre un canale di comu-
nicazione.
• Se tutto va a buon fine rispondere.
• Registra quanto viene detto all'altro capo del te-
lefono da chi sta chiamando.
REGISTRAZIONE
DEI SUONI
Per poter gestire la registrazione di un suono, dob-
biamo avvalerci della classe CmdaAudioRecorder-
Utility. Questa classe è fondamentale sia per la ripro-
duzione di suoni sia della loro registrazione. Cmd-
AudioRecordereUtility è definita nel file di inclusione
MdaAudioSampleEditor.h e una volta allocata, ci
metterà a disposizione una serie di funzioni mem-
bro. Come possiamo notare, per impostare una cor-
retta registrazione che proviene esclusivamente
dalla linea, abbiamo bisogno della funzione SetAu-
dioDeviceModeO. Questa funzione, necessita come
parametro, il tipo TDeviceMode che permette di im-
postare appunto il device audio nei seguenti modi:
• EDefault
• ETelephonyOrLocal
• ETelephonyMixed
• ETelephonyNonMixed
• ELocal
Degli Enum elencati, abbiamo utilizzato il solo ETe-
lephonyNonMixed che rispetto all' ETelephonyMi-
xed, registra l'audio proveniente esclusivamente dal-
la linea, senza effettuare anche la registrazione di
audio proveniente dal microfono del nostro telefo-
no. Scendendo ancora nel codice, troviamo la fun-
zione SetGainQ, che imposta il guadagno dell'audio,
e la funzione SetPositionO, che viene utilizzata per
far partire la registrazione dall'inizio. La funzione
CropLO è utilizzata per far sì che la zona adibita
all'audio, venga per prima azzerata e successiva-
mente con l'utilizzo della funzione RecordLQ, verrà
riempita con l'audio proveniente dalla linea. Infatti
il metodo RecordLQ, si occupa di concatenare allo
stream già presente, quello che la linea sta inviando
al Client, ecco il perché dell'utilizzo della funzione
CropLQ prima di eseguire la registrazione. Una volta
iniziata la registrazione, possiamo impostare il volu-
me al massimo chiamando la funzione: SetVolumeO
che, come parametro, avrà direttamente la funzione
MaxVolumeO. Questo perché la funzione MaxVolu-
meQ, restituisce il valore che corrisponde al massi-
Ci sono diversi RAD
che assicurano un
interfacciamento
perfetto con i devices
Symbian. Sicuramente
il nostro consiglio cade
sui prodotti Borland,
azienda che con il suo
C++ BuilderX Mobile
Version, ha lasciato
una firma indelebile
anche nel campo della
programmazione per la
telefonia mobile su
Symbian.
Fig. 6: Schema delle API
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 17 ►
COVER STORY T ■ Audio sui cellulari
Per poter creare
un'applicazione
Symbian con il Visual
Studio C++ 6, bisogna
avvalersi del Wizard
che troviamo nelI'SDK
fornito dalla Nokia e
appositamente
studiato per il vostro
cellulare.
Copiare lo stesso, nella
directory Template del
Visual Studio e poi,
avvalersi del pannello
Wizard del Visual
Studio che è possibile
raggiungere dal menu
"New".
mo del volume permesso dal device e quindi aven-
dola utilizzata come parametro della funzione
SetVolumeO, imposterà il massimo valore consenti-
to. Una funzione membro della classe CRecorderEn-
gine, di importanza vitale, è quella adibita alla co-
struzione delle classi sulla gestione del file audio, e
delle impostazioni audio che effettuano la registra-
zione. Tutto questo è contenuto nella funzione CRe-
cordEngine::CostructL(), di cui vediamo il codice:
void CRecorderEngine::Constructl_(const TDesC&
wav_file_name) {
m_file_name.Copy(wav_file_name);
rrMMdaAudioRecorder =
CMdaAudioRecorderUtility::Newl_(*this,NULL,80);
TMdaAudioDataSettings iSettings;
iSettings.iChannels = 1;
iSettings. iSampleRate = 8000;
m_iMdaAudioRecorder->Openl_(new TMdaFileClipLocation(
m_file_name),new TMdaWavClipFormat(),new
TMdaPcmWavCodec(),8uSettings);
Leggendo il codice, notiamo l'utilizzo della funzione
.Copy, metodo ottenuto mediante la dichiarazione
di tipo TBuf che troviamo nel file di inclusione Re-
corderEngine.h. Quello che bisogna però osservare
con maggiore attenzione, sono i settaggi per la clas-
se TMdaAudioDataSettings, che servono ad impo-
stare i parametri base per l'utilizzo e l'apertura del
media server. È possibile trovare la definizione di
questa classe nel file di inclusione audio.h. Questi
parametri permettono di impostare la risoluzione di
campionamento, il numero di canali da applicare al
sample audio, il volume, e altre impostazione che,
per maggior completezza, consigliamo di visionare
direttamente sulla guida ufficiale. Come ultima fun-
zione, troviamo la funzione OpenLQ che viene utiliz-
zata quando si vuole aprire un oggetto audio da
riprodurre o quando si vuole creare un oggetto au-
dio per la sua registrazione. Il nome effettivo del file
di registrazione, è definito nel file SimpleRecorder-
AppUI.cpp, con la seguente linea:
_LIT(KDefaultFileName, "c:\\voice.wav");
Ciò che abbiamo analizzato in questa sezione, è il
principale corpo, quello che più ci interessa, della
segreteria telefonica stand- alone.
RISPONDERE
CON UHI FILE AUDIO
E DISATTIVARE
IL MICROFONO
Accingiamoci dunque ad aggiungere quello che
consideriamo una rifinitura all'esempio allegato. La
prima cosa da effettuare è quella senza alcun dub-
bio, di disattivare il microfono. Purtroppo, solo dalle
versioni superiori del Symbian, è possibile effettua-
re registrazioni tramite linea e disattivare il microfo-
no tramite apposite API messe a disposizione dello
sviluppatore. Per i telefoni precedenti ma sempre
corredati di sistema operativo Symbian, è necessario
utilizzare un trucco; il trucco è quello di "simulare" la
pressione del tasto Mute, che compare quando c'è
una chiamata in corso. Il codice è relativamente
semplice, diamogli uno sguardo:
void CSimpleRecordAppUIi: :MuteCallL(){
RWsSession sess=CCoeEnv: :Static()->WsSession();
TWsEvent event;
TInt id=sess.FindWindowGroupIdentifier( 0,
_L("*Phone?") );
event. SetType(EEventKey);
event. SetTimeNowQ;
event. Key()->iCode = EKeyCBAl;
event. Key()->iModifiers = 0;
event. Key()->iRepeats = 0;
event. Key()->iScanCode = EStdKeyNull;
sess.SendEventToWindowGroup( id, event );
event. SetType(EEventKey);
event. SetTimeNowQ;
event. Key()->iCode = EKeyDownArrow;
event. Key()->iModifiers = 0;
event. Key()->iRepeats = 0;
event. Key()->iScanCode = EStdKeyNull;
sess.SendEventToWindowGroup( id, event );
event. SetType(EEventKey);
event. SetTimeNowQ;
event. Key()->iCode = EKeyDownArrow;
event. Key()->iModifiers = 0;
event. Key()->iRepeats = 0;
event. Key()->iScanCode = EStdKeyNull;
sess.SendEventToWindowGroup( id, event );
event. SetType(EEventKey);
event. SetTimeNowQ;
event. Key()->iCode = EKeyOK;
event. Key()->iModifiers = 0;
event. Key()->iRepeats = 0;
event. Key()->iScanCode = EStdKeyNull;
sess.SendEventToWindowGroup( id, event );
}
Come avete potuto notare, il codice è semplicissimo
da intuire, poiché non fa altro che generare una serie
di eventi sui tasti che verranno poi inviati al device.
Nel nostro caso, verrà eseguito quando la telefonata
è stata intercettata ed è stata chiamata la funzione
AnswerlncomingCallO. Per ripristinare poi lo stato di
default e quindi il microfono attivo, ma non è il
nostro caso dato che la comunicazione verrà inter-
rotta una volta registrato il suono, bisognerà riese-
guire la serie di eventi, modificandoli in modo da
adattarli alla sequenza di voci del menu del proprio
► 18 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Audio sui cellulari ■ T COVER STORY
device. Il codice potrà essere aggiunto al sorgente
della nostra applicazione o aggiunto alla classe
principale facendolo diventare un metodo e quindi,
ritoccando la CSimpleRecordAppUL Questa classe si
occupa della gestione degli eventi della tastiera. Fa-
cendo così, non dovremo far altro che richiamare
poi la funzione all'occorrenza. È possibile inserirla,
ed è consigliabile, anche direttamente nella funzio-
ne MoscoStateChangeEvent in questo modo:
void CRecorderEngine ::MoscoStateChangeEvent(
CBase* aObject, TInt aPreviousState, TInt
aCurrentState, TInt aErrorCode){
if(aCurrentState == CMdaAudioClipUtility::ERecording){
// sorgente per il mute
}else if (aErrorCode != KErrNone) {
StopRecordQ;}
}
Dopo il microfono, la nostra segreteria avrà bisogno
di una routine che si occuperà di aspettare un tot di
squilli, prima di stabilire la connessione.
Il codice di attesa che occorrerà, è il seguente:
void current_thread :: sleep(DWORD timeout){
TInt iMSec;
iMSec = timeout*1000;
RTimer timer;
TRequestStatus timerStatus;
timer.Createl_ocal();
TTimelntervalMicroSeconds timelntervalMS(iMSec);
timer.After(timerStatus,iMSec);
User::WaitForRequest(timerStatus);
>
Il Symbian purtroppo non ci viene in aiuto per
quanto concerne il calcolo degli squilli ricevuti, ma
ci consente di stabilire il tempo di attesa prima della
risposta. Questo piccolo esempio, fa capire come
bisogna attendere un certo tempo, quando è in atto
una chiamata in arrivo (Incoming Cali). Purtroppo
non esistono airi mezzi se non quello di calcolare un
minimo tempo prima di iniziare la registrazione o
eventualmente riprodurre un suono di benvenuto.
Anche per questo sorgente vale lo stesso discorso
fatto per il precedente. Lasciamo al lettore la scelta
di inglobarlo nella classe principale utilizzandolo
come metodo, o semplicemente copiandolo sem-
pre nella funzione MoscoStateChangeEvent.
RIPRODURRE UHI SUONO
PER LA RISPOSTA
Come ultima aggiunta, dobbiamo consentire alla
segreteria telefonica la possibilità di rispondere alla
chiamata riproducendo un suono pre-registrato di
benvenuto. Affinché ciò sia possibile, bisogna crea-
re un oggetto CMdaAudioRecorderUtility, successi-
vamente avvalerci della funzione OpenLQ, in modo
da poter aprire il file che vogliamo utilizzare come
benvenuto. La sintassi sarà così:
Openl_(new TMdaFileClipl_ocation(m_file_name), new
TMdaWavClipFormat(), new TMdaPcmWavCodec(),
&iSettings);
Successivamente, una volta aperta la connessione
con la funzione AnswerlncomingCallO ed aver di-
sattivato il microfono con la funzione precedente-
mente illustrata, dovremo chiamare la funzione
PlayLO, metodo dell'oggetto appena destritto.
I VARI SDK
È necessario scaricare l'SDK associato e
sviluppato esclusivamente per il telefono in
vostro possesso. Alcune case consigliano agli
sviluppatori, di avvalersi deN'SDK distribuito
dalla Nokia mentre altre, distribuiscono sui
loro siti web, SDK proprietari e messi a
disposizione gratuitamente agli sviluppatori. È
consigliabile quindi scaricare sempre SDK
specifici per il telefono che si possiede e su cui
si intende sviluppare l'applicazione.
J
CONCLUSIONI
Non mi stancherò mai di dire quanto sia flessibile
questo sistema operativo per la telefonia mobile, e
quanto sia degnamente supportato da colossi come
Nokia, Sony- Ericsson. L'espandibilità, la program-
mabilità e la versatilità ne fanno, a parer mio, il mi-
glior sistema operativo per telefonia mobile mai
creato poiché permette agli sviluppatori, di aggiun-
gere nuove funzionalità non presenti di default, e
agli utenti, di sfruttarle e di averne sempre nuove.
Con questo articolo vi abbiamo fornito le principali
conoscenze per la gestione di una segreteria telefo-
nica basata sul proprio cellulare invece che su un
servizio offerto dal provider. Ovviamente l'applica-
zione può essere enormemente sviluppata, ad
esempio inserendo delle funzioni per selezionare il
messaggio di risposta, oppure per effettuare una
select sulla base di un numero telefonico. Tutto deri-
va da quanto letto in questo articolo. Inviate pure le
creazioni più fantasiose all'indirizzo ioprogram-
mo@edmaster.it A chi è neofita di questo tipo di pro-
grammazione, consigliamo di seguire il corso di "io-
Programmo" sulla programmazione Symbian, che
da nozioni fondamentali quali la compilazione delle
applicazioni, ed entrare molto più facilmente in
quello che è, a mio giudizio, un mondo sempre più
florido.
Marcello Scala
ffi SUL WEB
http://www.forum.nokia
.corri
Dov'è possibile
reperire tutti gli SDK.
http://www.symbian.com
Sito ufficiale del
sistema operativo
Symbian.
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 19 ►
IOPROGRAMMO WEB T ■ Web Service
WEB Service
Usiamoli da PHP
Muoviamo i primi passi nei web service. Vediamo come utilizzare
un servizio esistente per realizzare qualcosa di divertente, ed infine
creiamone uno tutto per noi
REQUISITI
rn basi di PHP
Molti di voi avranno sentito parlare di
Web Service; molti sapranno anche per-
fettamente cosa sono e come funziona-
no, sicuramente in non molti avranno provato ad
usarli o addirittura implementarne uno. In questo
articolo assolveremo a diverse funzioni. Prima di
tutto, diremo qualcosa su cosa sono i Web Service.
In secondo luogo impareremo come usarli, infine
creeremo un nostro servizio pronto per essere uti-
lizzato e, perché no?... venduto.
COSA SONO
I WEBSERVICE
L'idea di base è realizzare "applicazioni distribui-
te". Supponete di stare sviluppando un'applica-
zione di "prenotazione viaggi" per un'agenzia che
ve ne ha fatto richiesta. Create un fantastico soft-
ware che interroga tutti i tour operator del mondo,
trova un albergo soddisfacente, effettua la preno-
tazione e infine stampa fattura e indicazioni per il
viaggio con tanti saluti ai clienti. Contemporanea-
mente dall'altra parte del mondo un programma-
tore abile quanto voi riceve una commessa per lo
PHP5 VS PHP4
L'intero articolo si ba-
sa sull'utilizzo di
PHP5. In PHP 4, l'uso
dei Web Service si ba-
sava su librerie ester-
ne quali ad esempio
Nusoap, che utilizzano
una sintassi e un
approccio pro-
fondamente diversi
da quelli qui discussi.
Quindi, è fondamenta-
le fare i vostri esperi-
menti utilizzando
PHP5. Il secondo re-
quisito essenziale è
l'attivazione
dell'estensione SOAP.
Potete attivare
l'estensione in ambito
Linux compilando con
il parametro -enable-
soap, oppure con
Windows decom-
mentando la linea ex-
tension=php_soap .dll,
nella sezione relativa
alle estensioni.
stesso tipo di software. Anche lui inizia da capo,
scrive milioni di righe di codice diverse dalle vo-
stre per raggiungere il vostro stesso identico risul-
tato. È abbastanza frustrante che si debba sempre
reinventare l'acqua calda! Ora supponiamo che la
vostra applicazione di prenotazione alberghiera si
chiami "TheBestBooking". Sarebbe bellissimo se il
tizio dell'altra parte potesse scrivere nel proprio
programma:
risultato = TheBestBooking->prenotaAlbergo(
"Roma","5","10/07/05", "15/07/05");
Che cosa vuol dire questa riga? Vuol dire che inve-
ce di riscrivere da zero tutto il codice per realizza-
re la propria applicazione, molto semplicemente il
software interroga un servizio messo a disposizio-
ne in rete da qualcuno che lo ha già realizzato.
Questo consentirebbe a chi scrive il programma di
evitare di dover perdere tempo con la creazione di
funzionalità base, ed invece potersi concentrare
sulla personalizzazione richiesta dai clienti, realiz-
zando applicazioni più funzionali in meno tempo.
TheBestBooking è un Web Service, ovvero un'ap-
plicazione che espone funzionalità sul Web.
Perché rendere disponibile il proprio lavoro al
mondo? qualcuno potrebbe farlo semplicemente
per "spirito di condivisione", per quella volontà di
condividere le proprie scoperte al fine di migliora-
re la qualità della vita, altri potrebbero "vendere" i
propri Web Service.
UTILIZZIAMO UM
WEBSERVICE DA PHP 5
Prima di tutto stabiliamo quale Web Service utiliz-
zare. In rete esistono tanti motori di ricerca per
Web Service. Per il nostro primo esempio abbiamo
utilizzato http://uddi.microsoft.com. Fra i tanti
Web Service esposti ne abbiamo trovato uno che ci
► 20 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Web Service ■ T IOPROGRAMMO WEB
restituisce informazioni sullo storico dei risultati
dei vari tornei del Palio di Siena. Il WebService in
questione è descritto all'indirizzo: http://www.il-
palio.siena.it/Palio.asmx. Delle varie righe di de-
scrizione quello che ci interessa maggiormente in
questo momento è il link al file WSDL che descrive
il servizio. Nel nostro caso è http://www.ilpalio
.siena.it/Palio.asmx? WSDL.
Cliccando sul link vi accorgerete che si tratta di un
file XML. I file WSDL descrivono nel dettaglio i me-
todi e le proprietà esposte da un Web Service. Poi-
ché WSDL è un formato standard appare ovvio che
descrivere in questo modo un web service, con-
sente a chiunque di comprendere quali sono i me-
todi che esso espone e come usarli. Diamo uno
sguardo al contenuto del nostro file WSDL:
<?xml version="1.0" encoding = "utf-8"?>
<wsdl:definitions
xmlns:http="http://schemas. xmlsoap.org/wsdl/http/"
xmlns:soap="http://schemas. xmlsoap.org/wsdl/soap/
" xmlns:s= "http://www.w3.org/2001/XMLSchema"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/e
ncoding/" xmlns:tns= "http://www.ilpalio.siena.it/"
xmlns:tm= "http://microsoft.com/wsdl/mime
/textMatching/"
xmlns:mime="http://schemas. xmlsoap.org/wsdl/mime/"
targetNamespace="http://www. ilpalio.siena.it/"
xmlns:wsdl = "http://schemas.xmlsoap.org/wsdl/">
<wsdl:types>
<s: schema elementFormDefault="qualified"
targetNamespace="http://www.ilpalio. Siena. it/">
<s:element name="Palii">
<s:complexType>
<s:sequence>
<s:element minOccurs="l" maxOccurs="l"
name="Anno" type="s:int" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="PaliiResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="l"
name="PaliiResult" type=
"tns:ArrayOfDettaglioPalio" />
[■■■]
Vi abbiamo riportato un estratto, ma diciamo che
al momento non è facilissimo capire in questo mo-
do cosa dovremmo fare per usare il nostro web ser-
vice, anche se forse i più esperti avranno già intui-
to come potrebbe funzionare.
Ma proprio perchè WSDL è un formato standard,
PHP è dotato di funzioni in grado di parserizzare il
file WSDL in questione e restituirci informazioni
corrette.
La nostra pagina PHP sarà così composta:
<?
$client = new SoapClient(
'http://www.ilpalio.siena.it/Palio
.asmx?WSDL');
echo
'<pre>";
$info=
=$client->_
_GetFunctions();
print_
.r($info);
echo
'<hr>";
$info=
=$client->_
_GetTypes();
print_
.r($info);
echo
'</pre>";
?>
Con la prima istruzione creiamo un nuovo oggetto
di classe SoapClient. Nel costruttore passiamo co-
me parametro il file WSDL che contiene la descri-
zione del Web Service da utilizzare. A questo punto
il file viene parserizzato, e l'oggetto client viene
"riempito" con i metodi e le proprietà esposte dal
Web Service. Inoltre all'oggetto client apparterran-
no alcuni metodi che ci consentono di "gestire" il
Web Service in questione. Ad esempio il metodo
GetFunctionsQ restituisce l'elenco di tutti i
metodi esposti. Nel nostro caso:
Array
(
[■
■]
[8] =
> ArrayOfDettaglioPalio Palii(string $Anno)
[9] =
> DettaglioPalio dettaglioPalio(string $Giorno)
[10]
= > DettaglioPalio UltimoPalioVinto(string
$Contrada)
[11]
= > ArrayOfDettaglioPalio PaliiVinti(string
$Contrada)
)
Abbiamo volutamente omesso alcune informazio-
WEB SERVICE SENZA WSDL
In questo articolo abbiamo
sempre fatto riferimento alla
lettura del file WSDL per
ottenere i metodi esposti dal
Web Service. Esiste un'altra
tecnica che consente di non
dover leggere il file WSDL ogni
volta che si richiama il servizio.
La tecnica in questione fa
riferimento all'uso delle funzioni
cali. Ha il vantaggio di non
dovere parserizzare il file WSDL
per ogni chiamata, ma lo
svantaggio di essere meno
semplice da utilizzare. D'altra
parte all'interno del file php.ini
trovate le seguenti linee:
[soap]
; Enables or dìsables WSDL caching
I TUOI APPUNTI
Utilizza questo spazio
per le tue annotazioni
feature.
soap. wsdl_cache_enabled= 1
; Sets the directory name where
SOAP extensìon wiììput cache fiìes.
soap.wsdl_cache_dir= "Itmp "
; (time to Uve) Sets the number of
second while cached file will he
used
; instead of originai one.
soap. wsdl_cache_ttl=86400
Che consentono di abilitare o
meno una cache per i file WSDL
diminuendo ovviamente il tempo
d'accesso.
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 21 ►
IOPROGRAMMO WEB T ■ Web Service
WEB SERVICE
NOTEVOLI
Microsoft commercia-
lizza il proprio Map-
Point esponendo le
funzionalità dell'appli-
cazione come Web
Service. Tradotto in
parole povere la vostra
applicazione può
interrogare MapPoint
Web Service per
conoscere ad esempio
il percorso stradale da
compiere per arrivare
da un luogo ad un
altro. Ciascuna
interrogazione ha
ovviamente un costo
per chi la effettua;
date uno sguardo
http://www.microsoft.com
/mappoint/products
/webservice/default.mspx
per saperne di più.
ni che in questo momento non ci interessavano.
Ma a questo punto l'elenco dei metodi esposti dal
Web Service è sufficientemente chiaro. Ad esem-
pio il metodo Palli prende in input una stringa e
restituisce una struttura di classe ArrayDettaglio-
Palio. Ci rimane da stabilire come questa struttura
viene definita. Per ottenere questo tipo di infor-
mazione ricorriamo al metodo GetTypesQ; che
infatti ci restituisce:
Array
J
[2] => struct ArrayOfDettaglioPalio {
DettaglioPalio DettaglioPalio;
[3] => struct DettaglioPalio {
string Contrada;
string Cavallo;
string Fantino;
[...]
boolean Straordinario;
boolean OrdineAICanapelncerto;
boolean Scosso;
>
)
Anche in questo caso per brevità abbiamo omes-
so qualche informazione, ma il senso è chiaro.
Non ci rimane che andare ad usare il nostro Web
Service. L'esempio è il seguente:
<?
$client = new SoapClient(
'http ://www.ilpalio. Siena
it/Palio.asmx?WSDL');
$Anno = "1984";
$SoapParam =
array('Anno' =
= >$Anno);
$Palii=$client-
>Palii($SoapPa
ram);
print ("<pre>'
);
print_r($Palii);
print ("</pre>
");
?>
Che ci restituisce che nel 1984 nelle due tornate
del 02/07e del 16/08 hanno vinto rispettivamente
la contrada dell'Oca con Baiardo IV e la contrada
del Nicchio con Orion. Nell'output completo c'è
anche una descrizione della gara.
Volendo affinare un po' il nostro codice, potrem-
mo trasformarlo in qualcosa del genere:
<?
$client = new SoapClient(
'http://www.ilpalio.siena.it/Palio.asmx7WSDL');
$Anno="1984";
$SoapParam = array('Anno' =>$Anno);
foreach($Palii->PaliiResult-> DettaglioPalio as
$risultato) {
print "Il ".$risultato->Data.
' ha vinto la contrada ".
$risultato->Contrada." con
".$risultato->
Fantino. "<br>";
};
?>
Osservate che abbiamo passato il parametro defi-
nendo un array il cui indice è identico al parame-
tro richiesto dal metodo.
CREARE
UHI WEB SERVICE
Ovviamente possiamo anche esporre un Web Ser-
vice, perché altri ne fruiscano. Considerate la se-
guente applicazione PHP:
<?
$coperti
na=array(
"91"=>"La grafica costruita
intorno a te",
"90"=>"Parola di Java",
"89" = >"Chat Bluetooth"
);
function GetCopertina($issue) {
global $copertina;
return $copertina[$issue];
}
ini_set('
soap.wsdl_cache_enabled", '
0");
$server
= new SoapServer("dummysoap.wsdl");
$server-
>addFunction("GetCopertina
');
$server-
>handle();
?>
Certamente non si tratta di un grande programma
PHP. Ma quel che importa è capire che abbiamo
appena definito il nostro primo Web Service. Co-
me si può notare, la sintassi è banale. La cosa più
importante è definire il file dummysoap. wsdl che
contiene la descrizione del servizio. Riportiamo di
seguito uno spezzone di codice che potete usare
come template:
<?xml version ='1.0' encoding ='UTF-8' ?>
<definitions name='StockQuote'
$Palii=$client->Palii($SoapParam);
targetNamespace='http://localhost/ioProgrammo/soap'
xmlns:tns=' http://example.org/StockQuote '
xmlns:soap='http://schemas. xmlsoap.org/wsdl/soap/'
xmlns:xsd = 'http://www. w3.org/2001/XMLSchema'
xmlns:soapenc='http://schemas. xmlsoap.org
/soap/encoding/'
xmlns: wsdl = 'http://schemas.xmlsoap.org/wsdl/'
xmlns= 'http://schemas.xmlsoap.org/wsdl/'>
<message name='getCopertinaRequest'>
<part name='issue' type='xsd:string'/>
</message>
► 22 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Web Service ■ T IOPROGRAMMO WEB
<message name='getCopertinaResponse'>
<part name='Result' type='xsd:string'/>
</message>
<portType name='GetCopertinaPortType'>
<operation name='getCopertina'>
< input message='tns:getCopertinaRequest'/>
<output message='tns:getCopertinaResponse7>
</operation>
</portType>
<binding name='GetCopertinaBinding' type=
'tns:GetCopertinaPortType'>
<soap:binding style='rpc' transport=
'http://schemas.xmlsoap.org/soap/http7>
<operation name='getCopertina'>
<soap:operation soapAction =
'um:xmethods-delayed-quotes#getCopertina7>
<input>
<soap:body use='encoded' namespace=
'urn :xmethods-delayed-quotes' encodingStyle=
'http://schemas.xmlsoap.0rg/soap/encoding/7>
</input>
<output>
<soap:body use='encoded' namespace=
'urn :xmethods-delayed-quotes' encodingStyle=
'http://schemas.xmlsoap.0rg/soap/encoding/7>
</output>
</operation>
</binding>
<service name='GetCopertinaService'>
<port name='GetCopertinaPort' binding =
'GetCopertinaBinding'>
<soap:address location='http://localhost
/ioProgrammo/soap/dummysoap.php'/>
</port>
</service>
</definitions>
Il codice relativo al file WSDL non è così semplice
come tutto il resto, tuttavia possiamo dare qualche
indicazione di massima per renderlo maggior-
mente comprensibile.
Proviamo a recuperare le funzioni esposte tramite
il solito getfunction.
Questa volta il risultato è
Array
(
[0]
= > string getCopertina(string $issue)
)
il metodo copertina è definito in
<portType name='GetCopertinaPortType'>
<operation name='getCopertina'>
< input message='tns:getCopertinaRequest'/>
<output message='tns:getCopertinaResponse'/>
</operation>
</portType>
come si vede qui si dice che l'input dovrà essere di
tipo getCopertinaRequest e l'output di tipo getCo-
pertinaResponse. A loro volta i messaggi di input e
output sono definiti in
<message name='getCopertinaRequest'>
<part name='issue' type='xsd:string'/>
</message>
<message name='getCopertinaResponse'>
<part name='Result' type='xsd:string'/>
</message>
Il resto del file è relativo alle dichiarazioni del for-
mato WSDL ed alcune altre impostazioni che ser-
vono al buon funzionamento del tutto. Questi
pochi dati non pretendono certamente di fornire
una spiegazione approfondita del formato WSDL
ma sono più che sufficienti per iniziare.
Seguendo questo template non dovreste comun-
que avere problemi nella creazione dei vostri ser-
vizi web.
USARE IL WEB SERVICE
Il client che usa questo Web Service è il seguente:
<?
$client = new SoapClient('http://localhost
/ioProgrammo/soap/dummysoap.php?WSDL');
$issue = "89";
print_r($client->getCopertina($issue));
?>
Che ci ritorna esattamente il titolo della copertina
del numero 89 di ioProgrammo.
CONCLUSIONI
La teoria dei Web Service è piuttosto articolata e
complessa. In questo articolo non volevamo
addentrarci nei dettagli del protocollo SOAP nei
meandri delle definizioni sui WS. Volevamo sem-
plicemente fornirvi un tutorial minimo e rapido
per iniziare soprattutto ad usare Web Service pre-
senti sul web e in parte anche per creare dei vostri
servizi.
La creazione del file WSDL merita un approfondi-
mento maggiore per potere essere ritenuta com-
pleta e sicuramente ne riparleremo.
Nel frattempo, utilizzando le informazioni conte-
nute in questo articolo, dovreste essere già in gra-
do di compiere i primi passi nel complesso mondo
dei Web Service.
uni po'
DI RIGORE
I Web Service
esportano le proprie
funzionalità tramite un
protocollo predefinito:
SOAP.
Tutti i linguaggi di
programmazione ad
alto livello hanno già
al loro interno
primitive tali da poter
usare il protocollo
usato dai Web Service,
le comunicazioni fra
client e server
avvengono su TCP/IP,
i messaggi sono sicuri
perché incapsulati in
formato SOAP.
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 23 ►
SISTEMA T ■ Applicazioni veloci con la cache
Una super cache
per i nostri dati
Aggiriamo i colli di bottiglia imposti dai limiti d'accesso ai database,
implementando nelle nostre applicazioni Java un meccanismo di caching
dei dati e sfruttando una libreria OpenSource altamente affidabile
Q CD Q WEB
javaencache.zip
REQUISITI
UAI.UJJ.U-JJiJ.M-.l4a
m Basi di Java
> J2SE 1.5, Encache
^a^a^a.
Tempo di realizzazione
rvì
Una cache è un'area tipicamente locale
alla macchina che esegue un'applica-
zione in una cache vengono memoriz-
zate copie di dati acceduti più frequentemente,
provenienti da una sorgente con tempi medi di
accesso così elevati da rappresentare un collo di
bottiglia per l'applicazione. Il caso più tipico è
quello di un sito web che prende i dati da un
database remoto. Chiaramente l'accesso sarebbe
più veloce se i dati fossero prelevati da una direc-
tory locale alla macchina - ovvero una cache -
una copia statica del database remoto. In questo
modo è possibile incrementare le prestazioni
riferendosi alla copia locale dei dati contenuti
nella cache. Esempi di cache si trovano in molti
dispositivi hardware e sistemi software: nei pro-
cessori per migliorare l'accesso ai dati, in RAM,
nei controller delle unità disco per migliorare i
trasferimenti di file, nelle applicazioni server che
costituiscono l'ossatura di Internet su rete, come
sui DNS, per limitare il traffico generato da
richieste di risoluzione di nomi di dominio.
Ovviamente se da un lato lavorare su copie loca-
li dei dati può incrementare le prestazioni, dal-
l'altro nascono nuovi problemi, principalmente
legati al possibile disallineamento tra i dati in
cache ed i dati reali. Ad esempio, nel caso si deci-
da di scrivere una nuova versione di un dato con-
COME INIZIARE
Colleghiamoci al sito http://sourcefor-
ge.net/projects/ehcache e dalla sezio-
ne "file" scarichiamo l'ultima ver-
sione della libreria che al momento
di scrivere l'articolo è la 1.1. Scom-
pattiamo l'archivio e copiamo nella
directory "lib" il file "ehcache-1.1
jar". Serve poi la libreria commons-
logging prelevabile dall'indirizzo
http://jakarta.apache.org/site/binindex
.cgi#commons-logging .
In questo caso il file da copiare
nella directory lib è "commons-
logging-api.jar". Nel caso si utilizzi
JDK1.2, o JDK1.3 serve anche la
libreria Xerces prelevabile da
http://xml.apache.org/xerces2-j
/download.cgi .
Ovviamente trovate tutto anche nel
ed allegato alla rivista.
tenuto anche in una cache è necessario, per
mantenere l'allineamento tra il dato originale e
la copia, annullare quello in cache e scrivere sulla
sorgente dati. Anche la lettura dei dati in presen-
za di cache risulta più macchinosa. Si tenta
prima di tutto di accedere al dato nella cache.
Qualora il dato non venga trovato, e si sia verifi-
cato quindi un cosiddetto "cache miss", si legge il
dato reale dalla sorgente, avendo l'avvertenza di
copiare il dato letto anche nella cache in modo
da averlo già a disposizione per una successiva
lettura. Una cache ha tipicamente prestazioni
superiori in termini di latenza e throughput
rispetto alla sorgente dati reale ma una dimen-
sione inferiore. Così l'altro problema principale
nell'utilizzo di cache è come scegliere gli ele-
menti da eliminare per far posto ai nuovi di cui è
richiesta la memorizzazione. Alcune cache scar-
tano gli elementi utilizzati meno frequentemen-
te, altri quelli non utilizzati da più tempo. In que-
sto articolo utilizzeremo Ehcache, una libreria
Java open source che permette di utilizzare una
cache nei propri applicativi e che ci svincola dal-
l'implementare un nostro meccanismo di cache
affidando invece la gestione ad Ehcache che è un
progetto stabile e perf ormante, utilizzato anche
in Hibernate. Prepariamo l'area di lavoro crean-
do la cartella "ehcache", con le sottocartelle "sre"
dove troveranno posto i sorgenti Java, "build"
dove saranno generati i file .class, "lib" dove
saranno inserite le librerie necessarie e "config"
per i file di configurazione e andiamo ad iniziare.
SCENARIO
Si supponga di dover sviluppare un sistema per
la vendita online di biglietti di autobus che ope-
rano su tragitti autostradali. Il web server che si
occuperà della vendita online è locato presso
una server farm ma il database gira su una mac-
► 24 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Applicazioni veloci con la cache ■ T SISTEMA
china negli uffici della sede. In una prima imple-
mentazione ogni richiesta di disponibilità di
posti verrà rigirata al database. Si vedrà poi come
l'uso di una cache possa migliorare decisamente
le prestazioni del sistema. Scriviamo la classe Bus
che ha la responsabilità di tenere traccia dei posti
disponibili su ogni pullman. Ogni Pullman è
caratterizzato da un codice identificativo, una
capacità massima ed un numero di posti corren-
temente prenotato. Ovviamente la capacità mas-
sima non dovrà essere mai superata.
public final class Bus{
private final String code;
private final int maxCapacity;
private int currentCapacity;
I metodi principali saranno checkAvailabilityO
che restituisce il numero di posti ancora disponi-
bili sul bus, e bookO che prenota, previo control-
lo sulla disponibilità, il numero di posti specifi-
cato. Questi metodi sono dichiarati syncrhoni-
zed, in modo da evitare che due thread concor-
renti possano prenotare gli stessi posti con il
risultato che di vendere su un pullman più posti
di quelli disponibili. A fronte di una prenotazio-
ne, il metodo book() utilizza un BusDAO di cui
parleremo appena oltre, per salvare il proprio
stato su database.
synchronized public boolean checkAvailability(
int seats){
if(currentCapacity + seats <= maxCapacity)
{return true;}return false;}
synchronized public boolean book(int seats){
if (checkAvailability(seats)){
currentCapacity = currentCapacity + seats;
busDao.setBus(this); return true;
}else{return false;}
}
ACCESSO AL DATABASE
SENZA CACHE
Implementando il pattern Data Access Object svi-
luppiamo l'interfaccia BusDAO che espone le
responsabilità di gestione della persistenza su
database delle istanze della classe Bus, creandole
ed aggiornando gli opportuni record su databa-
se. L'interfaccia BusDAO mette in evidenza i
metodi createBusQ tramite il quale l'applicativo
può richiedere una nuova istanza di Bus, specifi-
candone codice identificativo e capacità massi-
ma, getBusQ che restituisce l'istanza di Bus corri-
spondente ad un definito codice, setBusQ che
permette aggiornare il record di un bus passato
come parametro. Specifichiamo un'interfaccia
cosicché si nascondano i dettagli implementativi
della classe DAO. Questo ci consentirà di imple-
mentare due diverse classi una che esegue la
gestione dei database senza utilizzare encache, la
seconda che invece ottimizza le prestazioni tra-
mite encache. In questo modo sarà semplice
effettuare in un secondo momento il confronto
fra le prestazioni
public interface BusDAO {
Bus createBus(final String code,
final int
maxCapacity);
Bus getBus(final
String code);
void setBus(final
Bus bus);
}
Inizialmente scriveremo l'implementazione che
accede direttamente al database senza fare uso
della cache. Il metodo createBusQ eseguirà un
"inserì' 'per creare un record relativo al nuovo bus
ed invocherà setDaoQ sulla nuova istanza per
impostare il BusDAO utilizzato nel metodo
bookO, il metodo getBusQ eseguirà un "selecf'pev
caricare i valori degli attributi del pullman richie-
sto, il metodo setBusQ eseguirà un "update" per
aggiornare i valori dei campi che rappresentano
lo stato del bus passato come parametro.
public class BusDAODirect implements BusDAO {
public Bus createBus(final String code, final int
maxCapacity){
bus = new Bus(code, maxCapacity);
bus.setDao(this);
//SQL INSERT
return bus;}
public Bus getBus(final String code){ //... }
public Bus setBus(final Bus bus){ //... } }
UN'APPLICAZIONE
DI TEST
Sviluppiamo una classe che utilizzi le classi Bus e
BusDAO. Un esempio potrebbe essere quello di
creare un ristretto numero di istanze di bus e
provare ad invocarne i metodi di richiesta di
disponibilità e prenotazione di posti. Qui di
seguito una traccia.
public class CachedApplication {
private static final BusDAO busDAO =
new BusDAODirect();
public static void main(String[] a){
new CachedApplication().run();}
public static BusDAO getBusDAO(){return busDAO;}
public void run(){
Bus bus = busDAO.createBusC'busOOl", 60);
bus.book(3);} }
I TUOI APPUNTI
Utilizza questo spazio per
le tue annotazioni
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 25 ►
SISTEMA T ■ Applicazioni veloci con la cache
ALGORITMI
DI CACHIMG
Molti sono gli
algoritmi di caching
utilizzati. Tutti indicano
una politica da
utilizzare per decidere
quali elementi nella
cache rimuovere a
fronte della necessità
di inserire un nuovo
elemento:
• Least frequently used
(LFU): i candidati
all'eliminazione sono
tutti quelli che hanno
un rapporto tra
numero di accessi e
tempo di permanenza
nella cache basso.
• Least recently used
(LRU): viene eliminato
l'oggetto che non è
acceduto da maggior
tempo.
• First in first out (f ifo):
è come se la cache
fosse un tubo, da un
capo gli elementi sono
messi in cache, dall'al-
tro ne fuoriescono.
Quando si rende neces-
sario inserire un nuovo
elemento questo sem-
plicemente "spinge"
fuori dalla cache quello
in ultima posizione.
È importante notare come la classe principale
esponga tramite getBusDAOQ un'istanza di
BusDAO utilizzabile dalla classe Bus.
COMPILAZIONE
E RISULTATI
Compiliamo l'applicazione portandoci nella
directory "src", invocando javac
javac -classpath ..\Nb\commons-collections-3.1.jar;..
\lib\commons-logging-api.jar;..\lib\ehcache-l.l.jar
-d ..\build *.java
Lanciamo poi l'applicazione spostandoci nella
cartella "build" e digitando
java -cp ..\Nb\commons-collections-3.1.jar;..
\lib\commons-logging-api.jar;..\Nb
\ehcache-l.l.jar;..\config;.\ CachedApplication
Se per ogni sessione utente viene ottenuta un'i-
stanza dal database per controllarne la disponi-
bilità. È semplice immaginare come questo si
ripercuota direttamente nell'esecuzione di una
"select" su database remoto, creando del traffico
inutile poiché il bus in esame è stato magari
appena consultato da un un altro utente, e quin-
di potenzialmente potrebbe già trovarsi in
memoria. Facciamo girare l'applicativo e misu-
riamo il tempo speso nell'attesa di una risposta
da parte del database server remoto.
tempo di accesso al db in millisecondi: 531 7
inserì eseguiti:4, update eseguiti:48,
selects eseguiti'AOO
FACCIAMOLO
CON EHCACHE
Ehcache permette la configurazione della cache
programmaticamente o dichiarativamente con
l'utilizzo di file XML. Ecco la configurazione uti-
lizzata per questo esempio, da copiare nella car-
tella "config" con nome "buscache.xml".
<ehcache>
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory=" 10000" eternai ="false"
timeToIdleSeconds="120" timeToLiveSeconds="120"
overflowToDisk= "false" diskPersistent= "false"
diskExpiryThreadIntervalSeconds="120"/>
<cache name="bus"
maxElementsInMemory=" 10000" eternal="false"
overflowToDisk= "false" timeToIdleSeconds="300"
timeTol_iveSeconds="600"/>
</ehcache>
L'elemento "disksto re "indica dove devono esse-
re memorizzati gli elementi in cache. È possibile
specificare un path completo oppure una varia-
bile d'ambiente Java. In questo caso "java.io-
.tmpdir" punta alla directory temporanea.
L'elemento "defaultcache" imposta la configura-
zioni di default per tutte le cache. Sarà poi neces-
sario specificare solo i parametri che differiscono
dal default.
Ecco il significato degli attributi:
• Name: nome della cache. Dal programma si
utilizzerà tale nome per avere un puntatore
alla cache.
• Eternai: se false gli elementi nella cache ven-
gono comunque eliminati dopo un periodo
configurabile durante il quale non sono stati
acceduti, se true gli elementi non sono mail
eliminati dopo un timeout ma solo per far
posto a nuovi elementi.
• MaxElementsInMemory: numero massimo
di elementi nella cache in RAM.
• OverflowToDisk: se true, tutti gli elemeni
rimossi dalla cache in ram sono scritti
mediante serializzazione in una cache su
disco. Se false vengono invece semplicemen-
te rimossi.
• TimeToIdleSeconds: se un elemento in cache
non viene utilizzato per un periodo maggiore
al valore di questo attributo, viene scartato
dalla cache.
• TimeToLiveSeconds: un elemento creato
nella cache permane per un periodi pari al
valore di questo attributo, dopodiché viene
rimosso.
• DiskPestisten: se true, la cache su disco viene
ricaricata ad un nuovo riavvio della cache,
altrimenti ad ogni avvio, la cache su disco
risulterà vuota.
• DiskExpiryThreadlntervalSeconds: ogni
quanti secondi viene eseguito un controllo
sulla validità degli elementi memorizzati
nella cache su disco.
ACCESSO AL DB
CON CACHE
Scriviamo una nuova implementazione di Bus-
DAO che aggiunge caratteristiche di caching al
DirectBusDAO. Strutturalmente la nuova classe si
frapporrà tra l'applicazione e il DirectBusDAO
che accede alla sorgente dati, gestendo la cache e
richiamando i metodi del DirectBusDAO solo
quando servono.
► 26 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Applicazioni veloci con la cache ■ T SISTEMA
Il fatto che entrambe le classi DAO estendano la
stessa interfaccia fa sì che il client le possa utiliz-
zare indistintamente senza che il codice debba
essere cambiato.
import net.sf.ehcache.*;
public class CachedBusDAO implements BusDAO {
private final BusDAO source;
private static final CacheManager cacheManager;
private final Cache cache;
static {
try{cacheManager :
CacheManager.create(
"build/buscache.xml");
}catch(CacheException ce){throw new
RuntimeException(ce);} }
public BusDAOCached(final BusDAO source){
this. source = source;
cache = cacheManager.getCache("bus"); }
public Bus createBus(String code, int maxCapacity) {
Bus bus = source. createBus(code, maxCapacity);
return bus; }
public Bus getBus(String code) {
//Element rappresenta qualsiasi oggetto in una
cache Ehcache
Element e = nuli; Bus bus = nuli
try{
//Ricerca di un Element nella cache associato al
codice
e = cache. get(code);}
catch(Exception ex){throw new
RuntimeException(ex);}
if(e == null){
bus = source. getBus(code);
//Inserimento del bus in cache sottoforma di
Element associato al codice
cache. put(new Element(code, bus));
}else{bus = (Bus)e.getValue();>
return bus;}
public void setBus(Bus bus) {
cache. remove(bus.getCode());
source. setBus(bus);}
}
Nel costruttore viene passato un BusDAO a cui
verrà demandato il compito di interagire effettiva-
mente con il database. Nell'inizializzatore statico
viene creato un CacheManager partendo dal file di
configurazione scritto nei passi precedenti. Il
metodo createBusQ è una semplice delega. Il me-
todo getBusQ cerca l'istanza in cache. In caso posi-
tivo restituitce l'istanza trovata risparmiando un
accesso al database, in caso negativo delega al
busDAO sorgente la ricerca su database, posizio-
nando l'istanza trovata in cache per velocizzare
probabili futuri accessi. Il metodo setBusQ elimina
prima di tutto la copia in cache poiché disallineata
rispetto al nuovo stato e delega al busDao sorgente
il compito di aggiornare il record su database.
UTILIZZARE LA
VERSIONE CON CACHE
È necessaria una piccola modifica nelle classi per
abilitare l'utilizzo del busDAO con la versione
dotata di caching. La prima modifica è nell'appli-
cazione vera e propria.
private static final BusDAO busDAO = new
BusDAOCached(new BusDAORaw());
La seconda è nella classe bus che deve essere
dichiarata Serializable per far si che istanze di Bus
in cache RAM possano essere scritte su disco.
import java. io. Serializable;
public final class Bus implements Serializable{
Compiliamo l'applicazione portandoci nella di-
rectory "src", invocando javac
javac -classpath ..\lib\commons-collections-3.1.jar;..
\lib\commons-logging-api.jar;..\lib\ehcache-1.0.jar
-d ..\build *.java
Lanciamo poi l'applicazione spostandoci nella
cartella "build" e digitando
java -cp ..\lib\commons-collections-3.1.jar;..
\lib\commons-logging-api.jar;..\lib\ehcache-1.0.jar;
..\config;.\ CachedApplication
Compiliamo e facciamo girare l'applicativo.
I risultati ottenuto sono notevolmente migliori.
tempo di accesso al db in ms: 1041
insertA, update:42, selects:0, hits:357, misses:43
Fig. 1: Alcuni test presenti direttamente sul sito di
encache mostrano un miglioramento di oltre il 70%
rispetto a JCS
CONCLUSIONI
L'esempio ha dimostrato quanto sia semplice
aggiungere un livello di cache ad una classe che
accede ad una sorgente dati, in modo da aumen-
tarne drammaticamente le prestazioni.
Daniele De Michelis
CACHE HIT
E CACHE MISS
Nell'utilizzo di cache ci
sono due eventi signi-
ficativi: il cache hit
avviene quando si cer-
ca un elemento nella
cache e lo si trova con
successo, l'evento op-
posto è il cache miss,
quando l'elemento
non si trova e diventa
necessario accedere o
ad una cache di livello
superiore o diretta-
mente alla data source.
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 27 ►
SISTEMA T ■ Estensioni per Firefox
Firefox, il browser
estensibile
Una guida passo passo per creare un'estensione per Firefox
utilizzando XUL il linguaggio semplice e veloce derivato da XML
e utilizzato per interfacce grafiche di grande effetto
CD □ WEB
zip
REQUISITI
j_j Basi di XML, HTML
y e Javascript
. Firefox 1.0 o superiore
• -i \ ' y| ' 'ii l 'iil
Tempo di realizzazione
-/ '-/
Xul è un linguaggio basato su XML creato
dalla Mozilla Foundation per scrivere facil-
mente e velocemente interfacce grafiche.
Proprio XUL è stato scelto anche per la realizzazio-
ne di estensioni per il browser Firefox. La dove per
estensione si intende un piccolo programma che
"estende" e "personalizza" il Browser adattandolo
alle vostre esigenze. Voi stessi potete creare un'e-
stensione per il vostro browser. Tutto quello di cui
avete bisogno è un normalissimo editor di testo e
un compressore .zip. Se poi siete dei professionisti
della programmazione potete anche utilizzare un
editor XML per editare il vostro file .xul.
Come avrete già capito il cuore di un estensione
per Firefox è costituito proprio da un file .xul, in
questo articolo vi guideremo alla creazione di una
miniestensione.
ANATOMIA
DI UN FILE .XUL
Innanzitutto un file ".xul" è effettivamente un file
xml e inizia con le classiche dichiarazioni:
<?xml version = "1.0"?>
<?xml-stylesheet href="chrome://global/skin/"
type="text/css"?>
A seguire, solitamente troviamo la dichiarazione
del documento: una "window" con la specifica del
namespace a cui appartiene:
< window xmlns="http://www. mozilla.org
/keymaster/gatekeeper/there.is.only.xul"
orient="vertical" title="Bookmarks">
All'interno della <window> possiamo aggiungere
tutti i componenti grafici di cui la nostra applica-
zione necessita e che possono essere nativi di xul,
o importati dall' html:
<menupopup id="menu_ToolsPopup">.
</menupopup>
<image src="images/banner.jpg"/>
<html:button xul:onclick="DoSomething()">
Così facendo riusciamo, abbastanza facilmente e
velocemente, a comporre un'interfaccia grafica. In
un articolo pubblicato nel numero 91 di ioPro-
grammo, per esempio, avevamo costruito una bar-
ra in cui inserire dei bottoni per accedere alle
diverse sezioni di un sito, o per organizzare meglio
i nostri bookmark:
<?xml version = "1.0"?>
<?xml-stylesheet href="chrome://global/skin/"
type="text/css"?>
< window xmlns= "http://www.mozilla.org
/keymaster/gatekeeper/there.is.only.xul"
orient="vertical" title="Bookmarks">
<script type="application/x-javascript">
function loadURL(event) {
var contentFrame
document.getElementById(
'contentFrame');
var uri = event. target. getAttribute('value');
if (uri) contentFrame. setAttribute('src', uri);
>
</script>
<menubar oncommand = "loadURL(event);">
<menu label = "Programmazione">
<menupopup>
<menuitem label = "IoProgrammo" value=
"http://www.ioprogrammo.it/" />
<menuitem label = "Javastaff"
value= "http://www.javastaff.com/" />
<menuitem label = "XulPlanet"
[ -]
</menupopup>
</menu>
<menu label = "Aste">
<menupopup>
<menuitem label = "Italia" value=
^ 28 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Estensioni per Firefox H T SISTEMA
"http://www.ebay.it" />
<menuitem label="Gran Bretagna" value=
"http://www.ebay.co.uk" />
[...]
<menuitem label="Germania" value=
"http://www.ebay.de" />
</menupopup>
</menu>
<menu label = "Notizie">
<menupopup>
<menuitem label="Ansa" value=
"http://www.ansa.it" />
<menuitem label="Televideo" value=
"http://www.televideo.rai.it/nazionale
/homenaz.asp" />
</menupopup>
</menu>
<button label = "WebMail" value=
"http://www.gmail.com" />
</menubar>
<iframe id="contentFrame" src=
"http://www.google.it" flex="l" />
</window>
Se rendiamo disponibile questo file xul in un Web
Server, gli utenti che lo apriranno con Firefox, ve-
dranno una barra di navigazione con dei menu a
tendina che ci permettono di scegliere velocemen-
te la pagina nel frame sottostante. Il controllo degli
eventi è affidato alla funzione Javascript loadURLQ
incapsulata nei tag <script> e </script>.
LA STRUTTURA
DELLE DIRECTORY
Abbiamo capito ormai che tipo di struttura ha
un'interfaccia scritta appositamente per Firefox. Si
progetta la parte grafica utilizzando la sintassi di
xul, mentre il controllo delle interazioni è assegna-
to a funzioni in un linguaggio di scripting suppor-
tato dal browser, solitamente Javascript. Se vo-
gliamo scrivere un'estensione vera e propria da in-
stallare nel nostro browser dovremo solo procurar-
ci un compressore zip, dato che i file ".xpi" ovvero
i file autoinstallanti per le estensioni Mozilla altro
non sono che archivi in quel formato, poi rinomi-
nati e creare l'archivio seguendo qualche ac-
corgimento.
Per prima cosa occorre disporre i file che compon-
gono la nostra estensione in cartelle opportuna-
mente scelte. Iniziamo con il replicare la seguente
struttura
esempio_jar\
esempio _jar\conten t\
esempio _jar\conten t\esempio \
esempio _jar\skin \
esempio JaAskin \classic\esempio \
esempio_xpi\
esempio _xpi\chrome\
FILES STANDARD
A questo punto dobbiamo riempire le varie cartel-
le. Alcune conterranno immagini e suoni per far
funzionare la nostra applicazione, altre conterran-
no dei file di descrizione che serveranno all'esten-
sione per autoinstallarsi correttamente. Iniziamo
creando un file "descrittore" chiamato contents
.rdf nella cartella esempio _jar\content\esempio\.
TESTARE LE ESTENSIONI
Se siete in fase di sviluppo di una
nuova estensione è utile
installare una seconda instanza
di Firefox per testare il vostro
lavoro.
Infatti un qualunque problema
provocato da una non corretta
generazione del file .xpi
potrebbe provocare il mancato
riavvia di Firefox dopo avere
installato la nuova estensione.
In tal caso dovreste procedere a
un riavvio in safe mode.
Molto più comodo "sporcare"
un'installazione di Firefox
dedicata esclusivamente ai test.
Questo file, che si occupa di specificare tutte le
risorse contenute nell'estensione.
<?xml version = "1.0"?>
< RDF: RDF xmlns:RDF="http://www. w3.org
/1999/02/22-rdf-syntax-ns#"
xmlns:chrome="http://www.mozilla.org/rdf/chrome#">
<RDF:Seq RDF: about="urn:mozilla: package :root">
<RDF:li RDF:resource=
"urn : mozilla : package :esempio"/>
</RDF:Seq>
<RDF:Seq RDF: about="urn: mozilla :overlays">
< RDF: li RDF:resource="chrome://browser
/content/browser.xul"/>
< RDF: li RDF:resource="chrome://navigator
/content/navigator.xul"/>
</RDF:Seq>
<RDF:Seq RDF:about="chrome://browser
/content/browser.xul">
<RDF:li>chrome://esempio/content/esempioOverlay
.xul</RDF:li>
</RDF:Seq>
<RDF:Seq about="chrome://navigator/content
/navigator.xul">
<RDF:li>chrome://esempio/content/esempioOverlay
.xul</RDF:li>
</RDF:Seq>
<RDF:Description RDF:about=
"urn: mozilla: package: esempio"
chrome:displayName="Ciao da IoProgrammo."
chrome:author="l_uca Mattei"
chrome:authorURL="mailto: luca. mattei@javastaff.com"
I TUOI APPUNTI
Utilizza questo spazio per
le tue annotazioni
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 29 ►
SISTEMA T ■ Estensioni per Firefox
chrome:name= "esempio"
chrome:extension = "true"
chrome:description="La nostra prima estensione. ">
</RDF:Description>
CHE COSA È
RDF?
contents.rdf e un file
RDF (Resource
Description
Framework) che serve
a descrivere i contenuti
di una estensione.
RDF è una grammatica
XML che fornisce un
modello di dati facil-
mente processabile da
una applicazione.
Ulteriori informazioni
su questo formato
possono essere sul sito
del W3C:
http://www.w3.org/TR
/2004/REC-rdf-concepts-
20040210/
</RDF:RDF>
Il File comincia con la consueta dichiarazione
della versione xml e del namespace a cui appartie-
ne il nostro documento. Seguono un elenco di
dichiarazioni di risorse, indicate con il tipo di indi-
rizzamento chrome://.
Per esempio consideriamo questo indirizzo:
chrome ://browser/content/browser.xul
Si tratta di un riferimento al frame interno di Fire-
fox. L'indirizzo segue, nella composizione, delle
semplici regole. La prima parola dopo il prefisso
chrome:// rappresenta il package a cui le risorse,
divise in directory, appartengono, "browser" è il
package delle risorse interne a Firefox, mentre
"navigator" serve per mantenere una compatibi-
lità alle vecchie versioni di Mozilla. Tutte le nostre
risorse sono accessibili quindi a partire dall'indi-
rizzo:
chrome://esempio/
L'ESTENSIONE
Nel file contents.rdf T 'unico riferimento ad un file
della nostra estensione è:
chrome ://esempio/content/esempioOverlay.xul
Questo file, si occupa di disaccoppiare le funzio-
nalità dell'estensione dal file xul vero e proprio.
Nel nostro esempio abbiamo definito una sola
funzione:
<?xml version="1.0"?>
<overlay id = "esempioOverlay" xmlns=
"http://www.mozilla.org/keymaster/gatekeeper
/there.is.only.xul">
<script type="application/x-javascript" src=
"chrome ://esempio/content/esempioOverlay.js" />
<menupopup id = "menu_ToolsPopup">
<menuitem insertafter="devToolsSeparator" label =
"Esempio IoProgrammo" accesskey=
"e" oncommand = "esempio();" />
</menupopup>
</overlay>
Viene definito infatti un menu popup, inserito nel-
la sezione Tools o Strumenti della barra menu di
Firefox, con un unico menuitem, che esegue la
funzione Javascript esempio ()■
Tutte le funzioni Javascript vengono inserite nel
file esempioOverlay.js, come indicato nel file appe-
na visto. Il nostro esempioOverlay.js conterrà ov-
viamente solo una alert che visualizzi il classico
"Hello World":
function
esempioQ
{
alert("Ciao Mondo!
");
}
Questi due file costituiscono la parte più dinamica
dell'estensione, quella che intendiamo modificare
fino a raggiungere il comportamento funzionale
desiderato. Disaccoppiando questa parte dai file
xul, abbiamo anche un altro vantaggio, poter uti-
lizzare gli stessi overlays in più file xul, limitando-
ci ad importarli ogni volta:
<?xul-overlay href = "chrome ://esempio/content
/esempioOverlay.xul"?>
Da questo indirizzo chrome, è facile intuire che
andremo a salvare i file di Overlay nella cartella -
esempio _jar\content\
ADATTARE
L'ESTENSIONE ALLO SKIN
Avrete sicuramente notato che fra le cartelle da
creare c'è: - esempio _jar\skin\ questo perché in
un'estensione è possibile creare diversi profili gra-
fici, che il browser caricherà a seconda della skin
selezionata nelle impostazioni generali. Nel nostro
HelloWorld non abbiamo alcun bisogno di creare
diverse skin, per questo ci limiteremo al minimo
indispensabile, la skin predefinita: "classic".
Per questo, creiamo il file esempio _jar\skin\classic
\esempio \conten ts. rdf.
Possiamo tranquillamente utilizzare il template
classico per estensioni senza particolari profili
grafici:
<?xml version = "1.0"?>
<RDF:RDF xmlns:RDF=
"http://www.w3.Org/1999/02/22-rdf-syntax-ns#"
xmlns:chrome="http://www.mozilla.org/rdf/chrome#">
<RDF:Seq about="urn:mozilla:skin:root">
<RDF:li resource="urn:mozilla:skin:classic/1.0" />
</RDF:Seq>
< RDF: Description about="urn:mozilla:skin:classic/1.0">
<chrome:packages>
<RDF:Seq about="um:mozilla: skin: classic
/1.0:packages">
< RDF: li resource="urn:mozilla: skin: classic
/1.0:esempio" />
</RDF:Seq>
* 30 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Estensioni per Firefox ■ T SISTEMA
</chrome:packages>
<em:file>
</RDF: Description>
</RDF:RDF>
Eventuali file da utilizzare nella particolare skin
vanno inclusi nella cartella e richiamati con la sin-
tassi:
chrome://esempio/skin/nomefile.png
ULTIMI PASSI
Arrivati a questo punto, abbiamo creato tutti i file
che devono essere inclusi nella cartella esempio_
jar. Tutti i file e le sottocartelle di questa directory
vanno inclusi in un archivio zip, facendo atten-
zione che tutti i percorsi dei file inclusi siano re-
lativi, come nell'immagine.
File :\-,M
ca Visualizza Azioni
Opzioni ?
3Ì- 6
Apri Preferiti
gf ^ q| q| ^f gf
Aggiungi Estrai Cancella Cifra Prova Registra
Nome-t
Tipo Modificato Dimensi... Pe... Dim.
o... Percorso
■tnpconten
-. t"
FìleRDF 28/(14*2005 17.02 557 55%
Ì45 skiràdassic'ii sempi-'i,
1*1 contente, rdf
H esempioOverlay.xul
File RDF 35 1.045 53%
JScript Seri... 28/04/2005 16.36 47 00%
File XUL 28/04/2005 17.02 396 35%
'539 Luritent'iessmpir/s
47 corii:erìt:\eser.ipio\
?nt\esempìo\
Fig. 1: L 'archivio esempio.jat non è altto che un file
zip tinominato. Nel crearlo facciamo particolare
attenzione ai percorsi dei file
Una volta creato l'archivio lo rinominiamo in
esempio.jar e lo salviamo nella cartella esempio_
xpi\ chrome. In pratica Firefox sa che ogni volta
che deve accedere ad una risorsa passando per un
indirizzo chrome: //esempio deve andare ad attin-
gere da questo archivio.
Passiamo alla cartella esempio _xpi, che oltre al jar
appena creato, deve contenere due file: install.rdf
e install.js. Il primo è fondamentale, perché serve a
Firefox in fase di installazione, il secondo solo per
retrocompatibilità con le versioni di Mozilla prece-
denti. Vediamo il primo:
< Description about="urn : mozilla : extension :
file:esempio.jar">
<em : package>content/esempio/</em : package>
<em: skin >skin/classic/esempio/</em: skin >
</Description>
</em:file>
<em:targetApplication>
< Description >
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384
}</em:id>
<em:minVersion>0.7</em:minVersion>
< em : maxVersion > 1 . 1 </em : maxVersion >
</Description>
</em:targetApplication>
</Description>
</RDF>
Particolare attenzione va riservata agli id dell'ap-
plicazione target e dell'estensione.
Per quanto riguarda il primo, questo deve essere
sempre lo stesso, dato che identifica in maniera
univoca Firefox:
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384
}</em:id>
L'id della nostra estensione invece deve essere
univoco, per non far confusione con altre esten-
sioni. Per averne uno tutto nostro, possiamo ricor-
rere a due vie, un programma standalone da far
girare sul nostro PC, oppure una egi in peri libera-
mente utilizzabile dal web a questo indirizzo:
httpj/extensions. roachfiend. comlcgi-binlguid.pl.
Il programma standalone esiste sia in versione
Windows (GUIDGen), liberamente scaricabile dal
sito Microsoft, sia in versione Linux, utilizzabile da
shell richiamando uuidgen.
Install.js è l'equivalente in Javascript di install.rdf e
potremo ometterlo tranquillamente, se non inten-
diamo rendere compatibile la nostra estensione
con i vecchi Mozilla.
CONTATTA
L'AUTORE
Sono disponibile per
chiarimenti, critiche e
suggerimenti
all'indirizzo:
luca.mattei@javastaff.com
<?xml version="1.0"?>
<RDF xmlns="http://www. w3.org/1999/02
/22-rdf-syntax-ns#"
xmlns:em="http://www. mozilla. org/2004/em-rdf#">
< Description about="urn: mozilla :install-manifest">
<em:id>{ee9692e7-le80-41de-88c8-09310124abde
}</em:id>
<em:name>IoProgrammoEsempio</em:name>
<em:version>0.1</em:version>
<em: description >Semplice Helloworld
</em:description>
<em:creator>l_uca Mattei</em:creator>
<em:homepageURL> http://www.ioprogrammo.it
</em:homepageURL>
IL FILE XPI
Ora l'estensione è pronta, costruiamo un archivio
zip a partire dalla cartella esempio_xpi, stando at-
tenti ai path dei singoli file, come avevamo fatto
per il file jar. Rinominiamo il file in esempio.xpi,
formato scelto da Mozilla per contraddistinguere i
suoi plug-ins e siamo pronti per l'installazione.
Basta andare nel menu del nostro amato Browser,
cliccare su "Apri file.. 11 e scegliere la nostra nuova
creazione. Se tutto va bene, riusciremo ad installa-
re il nostro HelloWorld e potremo eseguirlo dal
menu strumenti.
Luca Mattei
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 31 ►
SISTEMA T I Common Virtual File System
Un File System
portatile in Java
Utilizzando una libreria OpenSource di Jakarta costruiremo
un'applicazione che utilizza internet come un enorme disco rigido
virtuale. Inoltre diremo qualcosa sui server Web e i database...
LI CD □ WEB
File_system_portatile.zip
w M
Il nostro problema è il seguente: come moltissime
persone abbiamo tanti file sparsi su internet.
Qualcosa lo abbiamo lasciato su un FTR qualco-
sa in ufficio in una directory condivisa, altra roba
l'abbiamo sparsa sul file system di Gmail o su altri
file system non locali. Il punto è che andiamo spes-
so in giro, perciò sarebbe utile mantenere una copia
locale di tutti i file che abbiamo sparso sincroniz-
zandoli con il nostro Hard Disk. È anche vero che
andando spesso in giro non usiamo sempre lo stes-
so portatile, o la stessa periferica mobile. Perciò lo
scopo di questo articolo è il seguente:
1) Realizzare un client Java scaricabile da internet.
2) Utilizzare il client per autenticarsi su un server.
3) Sulla base dell'autenticazione eseguire la sincro-
nia con le nostre risorse remote e il file system
locale su cui stiamo lavorando.
I vantaggi di questo sistema stanno nel fatto che, l'u-
nico file che è necessario avere a disposizione per
eseguire la sincronia è il client, e possiamo scaricar-
lo da internet. La lista dei file da sincronizzare risie-
derà anche essa sul server perciò realmente oltre che
del client non abbiamo bisogno di niente.
Una volta autenticato eseguiamo la sincronizzazio-
ne e ci ritroveremo sulla macchina locale i file che
mi servono.
COME FARE?
Per i nostri scopi utilizzeremo Common WS di Ja-
karta. Questo è un progetto opensource con il quale
si vuole creare una libreria stabile per gestire diversi
tipi di file e risorse {ftp, http, smb etc etc.) come una
singola risorsa. In questo modo agli occhi dello svi-
luppatore Java la manipolazione di una risorsa o di
un'altra è del tutto trasparente. Nella nostra applica-
zione l'utente comunica inizialmente con un server
che tiene memorizzati i nomi di login, con la relati-
va password e la lista di risorse registrate dall'utente.
In questo modo dopo una prima autenticazione ab-
biamo a disposizione la lista di tutte le nostre risor-
se sparse nella rete. Ora il client può tranquillamen-
te scegliere una cartella sul computer dove si trova,
da tenere sincronizzata, aggiungendo o rimuovendo
file e cartelle.
In automatico il programma, in base alla lista delle
nostre risorse remote, aggiorna i nostri repository,
trasferendo o cancellando i file. C'è una certa "com-
plicazione" nel mantenere su un server remoto una
COSA E JAKARTA
n
REQUISITI
« J2SE, Servlet,
LUÌ JDBC
J2SE SDK, Tomcat,
Common VFS
_j^j
Tempo di realizzazione
rs\fs\fs\M
Jakarta è un progetto per lo
sviluppo di codice opensour-
ce, riutilizzabile in diversi am-
biti. All'interno del sito prin-
cipale http://jakarta.apache.org
troviamo una suddivisione tra
i prodotti per tipologia: libre-
rie, f ramework e server.
Sfogliando tutti i progetti che
sono ospitati da Jakarta pos-
siamo trovare tantissimi soft-
ware da utilizzare nelle nostre
applicazioni evitando così,
ogni volta, di reinventare
l'acqua calda. All'interno di
questa lunga lista di librerie e
tool troviamo una sezione che
può essere utile conoscere in
molti progetti, la sezione
"Commons". Questa racchiu-
de tutti i progetti dedicati alla
riusabilità di componenti
Java. All'interno di questa
sezione troviamo due ulteriori
sottosezioni: Proper, repo-
sitory di componenti Java
riutilizzabili e Sandbox, work-
space che non prevede la
durata e la manutenzione dei
progetti dei partecipanti.
Proprio all'interno di Sandbox
troviamo il componente
molto interessante che utiliz-
zeremo nel nostro progetto:
Common VFS (Virtual File Sy-
stem).
► 32 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Common Virtual File System ■ T SISTEMA
lista di utenti con le relative risorse associate, ma se
in un futuro volessimo vendere il nostro client il
sistema ci verrebbe molto utile.
TRASFERIMENTO FILE
L'interfaccia FileSystemManager, inclusa nel packa-
ge org.apache.common.vfs, ci permette di gestire i
file system a nostra disposizione. Ecco un semplice
modo per connetterci ad un file system
FileSystemManager fsManager= VFS.getManager();
FileObject aFile = fsManager.resolveFile(
"ftp://user:pass@server:21" );
In questo modo abbiamo inizializzato un FileObject,
un'interfaccia che rappresenta un file. Un file può
essere una directory o un semplice file. Avremo
quindi a disposizione diversi metodi per elencare i
file, crearli e distruggerli. Definiamo un metodo che
ci servirà di sicuro, ovvero il metodo per trasferire
file dal nostro computer ai computer sparsi sulla
rete e viceversa scaricare i nostri file dalla rete.
public static void copia(String from,String to)
throws Exception
{
FileObject src = fsManager.resolveFile(from);
FileObject dest = fsManager.resolveFile(to);
if(dest.exists() && dest.getTypeQ == FileType.FOLDER)
dest = dest.resolveFile(src.getName()
.getBaseNameO);
dest.copyFrom(src, Selectors.SELECT_ALL);
}
Questo metodo richiede come parametri due indi-
rizzi, sotto forma di stringa, i quali saranno rispetti-
vamente la sorgente da cui copiare e la destinazione.
Una volta risolto l'indirizzo del file in un oggetto
FileObject possiamo tranquillamente copiare il file
dalla sorgente alla destinazione passata nell'argo-
mento. In questo modo, in maniera del tutto traspa-
rente per il programmatore, avviene il trasferimento
del file, rispettando alla perfezione il protocollo della
risorsa remota.
Non ci resta che pensare a come organizzare l'appli-
cazione che vogliamo sviluppare. All'inizio il nostro
programma verrà inizializzato passando come argo-
mento una cartella, all'interno della quale trovere-
mo due diverse sottocartelle che rappresenteranno
rispettivamente i file remoti scaricati e i file locali da
inviare. Quindi dovremo preoccuparci di sincroniz-
zare la cartella Download con tutti i nostri file sparsi
sulla rete. Poi dovremo attivare un Thread che perio-
dicamente controllerà la presenza di file nella cartel-
la Upload e, nel caso ci fossero, inviarli verso i nostri
server remoti.
SINCRONIZZAZIONE
Supponiamo di avere a nostra disposizione la lista
dei vari computer su cui risiedono i nostri file. Per il
momento immaginiamo di averla, già disponibile,
successivamente vedremo come ottenerla.
Dunque, abbiamo a disposizione il nome della car-
tella locale dove mettere i file e la lista dei vari file sy-
stem remoti e non rimane altro che ciclare su ogni
file system e scaricare tutti i file usando la funzione
che abbiamo definito in precedenza
public static void initialSync(Vector list) throws
Exception{
System. out.println("Sincronizzazione iniziale..");
fslist=list;
fsManager = VFS.getManagerQ;
for (int i=0;i<fslist.size();i++) {
FileObject fo = fsManager.resolveFile((String)
fslist.elementAt(i));
FileObject[] fofiles = fo.getChildrenQ;
for ( int j = 0; j < fofiles. length; j++ ){
copia(fofiles[j].getl\lame().getURI(),
downloadDir);
System. out.println(fofiles[j].getName()
+" copiato");} }
System. out.println("Sincronizzazione iniziale finita.");
}
Alla fine di questo metodo abbiamo sincronizzato
tutti i nostri file nella cartella downloadDir. Ora dob-
biamo preoccuparci dell'upload. Come già detto ab-
biamo a disposizione una cartella di transito, nella
quale i file che devono essere trasferiti ai server re-
moti sostano per un tot di tempo. La soluzione in
questo caso può essere di definire un classe che
estende TimerTask e che controllerà periodicamen-
te la cartella per vedere se ci sono file in transito.
In questa fase definiamo staticamente ogni quanti
secondi verrà risvegliato il TimerTask per il control-
lo, poi magari potremmo farlo definire dall'utente.
C'è un'ulteriore riflessione da compiere. Disponia-
mo della lista dei nostri server, ma su quale effettue-
remo l' upload? Potrebbero esserci mille vie e mille
motivi per definire un ordine di preferenza nei ser-
ver, in questo semplice programma effettueremo un
round robin tra i vari file system remoti, scegliendo
progressivamente rispetto alla lista di file system che
abbiamo per effettuare l'upload. Dopo aver sincro-
nizzato la nostra cartella locale facciamo partire il
TimerTask
Timer timer = new Timer();
UploadController uc=new UploadController(this);
timer.schedule( uc, 5000, 5000 );
Il nostro TimerTask verrà avviato ogni 5 secondi per
controllare la presenza di nuovi file. Ecco quindi
l'implementazione della classe UploadController
GLOSSARIO
COMMON VFS
Common VFS richiede il
download di molte li-
brerie presenti sul sito
di Jakarta. Ogni libreria
riguarda un protocollo
particolare, quindi gli
sviluppatori di Com-
mon VFS hanno dovuto
definire uno standard
per le risorse a cui acce-
dere. Nel momento in
cui ci colleghiamo ad
una risorsa specifica
viene richiamata l'im-
plementazione relativa,
senza che noi la speci-
fichiamo direttamente
nel codice.
VFS
(Virtual File System) è
una tecnologia che in
molti vogliono svilup-
pare all'interno di si-
stemi operativi e pro-
grammi. È un'astrazio-
ne del file system e
quindi porta benefici
dal punto di vista dello
spazio ma chiaramente
si possono incontrare
dei problemi per quan-
to riguarda l'implemen-
tazione dell'architettu-
ra. Nei seguenti link
trovate dei progetti ri-
guardanti VFS
http://developer.gnome
.org/doc/API/gnome-vfs/
http://www.cse.unsw.edu.
au/~neilb/oss
/linux-commentary /vfs.html
http://www.parl.clemson
.edu/pvfs/desc.html
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 33 ►
SISTEMA T ■ Common Virtual File System
APPROFONDIMENTO
Come già detto
Common VFS utilizza
tante librerie presenti
sul sito di J a kart a. Per
una lista completa di
quelle che servono vi
rimando all'uri
http://jakarta.apache.org/
commons/sandbox/vfs/do
wnload.html
È seguire lo sviluppo di
Common VFS e di tanti
altri progetti
opensource che potete
trovare sul sito di
Jakarta grazie alle
mailing list. Sono
disponibili due diverse
tipologie di mailing
list, utenti e
sviluppatori. Per
ulteriori informazioni
consultate il sito
http://jakarta.apache.org/
site/ma il .html
class UploadController extends TimerTask {
Kernel kernel;
String uploadDir;
public UploadController(Kernel kernel) {
//Passiamo come argomento il kernel in modo
//tale che questa classe potrà accedere alle
//informazioni e ai metodi già definiti
this.kernel = kernel;
//La directory di upload verrà utilizzata dalla
//classe Java File quindi il formato dovrà essere
//quello canonico c:\cartella\sottocartella
this. upload Dir=kernel. uploadDir; }
public void run() {
try {
//Dopo aver caricato la lista dei file system remoti
//selezioniamo quale server utilizzeremo per
//l'upload. Avendo deciso un semplice round robin
//per schedulare la scelta del file system dovremo
//mantenere anche l'informazione riguardante
//l'ultimo server utilizzato
Vector list=kernel.fslist;
String url = (String)list.elementAt(
kernel. lastpos%list.size());
//Carichiamo quindi la directory di upload e ci
//facciamo restituire la lista dei file presenti.
//Per ognuno di essi richiameremo il metodo copia,
//utilizzando come parametri il path locale e l'uri
//remoto. Successivamente cancelliamo il file dalla
//cartella
File f=new File(uploadDir);
File lista[]=f.listFiles();
for ( int i = 0; i < lista. length; i++ ){
System. out.println( lista[i].getAbsolutePath() );
copia(lista[i].getAbsolutePath(),url);
lista[i].delete();>
if (lista.length>0)
kernel. lastpos+ + ; }
catch(Exception e) {
System. out.println(e.toString());> }
}
Grazie a questa classe ogni cinque secondi verrà
controllata la cartella che abbiamo scelto come
transito per i file e verranno trasferiti i file sui reposi-
tory remoti. Possiamo dire che la parte centrale del
programma è stata sviluppata. Ora dobbiamo occu-
parci di reperire la lista dei file system da un server.
LATO SERVER:
AUTENTICAZIONE
Esiste un server dove vengono conservate tutte le
informazioni riguardanti utenti e liste di risorse per
ogni utente. Questo serve appunto alla nostra appli-
cazione per poter distribuire ed aggiornare le liste. Il
server prima di effettuare qualsiasi operazione deve
riconoscere l'utente, quindi dobbiamo avere un
database di utenti, definiti nella seguente maniera
CREATE TABLE utenti (id INTEGER GENERATED BY DEFAULT
AS IDENTITY(START WITH , INCREMENT BY 1 ),
user VARCHAR NOT NULL, pass VARCHAR NOT NULL ,
PRIMARY KEY (id ) );
Oltre al database degli utenti dovremo anche avere
un database con la lista delle risorse per ogni utente.
Chiaramente in questo database dovremo riferirci
alla lista degli utenti, altrimenti non sapremmo di
chi è la risorsa che viene elencata. Dovremo quindi
inserire un campo per l'id dell'utente
CREATE TABLE risorse ( id INTEGER GENERATED BY
DEFAULT AS IDENTITY(START WITH , INCREMENT BY 1
), utente INTEGER, uri VARCHAR NOT NULL ,
PRIMARY KEY (id), FOREIGN KEY(utente)
REFERENCES utenti(id) );
Ora che abbiamo creato le due tabelle dobbiamo
supporre che ci sia un'interfaccia web dove gli uten-
ti possono iscriversi. Quindi pensando di avere già
degli utenti inseriti scriviamo la Servlet che in base
ad utente e password restituirà la lista di risorse. Nel
metodo doGetQ della nostra Servlet dovremo distin-
guere due possibili azioni: autenticazione e l'inseri-
mento di una nuova risorsa. Ecco quindi la fase ini-
ziale di autenticazione
String action = request.getParameter("action");
if (action. equals("autenticazione")) {
String user=request.getParameter("user");
String pass=request.getParameter("pass");
Vector Nst=caricaLista(user,pass);
if (list.size()>0) {
for(int i=0; i<list.size() ; i++) {
out.print(list.elementAt(i)+";"); } }
else out.print("0");
Il metodo caricaListaQ carica appunto la lista delle
risorse associate all'utente che si è collegato. Se non
dovesse esistere l'utente o se la password fosse sba-
gliata il metodo ritornerebbe un vettore vuoto e
quindi la risposta del server sarebbe 0, che il client
poi interpreterà come un errore. La connessione al
database avviene tramite JDBC, settando nel clas-
spath le librerie relative al database da utilizzare.
In questo caso è stato usato HypersonicSQL.
public Vector caricaLista(String user,String pass) {
Connection e;
Statement SQLStatement;
ResultSet rs;
Vector liste=new Vector();
try {
Class. forName("org.hsqldb.jdbcDriver" );
* 34 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Common Virtual File System ■ T SISTEMA
e = DnverManager.getConnection("jdbc:hsqldb:
hsql://localhost/fsp", "user", "pass");
} catch (Exception e) {
e.printStackTrace();
return nuli; }
try{
SQLStatement = c.createStatement();
rs = SQLStatement. executeQuery("SELECT uri
FROM risorse WHERE utente IN (select id from
utenti where user="'+user+"' and
pass='"+pass+"'); ");
while(rs.next())
liste. add(rs.getString(l));
if(rs != nuli) rs.closeQ;
if(SQLStatement != nuli) SQLStatement.close();
if(c != nuli) c.close();
} catch (Exception e) {
e.printStackTraceQ;
return liste; }
return liste; }
Con una semplice connessione al database ci faccia-
mo restituire, se esiste, la lista di risorse associate
all'utente e la restituiamo nella risposta.
Quindi l'autenticazione e la trasmissione delle risor-
se da server a client è stata implementata
INSERIMENTO RISORSA
Ora dobbiamo occuparci di un'altra funzionalità ba-
se per il nostro programma, ovvero l'inserimento di
una nuova risorsa per un utente. All'inizio avremo
soltanto un utente registrato e zero risorse associate.
Poi piano piano il client inserirà delle risorse nel
database, che il nostro programma popolerà di file
nella maniera che abbiamo già discusso. Dobbiamo
prevedere un ulteriore switch nella nostra Servlet,
passando un diverso parametro come "action".
else if (action. equals("inserimento")) {
String user=request.getParameter("user");
String pass=request.getParameter("pass");
String risorsa = request.getParameter("risorsa");
boolean inserito=inserisciRisorsa(user,pass,risorsa);
if (inserito) {
out. print("l");}
else out.print("0");}
rimane da definire il metodo per inserire la risorsa
dell'utente all'interno del database. Per farci restitui-
re tutte le risorse associate ad un utente dobbiamo
effettuare una connessione simile alla precedente,
trovando prima di tutto l'id relativo all'utente che
vuole inserire una nuova risorsa e poi effettuare un
INSERT sulla tabella delle risorse con l'id ottenuto.
int id=-l;
SQLStatement = c.createStatement();
rs = SQLStatement.executeQuery("select id from
utenti where user="'+user+ m and pass="'+pass+"'; ");
while(rs.next())
id=rs.getlnt(l);
rs = SQLStatement. executeQuery("insert into
risorse(UTENTE,URL) values('" + id + '", "'+url + '")"
Con questo ultimo metodo della Servlet abbiamo
completato la parte server del nostro programma. In
teoria servirebbe gestire anche gli utenti, la registra-
zione, l'eliminazione. Ma per lo scopo di questo arti-
colo ci fermiamo a questo punto.
INTERFACCIA GRAFICA
Dobbiamo implementare l'interfaccia grafica attra-
verso la quale l'utente può utilizzare il nostro pro-
gramma. Viste le poche (ma corpose) funzionalità
che sono inserite all'interno dell'applicazione viene
d'istinto pensare ad una semplice interfaccia grafi-
ca. Inizialmente dobbiamo porre l'utente davanti a
tre possibili funzioni: la sincronizzazione, l'upload
di un file e l'inserimento di una nuova risorsa. Pen-
siamo quindi di far visualizzare un JFrame, all'inter-
no del quale disporremo tre bottoni con tre diverse
immagini.
super("FSp vO.0.1");
setSize(250, 150);
setLocation(300,200);
JPanel panel = new JPanel();
panel. setLayout(new GridLayout(l,2));
Imagelcon iconl = new ImageIcon("Dl.gif");
Imagelcon icon2 = new ImageIcon("Ul.gif");
Imagelcon icon3 = new ImageIcon("Insert.gif");
ButtonActionListener ball = new
ButtonActionListener("Download");
ButtonActionListener bal2=new
ButtonActionListener("Upload");
ButtonActionListener bal3=new
ButtonActionListener("Inserisci");
JButton uno=new JButton(iconl);
JButton due=new JButton(icon2);
JButton tre=new JButton(icon3);
uno.addActionListener(ball);
due.addActionListener(bal2);
tre.addActionListener(bal3);
panel. add(uno);
panel. add(due);
panel, add(tre);
getContentPane().add("Center", panel);
show();
Come si può vedere, su ogni bottone abbiamo ag-
giunto un ButtonActionListener. Questa è un classe
definita da noi che implementa l'interfaccia Action-
SUL WEB
Ecco alcuni link riguar-
danti le tecnologie
supportate da Common
VFS
http://samba.org/cifs/
http://www.microsoft.com
/mind/1196/cifs.asp
http://www.freesoft.org
/CIE/RFC/959/index.htm
http://www.webdav.org/
http://en.wikipedia.org
/wiki/SSH file transfer
protocol
CONTATTA
L'AUTORE
L'autore, Federico
Paparoni, può essere
contattato per
suggerimenti o
delucidazioni
all'indirizzo email
federico.paparoni@
javastaff.com
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 35 ►
SISTEMA T I Common Virtual File System
Per testare la nostra
applicazione
dobbiamo prima di
tutto mettere nel
CLASSPATH tutte le
librerie richieste da
Common VFS (e sono
parecchie!!), poi una
volta compilata dob-
biamo creare un file di
Property inserendo i
nostri dati. Infine dob-
biamo fare il deploy
del WAR (Web
Archive) della Servlet
nella sottocartella
webapps di Tomcat.
Fate partire il
programma e il gioco
è fatto.
Listener e che permette di sapere quando un botto-
ne è stato premuto. In base quindi al bottone che
viene cliccato dall'utente dovremo implementare
un diverso metodo
class ButtonActionListener implements Action Listener {
String action;
public ButtonActionl_istener(String action) {
this.action=action;>
public void action Performed (Action Event e) {
necessarie al nostro programma
Properties p=new PropertiesQ;
if (action. equals("Download"))
k.synchQ;
else if (action. equals("Upload"))
choosellpIoadQ;
else
insertRisorsa(); } }
Tramite questi tre diversi metodi riusciamo a dare
all'utente il completo controllo dell'applicazione. Il
metodo relativo al download non farà altro che sin-
cronizzare un'altra volta il contenuto della nostra
cartella locale, richiamando un metodo della classe
Kernel. Il metodo chooseUploadO permette invece
all'utente, tramite un'interfaccia grafica, di scegliere
quale file selezionare per l'upload. In questo modo
automatizziamo il fatto di dover andare a copiare il
file nella nostra cartella di transito.
public void chooseUploadO {
try {
String filename = File.separator+"tmp";
JFileChooser fc = new JFileChooser(new
File(filename));
// Mostra la finestra per la scelta del file
fc.showOpenDialog(this);
File selFile = fc.getSelectedFile();
System. out.println(selFile.getCanonicalPath());
k.upload(""+selFile.getCanonicalPath());>
catch(Exception e) {System. out.println(e.toString());}}
Infine il metodo insertRisorsaO chiede all'utente
quale risorsa vogliamo inserire sul server, utilizzan-
do la classe grafica JOptionPane .
COMUNICAZIONE
CLIENT-SERVER
Eccoci quindi arrivati all'ultima parte del program-
ma. Praticamente manca la classe che si occuperà
di gestire la comunicazione con il server, prima
identificandosi e prendendo la lista di risorse e poi
inserendo nuove risorse sul server. Per il momento
username e password vengono lasciati in un file di
Property, ma chiaramente si potrebbe pensare ad
una migliore gestione di questi dati.
Quindi, adesso, all'inizio del programma, verranno
recuperate dal file di Property le informazioni
p.load(new FileInputStream("fsp.txt"));
String user=p.getProperty("user");
String pass=p.getProperty("pass");
String downloadDir=p.getProperty("downloadDir");
String uploadDir=p.getProperty("uploadDir");
k=new Kernel(user,pass,downloadDir,uploadDir);
Per la comunicazione con il server dovremo creare
una classe, Comunication, che una volta inizializza-
ta con username e password potrà essere interroga-
ta per dialogare con il server. Inizializziamo la classe
all'interno del Kernel, poi successivamente la ri-
chiamiamo in base all'input fornito dall'utente.
Il primo metodo da definire è quello riguardante la
lista di risorse
//Qui avviene la connessione alla Servlet, passando come
//argomento l'action autenticazione e i dati relativi all'utente
URL server = new URL("http://127. 0.0. 1:8080
/PFSweb/PFSServlet?action=autenticazione&user=
"+user+"&pass="+pass+"");
BufferedReader in = new BufferedReader(new
InputStreamReader(server.openStream()));
String inputLine;
String input="";
Vector el = new Vector();
while ((inputLine = in.readLine()) != nuli) {
input=input+inputLine; }
//Dopo aver letto tutta la risposta del server noi sappiamo
//che le risorse sono formattate nel seguente modo:
//risorsa l;risorsa2;risorsa3; Quindi utilizziamo uno
//StringTokenizer per avere tutte le risorse e per
//restituirle al Kernel sotto forma di vettore
StringTokenizer st = new StringTokenizer(input,";");
while (st.hasMoreTokens()) {el.add(st.nextToken());>
in.close();
return el;
Nell'altro metodo che conterrà la classe Comunica-
tion dovremo solo comunicare al server qual è la
nuova risorsa da inserire, richiamando semplice-
mente la Servlet con i giusti parametri. Ecco quindi
completato tutto il nostro progetto.
CONCLUSIONI
Ci sono tantissime cose che possono essere fatte per
migliorare un software del genere. Prima di tutto
l'interfaccia grafica. Poi la gestione dei dati persona-
li, che adesso è relegata ad un file di testo. Il nostro
scopo era comunque metervi in grado di utilizzare
la potenza di Jakarta Common VFS. Speriamo di
esserci riusciti.
Federico Paparoni
+ 36 /Luglio-Agosto 2005
http://www.ioprogrammo.it
SISTEMA T ■ Riconoscimento del testo in Visual Basic.NET
Creiamo un OCR
con Visual Basic .NET
L'installazione di Office 2003 ha come effetto secondario quello di
aggiungere al sistema alcune simpatiche librerie. Una in particolare
ci servirà per creare un nostro modulo per il riconoscimento del testo
□ CD LJ WEB
ocrnetzip
P^«^$T mW
■Ai.i.ujj.Mjju.ima
J53 Prìncipi di VB.NET
Visual Studio 2003
^3 ^3 ^3.
Tempo di realizzazione
Quando installiamo una nuova release di Of-
fice a quasi tutti capita di non far troppo ca-
so alle applicazioni accessorie in essa con-
tenute. Confesso che anche a me era a prima vista
sfuggita una piccola applicazione accessoria che a
noi programmatori può dare molte soddisfazioni.
Tra gli strumenti presenti nella versione Office 2003
c'è un piccolo programma chiamato Microsoft Office
Document Imaging che serve a effettuare il ricono-
scimento caratteri {OCR per gli amici) su documen-
ti digitalizzati in formato TIFF.
E fin qui, direte voi, buono a sapersi. Ma il bello è che
la versione di Microsoft Office Document Imaging
integrata in office 2003 (non quella di Office XP)
rende disponibile nel sistema anche una libreria
COM con relative API.
A questo punto lo scaltro programmatore avrà già
intuito il gioco: sfruttare le librerie di Microsoft Of-
fice Document Imaging, che a questo punto po-
tremmo cominciare anche a chiamare confiden-
zialmente MODI, per integrare un OCR nelle nostre
applicazioni! In particolare vedremo come utilizzare
MODI nell'ambiente di sviluppo .NET.
IL NOSTRO
PROGETTO OCR
La prima cosa da fare è referenziare nel progetto
Visual Basic di Visual Studio, come illustrato nei pas-
si, la libreria "Microsoft Office Document Imaging
| IL MODELLO AD OGGETTO
W ESPOSTO DA MODI
Il modello ad oggetti di MODI
•
L'oggetto Layout espone i risulta-
consiste nei seguenti oggetti
ti del riconoscimento caratteri
principali:
su una pagina.
•
MiDocSearch espone le funziona-
• Document rappresenta una
lità di ricerca sul documento.
ordered collection di pagine
•
Il controllo Viewer (l'oggetto
(ìmages).
MiDocView) è un controllo
• Image rappresenta la singola pa-
ActiveX che mostra le pagine di
ge di un documento.
v
un documento.
E
Componenti di .NET Framework Componenti COM |
Nome
Microsoft MAPI - itrol, version 6.0
□ Microsoft M trolj version 6,0
Microsoft Multimedia Co itro . versio - 6,0
□ Microsoft Office Chart 10,0
□ Microsoft Office Chart 11,0
Jlaosolt '"'ilice Data 5->uce Contro! 10,0
Microsoft Office Data Source Control : LG
C
c
e
e
e
e
e
\WINDOWS\System32\MSMAPI32. . . .
\WINDOWS\sy stem32\msmask32 . oc;
\WINDOW5\5ystem32\MCI32.0CX
\PROGRA~l\FILECO~l\MICROS~... |
\PROGR A~ l\FILECO~ 1 \MICROS~ . , H
\PROGRA~l\FILECO~l\MICROS~... !
\PROGRA~l\FILECO~l\MICROS~... |
^^^'■'■J.qj.d.L'JLUJIL'.tJ.'lLU.II.f.lUJl^'.'iaJIJ^
□ Microsoft Offii ■ pient Control
□ Microsoft Office Outlool F'ich Foirnat Conti ol
m e
e
e
\PROGRA~l\FILECO~l\MICR05~... j
\PROGR A~ 1 \MICROS~2\OFFICE 1 . , .
\PROGRA~l\MICR05~2\OFFICEl... 9
^1
Microsoft Office De» \::V:À Imaging Viewer t entrai .1,
Lingua: Sconosciuto
OK Annulla Reimposta
Fig. 1: Inserimento del controlio ActiveX
11.0 Type Library" che si dovrebbe trovare nel tab
"COM" della finestra di dialogo che appare cliccan-
do su soluzione/referencesl aggiungi riferimento (se
non fosse presente o non abbiamo installato Office
2003 nel sistema oppure la funzionalità non è stata
prescelta in fase di installazione della suite). A que-
sto punto Visual Studio crea per noi una libreria
wrapper che espone in .Net metodi e proprietà del-
l'oggetto COM sottostante. In un successivo passag-
gio dobbiamo poi inserire nella Casella degli stru-
menti il riferimento al controllo ActiveX che ci ser-
virà per ottenere un Viewer nel quale l'utente può
compiere visivamente le operazioni di OCR sul
documento. L'operazione è simile alla precedente
con la differenza che dobbiamo partire con un click
con il tasto destro sulla casella degli strumenti e poi
selezionare la voce "aggiungi /rimuovi elementi" nel
menu contestuale. Anche qui scegliamo Microsoft
Office Document Imaging Viewer Control nel tab
"COM" dei controlli come possiamo vedere in
Figura 1 e vedremo comparire nella casella degli
strumenti, insieme agli altri controlli Windows
Forms anche l'icona del controllo MODI Viewer ).
Naturalmente l'utilizzo del controllo ActiveX è ne-
cessario solo se, come nel nostro caso, vogliamo di-
sporre di un'interfaccia utente per la gestione di
MODI, si potrebbero avere infatti dei progetti che
utilizzano MODI per processare automaticamente
dei documenti.
+ 38 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Riconoscimento del testo in Visual Basic.NET ■ T SISTEMA
! | BlendPanel
[j^ FileViewer
lilg FolderViewer
O IrmageButton
|"i=l NotificationWindow
13 TaskPane
q^ UtilityToolBar
§| Microsoft FlexGrid Control, version 6.0 (5P6)
^ Microsoft Office Docurment Irnaging Viewer Control 11.0
D
Fig. 2: Il controllo ActiveX nella casella degli stru-
menti
CHIAMARE MODI...
Una volta che abbiamo creato i riferimenti neces-
sari nel progetto vediamo cosa bisogna fare per
utilizzare praticamente la libreria MODI.
Innanzitutto creiamo una, per quanto minimale,
interfaccia utente composta da:
• una Form
• un menu principale con i comandi di base
• una barra di status dove visualizzare i messag-
gi del programma
• e naturalmente ... un controllo MODI Viewer.
puro
File Edit
Ready.
A
Fig. 3: L 'interfaccia utente con evidenziato il control-
lo ActiveX disegnato sulla Form
Adesso che abbiamo tutti gli strumenti vediamo
come interagire da codice con le funzionalità di
OCR. Quello che dovrà fare il nostro programma
di prova sarà:
1. consentire all'utente di scegliere un file su cui
applicare l'OCi?;
2. gestire il processo di OCR segnalandone lo
stato;
3. copiare o salvare l'output prodotto.
Omettiamo per brevità il primo passaggio riman-
dando al codice allegato nel CD l'analisi dei detta-
gli implementativi. Passiamo direttamente al
L'ACQUISIZIONE DA SCANNER
I driver di acquisizione da scanner e
da altre periferiche in ambiente
Windows seguono soprattutto due
standard: TWAIN e WIA (Windows
Image Acquisition Driver).
TWAIN è un API per l'acquisizione
di immagini per i sistemi operativi
Microsoft Windows e Apple Macin-
tosh. La prima specifica per questo
standard fu definita nel 1992, ed at-
tualmente viene seguita la versione
1.9 rilasciata a Gennaio 2000.
TWAIN è tipicamente utilizzato co-
me un'interfaccia tra il software
applicativo ed uno scanner o foto-
camera digitale. La parola TWAIN è
ripresa dalla "Ballata dell'Est e del-
l'Ovest" di Kipling - "...and never
the twain shall meet... ", che riflette
la difficoltà, di connettere scanner
e personal computer. La parola
TWAIN è stata utilizzata per rappre-
sentare lo standard con notazione
maiuscola. Questo erroneamente
ha portato a ritenerla un acronimo
che alcuni hanno interpretato ironi-
camente come "Technology With-
out Ari Interesting Name". Il sito uf-
ficiale di TWAIN è www.twain.org .
WIA è invece un API proprietaria di
Microsoft che persegue più o meno
gli stessi scopi presentandosi come
interfaccia di programmazione la
"Stili Image (STI) architecture" in-
trodotta con Windows Me, Win-
dows XP, e piattaforme successive.
Informazioni su WIA sono reperibili
sul sito : http://msdn.microsoft.com
/library/default.asp?url=/library/en-us
/still/hh/still/WIA intro 1b17ef5f-807a-
4077-9cc3-e8e33b178bf1.xml.asp .
Nessuna delle due tecnologie dispo-
ne di classi dedicate nel .NET frame-
work, tuttavia sono stati sviluppati
due progetti: www.codeproject.com
/dotnet/twaindotnet.asp per TWAIN e
www.codeproject.com/dotnet/wiascripti
ngdotnet.asp per WIA che dimostra-
no la possibilità di utilizzarle anche
dal framework.
punto in cui disponiamo di un file TIFF scelto dal-
l'utente (o meglio del percorso in cui questo file è
memorizzato) , alla selezione dovremmo costruire
un metodo simile a:
Private _MODIDocument As MODI.Document = Nothing
Private Sub SetImage(ByVal filename As String)
' prepara l'immagine..
Try
_MODIDocument = New MODI.Document
_MODIDocument.Create(filename)
AxMiDocViewl.Document = _MODIDocument
I TUOI APPUNTI
AxMiDocViewl.RefreshQ
Catch ex As System. Runtime
.InteropServices.COMException
MessageBox.Show(ex.Message)
End Try
End Sub
Si valorizza cioè un oggetto del tipo MODI.Docu-
ment dichiarato a livello di classe attivandone il
metodo Create passandogli il riferimento al per-
corso del file TIFF. Successivamente assegniamo il
Document appena creato alla relativa proprietà del
controllo ActiveX chiamando inoltre il metodo
Refresh di quest'ultimo.
A questo punto il documento sarà _MODIParame-
ters visibile all'utente nel controllo ed in fase di
esecuzione apparirà l'anteprima che possiamo
vedere in Figura 4.
A questo punto scriviamo il metodo che fa partire
il processo di riconoscimento del testo:
Utilizza questo spazio per
le tue annotazioni
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 39 ►
SISTEMA T ■ Riconoscimento del testo in Visual Basic.NET
IL FORMATO
Il formato TIFF (Tag
Image File Format) è il
formato in cui
vengono archiviati di
solito le immagini
ottenute dagli scanner
o ricevute via Fax. È un
formato non
distruttivo che
permette la
manipolazione dei
singoli canali colore e
dà luogo a files di
grosse dimensioni.
FORMATI
DI IMMAGINE
GESTITI
DA MODI
Stando alla documen-
tazione sembrerebbe
che gli unici formati su
cui MODI sia in grado
di operare il riconosci-
mento caratteri siano
TIFF e MDI (un formato
proprietario), in realtà
nelle nostre prove sia-
mo riusciti a caricare
nel MODI Viewer Con-
trol anche immagini di
numerosi altri formati
grafici tra cui JPG, GIF
e BMP.
File Edit
1
*—.»»
SSSS^SSS^^ 513 ^
*^M mZm- r^**pn~> rn— ■ tM^H 1 1 *H*I i- « **U _
lMnfc*MfclIFpM-hi IjBin <*<*. -r.m— i*r*~"- -*—
,T " n "' - ^— »«— T
hHftoMrtdu òiji&em
zcr^l sa£
Ready.
Fig. 4: L'anteprima del documento caricata nel Viewer
Control
Public Sub Analyse()
If IsNothing(_MODIDocument) Then Return
Try
' gestione dell'evento OnOCRProgress
AddHandler _MODIDocument
.OnOCRProgress, AddressOf ShowProgress
' chiamata a MODI per OCR
_MODIDocument.OCR(_MODIDocument.OCR(
MODI.Mil_ANGUAGES.miLANG_ITALIAN, True, True))
statusBarl.Text = "Ready."
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
Il metodo è semplicissimo: si aggiunge un gestore
dell'evento OnOCRProgress del MODIDocument
(che non è altro che l'oggetto che abbiamo visto
valorizzare da Setlmagé), si chiama il metodo OCR
del MODIDocument stesso e si segnala la fine del-
l'operazione nella StatusBar.
I parametri di configurazione dell'operazione di
OCR assegnati al metodo sono tre:
1. Langld (opzionale) rappresenta la lingua da
utilizzare per l'operazione di OCR, come pre-
definita è miLANG_SYSDEFAULT ovvero quel-
la di sistema. Le lingue riconosciute dall' engi-
ne di MODI sono quelle corrispondenti all'e-
numerazione MiLANGUAGES:
• miLANG_CHINESE_SIMPLIFIED (2052,
&H804)
• miLANG_CHINESE_TRADITIONAL (1 028,
&H404)
• miLANG_CZECH (5)
2.
3.
miLANG_DANISH (6)
miLANG_DUTCH (19, &H13)
miLANG_ENGLISH (9)
miLANG_FINNISH (11)
miLANG_FRENCH (12)
miLANG_GERMAN (7)
miLANG_GREEK (8)
miLANG_HUNGARIAN (14)
miLANGJTALIAN (16, &H10)
miLANGJAPANESE (1 7, &H11)
miLANG_KOREAN (18, &H12)
miLANG_NORWEGIAN (20, 8iH14)
miLANG_POLISH (21, &H15)
miLANG_PORTUGUESE (22, &H16)
miLANG_RUSSIAN (25, &H19)
miLANG_SPANISH (10)
miLANG_SWEDISH (29, &H1D)
miLANG_SYSDEFAULT (2048, &H800)
miLANG_TURKISH (31, &H1F)
OCROrientlmage - (opzionale Boolean) Moda-
lità di correzione automatica della rotazione
del documento. Come valore predefinito assu-
me True.
OCRStraightenlmage - (opzionale Boolean)
Correzione delle distorsioni dell'immagine.
Come valore predefinito assume True.
Ovviamente nel nostro progetto dovremo chiama-
re il metodo Analyse da un gestore di eventi quale,
ad esempio il metodo che gestisce il click su una
voce di menu:
Private Sub miAnalyse_Click(ByVal sender As Object,
ByVal e As System. EventArgs) Handles
miAnalyse. Click
Analyse()
End Sub
COPIARE E SALVARE
Una volta compiuto il processo di OCR il testo
riconosciuto resta associato al file originario tanto
che l'utente può selezionare tutto o parte del testo
Fig. 5: La selezione del testo riconosciuto nell'ante-
prima del documento
* 40 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Riconoscimento del testo in Visual Basic.NET ■ T SISTEMA
nella finestra di anteprima come vediamo in
Figura 5.
Naturalmente questa associazione è gestibile da
codice attraverso la proprietà TextSelection del
controllo MODI Viewer che restituisce un oggetto
del tipo IMiSelectableltem che espone i seguenti
metodi/proprietà a noi utili:
• Metodo CopyToClipboard che naturalmente
copia il testo selezionato negli appunti di siste-
ma.
• Proprietà Text che rappresenta la stringa di
testo riconosciuta.
• Proprietà Words che rappresenta un array di
oggetti MODI. Word , in pratica corrispondenti
alle singole parole del testo riconosciuto.
A questo punto a noi non resta che dare la possibi-
lità all'utente di salvare in un file o negli appunti di
sistema il testo riconosciuto dall' engine OCR.
Lo facciamo con due semplici metodi associati an-
ch'essi a voci di menu. Per salvare negli appunti:
Private Sub miCopy_Click(ByVal sender As Object,
ByVal e As System. EventArgs)
Handles miCopy.Click
' copia nella clipboard..
If IsNothing(AxMiDocViewl. TextSelection) Then
AxMiDocViewl.SelectAII(O)
End If
AxMiDocViewl. TextSelection. CopyToClipboard()
End Sub
Dove se l'utente non ha selezionato nessuna por-
zione di testo si fa partire automaticamente una
selezione di tutta la prima pagina con il metodo
SelectAll del Viewer control passandogli come
parametro il numero di pagina (in questo caso
come primo elemento dell' array delle pagine).
"■"■Ai sensi dell' a|KUo, comma 1, della lega
1996, n.675, i dati personali forniti dai
saranno raccolti presso l'Istituto di Info
Telematica per le finalità strettamente ed
all'operazione di cambio provider del nome)
oggetto e saranno trattati presso una bancl
per Io svolgimento delle operazioni celati]
stessa.
Il conferimento ditali dati all'Istituto
Telematica del CNR è obbligatorio ai fini |
Dopodiché si richiama semplicemente il metodo
CopyToClipboard sulla selezione.
Altrettanto semplice il codice per salvare su file:
Private Sub miSave_Click(ByVal sender As Object,
ByVal e As System. EventArgs)
Handles miSave. Click
' salva ..
If Not IsNothing(_MODIDocument) Then
If IsNothing(AxMiDocViewl. TextSelection) Then
AxMiDocViewl. SelectAII(O)
End If
Dim TextSelection As String =
AxMiDocViewl. TextSelection. Text()
' ometto il codice per creare la finestra di dialogo ..
If saveFileDlg.ShowDialog(Me) =
DialogResult.OK Then
Dim writer As IO.StreamWriter
writer = IO.File.CreateText(
saveFileDIg.FileName)
writer. Write(TextSelection)
writer.CloseQ
End If
End If
Fig. 6: II risultato del nostro OCR copiato in notepad
End Sub
Dove si acquisisce la stringa riconosciuta tramite
la proprietà text della selezione e la si scrive in un
file con i metodi standard di IO di .Net.
CONCLUSIONI
La libreria MODI si presta naturalmente a molte
altre (e più sofisticate) applicazioni pratiche: pen-
sate ad esempio ad un sistema di gestione docu-
mentale che integri insieme all'archiviazione otti-
ca di un documento anche il riconoscimento del
testo che magari può alimentare un database per
integrare un motore di ricerca per ritrovare i
documenti originari.
Certo in uno scenario di questo tipo MODI sconta
una grossa limitazione pratica quella di non gesti-
re l'acquisizione del documento da scanner, in
realtà l'applicazione vera e propria Microsoft
Office Document Imaging di Office 2003 ha questa
possibilità, purtroppo però (non si sa bene per
quale ragione) queste funzionalità non sono state
esposte nell'oggetto COM e quindi restano inac-
cessibili.
Ci sono tuttavia delle interessanti librerie Open
Source, scritte in C# e quindi utilizzabili anche da
Visual Basic .NET, che presentano delle interfacce
agli standard TWAIN o WIA (vedi box "L'acquisi-
zione da scanner") per l'acquisizione di documen-
ti da scanner, in una prossima occasione ci torne-
remo sopra!
Francesco Smelzo
APPROFONDIMENTI
Un help completo sulla
tecnologia MODI è
reperibile all'indirizzo:
http://msdn.microsoft.com
/library/en-us/Mspauto
/html/dihowUsingMODIO
bjectModel HV01 049396
.asp
Francesco Smelzo è
specializzato nello
sviluppo in ambiente
Windows con
particolare riferimento
ad applicazioni in
ambiente .NET sia web-
oriented che desktop.
Il suo sito web è
www.smelzo.it .
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 41 ►
NETWORKING T I Java Internet Relay Chat
Java PircBot
la chat è fatta!
Costruiamo un robot da utilizzare nella gestione di un canale IRC
Vedremo come amministrare gli utenti, sviluppare semplici trigger
e mostreremo come creare un piccolo dee server
QCDQ
CD J WEB
Un_robot_per_amico.zip
>Wjf REQUISITI
O, J2SESDK, PircBot 1.4.4,
TjS Google Web Api
Tempo di realizzazione
^ ^ (s\
/RC (Internet Relay Chat) è uno dei metodi
più diffusi fra i chatters su Internet. Svilup-
pato nel 1988 è un sistema di chat che si ba-
sa su un server centrale e su diversi client che si
connettono a questo server seguendo l'RFC
1459. All'interno di questi server esistono diversi
canali dove poter entrare e discutere. A loro volta
i diversi server entrano in comunicazione fra loro
formando una specie di circuito condiviso dagli
stessi utenti connessi però a server diversi. Ogni
utente sceglie un nickname con il quale viene
identificato univocamente su tutti i server. Inol-
tre c'è la possibilità di gestire e moderare il cana-
le in diverse maniere. Esistono diversi tipi di mo-
derazione: OP e VOICE. Gli OP di un canale sono
i proprietari o comunque quelli che gestiscono il
canale, cambiano il TOPIC (l'argomento), setta-
no limiti e decidono se una persona può rimane-
re nel canale o no. Invece i VOICE sono le uniche
persone che possono parlare (oltre agli OP)
quando il canale è settato in un modalità "mode-
rata". Inoltre esistono diverse versioni di server
ire (ired), tutte aderenti all'RFC 1459 ma con di-
verse funzionalità. Frequentando i diversi server
che sono disponibili in rete si possono incontra-
re canali che trattano i più svariati argomenti.
~ C:\PROGRA~1\XIN0XS~1\JCREAT~1\GE2
1:40:50 2005
1114067782904 : ire .f oonet .con 004 DeLiRiUn ire .f oonet .con Unreal3.2.3 iowghratìsO
RTUSxl
1114067782904 *** Logged onto seruer.
1114067782914 >»JOIN ttjauastaff
1114067782914 : ire .f oonet .con 005 DeLiRiUn StìFELIST HCN MAXCHANNELS=10 CHtìNLIMIT
Ptt:10 MtìXLIST=b:60,e:60,I:60 NICKLEN=30 CHANNELLEN=32 TOPICLEN=307 KICKLEN=307 fì
UAVLEN=307 MAXTARGETS=20 UALLCHOPS UtìTCH=128 :are supported by this seruer
1114067782914 : ire .f oonet .con 005 DeLiRiUn SILENCE=15 M0DES=12 CHANTYPES=tt PREFI
K=<qaohu >"&(?* + CHANMODES=beI ,kf L, lj,psmntirRcOAQKUGCuzNSMTG NETWORK =ROXnet CASEM
APPING=ascii EXTBAN=
MAP,DCCALLOU,USERIP
1114067782924 :irc.f
1 seruers
1114067782924 :irc.f
1114067782924 :irc.f
1114067782924 :irc.f
ELIST=MNUCT STATUSMSG=
upported by tbis seruei
con 251 DeLiRiUn :TJier€
con 253 DeLiRiUn 1 :unl
con 254 DeLiRiUn 1 :chc
con 255 DeLiRiUn :I bai.
INUEX CMDS=KNOCK,
11114067782924 :irc .f oonet .con 265 DeLiRiUn :Current Locai Users
1114067782924 :irc .f oonet .con 266 DeLiRiUn :Current Global Users: 2 Max: 2
1114067782924 :irc .f oonet .con 422 DeLiRiUn :MOTD File is nissing
1114067782924 :DeLiRiUn MODE DeLiRiUn :+iwx
1114067782924 :DeLiRiUn*PircBot(?6DA1959F.10BF5A3F.D41AEED.IP JOIN :ttjauastaff
1114067782934 : ire .f oonet .con 353 DeLiRiUn = ttjauastaff :DeLiRiUn PdBc
1114067782934 : ire .f oonet .con 366 DeLiRiUn ttjauastaff :End of /NAMES list.
11114067782934
E114067782934
Senza perderci nel mare di IRC, delle sue leggen-
de e dei suoi tanti canali passiamo direttamente
all'aspetto che analizzeremo in questo articolo.
Tramite un framework Java realizzeremo un ro-
bot, che è un vero e proprio client del server IRC,
vedremo come poter manovrare questo robot dal
punto di vista della programmazione e poi lo
doteremo di diverse funzionalità interessanti.
PIRCBOT: Ul\l
FRAMEWORK PER BOT
PircBot è un framework opensource per svi-
luppare in Java dei Bot IRC. Grazie a questo
framework possiamo in maniera molto sem-
plice "costruire" un Bot ire, ovvero un softwa-
re che "gestirà" un canale quando noi non sia-
mo connessi. Nel package MifelillJllflBBBBRH
troviamo la definizione delle classi base del
framework, le classi che andremo ad utilizza-
re. La classe base da cui partire è 1 un* imi .
estendendo la quale otterremo dei comporta-
menti specializzati. Vediamo prima di tutto
come far partire il nostro primo Bot e farlo
collegare al nostro server IRC, in un nostro ca-
nale. Il codice da utilizzare è il seguente
import org.jibble.pircbot.*; •-
Fig. 1: L'output che il nostro bot produce quando sì collega ad un server IRC
public class Hello World Bot extends PircBot {•-
public HelloWorldBot(){
this.setName("HelloWorldBot");}
}
Così richiamando il metodo setNameQ abbiamo
deciso quale sarà il nickname che il nostro Bot
utilizzerà sul server IRC. Il prossimo passo sarà
collegare questo Bot ad un server, farlo entrare
in un canale, e chiudere la connessione.
import org.jibble.pircbot.*;
► 42 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Java Internet Relay Chat H ▼ NETWORKING
public class BotExample {
public static void main(String a[]){
HelloWorldBot hwb=new HelloWorldBot();
hwb.connect("irc. azzurra.org"); •-
-•hwb.joinChannel("#javastaff");
hwb.sendMessage("#javastaff","Ciao a tutti");
hwb.disconnectQ; }
diciamo al Bot a qua-
Con il metodo
le server si deve connettere. Una volta che la
connessione è avvenuta facciamo entrare il
bot nel canale EBE S3fì- Il simbolo # è un
prefisso che identifica i canali su IRC (ad
esempio #java, Mtalia, #romd). Appena il bot
entra nel canale mandiamo un messaggio
pubblico su tfjavastaff con il metodo sendMes-
sageQ- Infine ci disconnettiamo dal server e
terminiamo l'esecuzione. Per testare questo
primo esempio dobbiamo inserire nel nostro
classpath la libreria pircbot.jar e chiaramente
avere un collegamento ad internet. I server
dove poter testare questa applicazione sono
tantissimi. Un test per il controllo del buon
esito dell' applicazione è molto semplice.
È sufficiente connettersi con un client come
mire - http://www.mirc.com - o Xchat - http://
www.xchat.org/ - e vedere cosa combina il no-
stro Bot.
IL NOSTRO BOT
La cosa più interessante in un Bot è la possibi-
lità di interagire con le persone che sono pre-
senti nel canale. Per fare ciò dobbiamo poter
semplicemente controllare i messaggi del ca-
nale dove il nostro Bot si trova e parsarli in
maniera corretta. In questo caso ci viene in
aiuto PircBot con la sua libreria. Il formato
con cui i messaggi passano sul canale è ade-
rente alla RFC 1459, e i messaggi assomigliano
a qualcosa del genere:
1113949468186:d0c!d0c@6DA1959E10BF5A3F
.D41AEED.IP PRIVMSG tijavastaff xiao DeLiRiUm
1113949480624:d0c!d0c@6DA1959E10BF5A3F
.D41AEED.IP PRIVMSG #javastaff xhe fai?
Ovviamente un messaggio di questo genere
non è adatto ad essere comprensibile. E tipi-
camente andrebbe "parsalo" e "scomposto"
per ottenere solo le informazioni che ci servo-
no. Non è necessario riprogrammare il parser
usando PircBot, di fatto esiste già il metodo
OnMessageQ nella classe base, che implemen-
ta un parser a tutti gli effetti. Attraverso il me-
todo OnMessageQ è facile usare dei trigger per
riprogrammare a nostro piacimento i com-
portamenti del Bot. Iniziamo quindi a ridefi-
nire il metodo OnMessageQ per avere una rea-
zione da parte del nostro Bot.
public void onMessage(String channel, String sender,
String login, String hostname, String message) {
if (message. equals("Africa"))
sendMessage(channel, "Celestino");
}
In questo modo il nostro Bot quando intercet-
ta la parola ^Africa 11 all'interno di un messag-
gio, scriverà come reazione nel canale la paro-
la "Celestino", usando la primitiva sendMessa-
ge() definita nella classe PircBot.
LA GESTIONE
DEI CANALI
Come abbiamo visto precedentemente, con il
metodo joinChannelQ possiamo far entrare il
nostro Bot nel canale che viene passato come
argomento. Immaginiamo ora di volerlo far
entrare automaticamente in una serie di ca-
nali, definiti magari in un file di testo. Per fare
ciò non dobbiamo far altro che scrivere un
metodo che, chiamato dopo la connessione al
server, legge da un file i nomi dei canali e ri-
chiede, uno per uno, di entrare nei canali.
public void channelJoin() {
canali = new Vector();
try{
BufferedReader br=new BufferedReader(new
FileReader("canali.txt"));
String linea=br.readl_ine();
while(linea!=null) {
bot.joinChannel("#"+linea);
GLI ALTRI BOT IRC
Chi ha frequentato per molto
tempo IRC avrà sicuramente
incontrato dei canali con dei Bot.
Nella maggior parte dei casi non
si tratta di un Bot sviluppato con
PircBot, ma di un suo famoso pre-
decessore, l'Eggdrop. Questo è il
primo esempio di Bot su dei
server IRC, sviluppato in C e con
la possibilità di aggiungere delle
estensioni in TCL. Per poter
utilizzare un Eggdrop dobbiamo
prima di tutto scaricare i
sorgenti, compilarli e poi
modificare il file di
configurazione base. Esistono poi
GOOGLE
WEB API
Per poter utilizzare le
Google Web Api
dobbiamo registrarci
all'uri
www.google.com/apis/ .
Successivamente ci
sarà inviata per email
la chiave da utilizzare
nel nostro programma.
Oltre al webservice di
Google potrebbero
esserne utilizzati tanti
altri per includere
funzionalità nel nostro
Bot. Per questo vi
rimando ad uno dei siti
dove c'è una raccolta
ben fornita di
WebService.
www.xmethods. net/
tantissime TCL da aggiungere
all'Eggdrop, la maggior parte
create per difendere i canali o per
il semplice divertimento degli
utenti. Un altro Bot molto
famoso è l'Iroffer, un Bot che
agisce come un vero e proprio
file server e che permette di
condividere file all'interno dei
canali IRC. Ecco un paio di link
riguardanti questi due famosi
antenati di PircBot
http://www.eggheads.org/
http://www.egghelp.org/
http://iroffer.org/
http://iroffer-lamm .sourceforge.net/
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 43 ►
NETWORKING T I Java Internet Relay Chat
linea=br.readLine();
canali. add(linea); }
br.closeQ;}
catch (Exception e) {}
>
Così tutti i canali che avranno l'onore di avere
come ospite la nostra creatura saranno facil-
mente gestibili tramite questo file di testo.
RICONOSCERE
GLI UTENTI
Uno dei principali motivi per cui si crea un Bot
su dei canali IRC è per gestire il canale, anche
quando non sono presenti i veri moderatori del
canale. Implementeremo un vero e proprio
sistema di identificazione che permetterà sol-
tanto a determinati utenti di utilizzare il nostro
Bot e le sue caratteristiche che poi andremo pia-
no piano ad implementare. Avremo bisogno di
una classe abbastanza semplice, dove imple-
menteremo i metodi get e set per ogni variabile.
Inoltre implementeremo l'interfaccia Serializa-
ble, così potremo serializzare un vettore di uten-
ti e ricaricarlo tranquillamente all'avvio.
import java.util.*;
import java. io.*;
public class User implements Serializable{
String username, password, email, dir;
int level;
public User(String username,String password, String
email, int level, String dir) {
this.username= username;
this.password = password;
this.email=email;
this.level = level;
this.dir=dir; }
public String getllsername() {
return username;}
public void setllsername(String
username) {
this.username=username; }
}
Una volta definito come sarà rappresentato
l'utente dobbiamo decidere un metodo per
far si che gli utenti possano registrarsi. Per fa-
re ciò l'utente dovrà scrivere in query (mes-
saggio privato) al Bot una frase che segue i se-
guente pattern
|— IREGISTER password email
Ridefiniamo il metodo onPrivateMessageQ,
creando prima di tutto uno StringTokenizer
basato sullo spazio, per ottenere così tutte le
singole parole presenti nel messaggio che ci
viene passato.
public void onPrivateMessage(String sender, String
login, String hostname, String message) {
StringTokenizer st=new StringTokenizer(message," ");
String action=st.nextToken();
//REGISTRAZIONE UTENTE
— • if(action.equals("!REGISTER")) {
registerAction(st,sender); }
COME INIZIARE
> CREAZIONE DEL BOT
public class Delirium extends PircBot
Delirium bot = new Delirium();
bot.setVerbose(true);
bot.setName("Del_iRillm");
I Dopo aver incluso la libreria pircbot.jar
Inel nostro classpath creiamo la classe
che estende PircBot e nel main del nostro
programma inizializziamo il Bot.
> CONNESSIONE AL SERVER > LA PRIMA PAROLA
bot . co nnect(" server, ire");
canali = new VectorQ;
try{
BufferedReader br=new BufferedReader(
new FileReader("canali.txt"));
String linea = br.readl_ine();
while(linea! = null) {
bot.joinChannel("#"+linea);
linea = br.readl_ine();
canali.add(linea); }
br.closeQ;}
catch(Exception e) {
}
JJ Colleghiamo il Bot al server IRC passa-
Uto come parametro al metodo connect().
Effettuata la connessione entriamo in tutti i
canali che sono presenti in un nostro di testo.
public void onMessage(
String channel, String sender, String login,
String hostname, String message)
{
if (message.equals("Africa"))
sendMessage(channel, "Celestino");
Facciamo pronunciare la prima parola al-
ila nostra creatura. Implementando il
metodo onMessage() decidiamo un trigger,
in risposta al quale il Bot ci risponde nel canale.
+ 44 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Java Internet Relay Chat H ▼ NETWORKING
In questo modo registriamo l'utente aggiun-
gendolo al vettore degli utenti. Ora rimango-
no da scrivere i due metodi che all'inizio e alla
fine dell'esecuzione del nostro Bot caricherà e
salverà il vettore di utenti registrati, serializ-
zando su file. All'avvio quindi eseguiremo il
seguente metodo
public void caricaUtenti() {
try {
FilelnputStream in =
= new FileInputStream(
"user.ser");
ObjectlnputStream
s = new
ObjectlnputStream(in);
utenti =(Vector) s.readObject();
s.close(); }
catch(Exception e) {
System. out.println(e.toString()); }
}
Così abbiamo caricato nel vettore tutti gli
utenti registrati. Ora definiremo il metodo che
invece sarà richiamato prima della disconnes-
sione del nostro Bot dal server IRC, per salva-
re sul file il vettore di utenti aggiornati. Anche
in questo caso utilizziamo uno stream che ci
permette di manipolare direttamente oggetti
come ObjectOutputStream (e in precedenza
ObjectlnputStream) .
public void salvaUtenti() {
try {
FileOutputStream out = new
FileOutputStream("user.ser");
ObjectOutputStream s = new
ObjectOutputStream(out);
s.writeObject(utenti);
s.close(); }
catch(Exception e) {
System, out. println(e.
toString());
}
}
In questo modo abbiamo completato la ge-
stione degli utenti riconosciuti dal nostro Bot.
INVIO E RICEZIONE FILE
Utilizzando IRC è possibile effettuare uno
scambio diretto di file che viene chiamato
DCC (Direct Client to Client), utilizzato anche
per poter parlare. Chiaramente anche il no-
stro Bot può utilizzare queste funzionalità, es-
sendo lui stesso un client che si collega al ser-
ver IRC. Prima occupiamoci di ricevere i file.
Esiste un metodo definito all'interno della
classe PircBot che ci permette di avere la no-
tifica del file in arrivo. Questo metodo, on-
IncomingFileTransferQ, dovrà essere imple-
mentato nel nostro Bot, per poter accettare i
file in arrivo.
public void onIncomingFileTransfer(DccFileTransfer
transfer) {
File file = transfer.getFileO;
transfer. receive(file, true);
}
Il file che abbiamo accettato di ricevere verrà
direttamente copiato nella cartella dove è in
esecuzione il nostro Bot. In questo modo po-
tremmo anche creare un repository per tutti
> CARICARE GLI UTENTI
> GESTIONE DEI FILE
> RICERCHE CON GOOGLE
public void caricaUtenti() {
try {
FilelnputStream in = new
FileInputStream("user.ser");
ObjectlnputStream s = new
ObjectlnputStream(in);
utenti =(Vector) s.readObject();
public void onIncomingFileTransfer(
DccFileTransfer transfer)
{
File file = transfer.getFileO;
WebSearch ws=new WebSearch(O);
Vector result=ws.search(toSearch);
sendMessage(channel,sender+" ecco
i risultati della tua ricerca");
for (int i=0;i<result.size();i++)
{
String temp=(String)result.elementAt(i);
sendMessage(channel,temp);
>
transfer. receive(file, true);
>
s.close();
}
catch(Exception e) {
System. out. println(e.toString());
else if (action. equals("!REGOLE"))
{
dccSendFile(f,sender,120000);
}
}
}
W\ Carichiamo una lista di utenti che s
Usono precedentemente registrati.
Andiamo a leggere il vettore degli utenti di-
rettamente da un file, dove li abbiamo sal-
vati nella precedente sessione.
n Per ricevere un file dobbiamo imple-
Umentare onlncomingFileTransferQ e
accettare il trasferimento. Per l'invio utiliz-
ziamo dccSendFileQ, indicando l'utente, il
File e il timeout.
^9 Creiamo una class WebSearch utiliz-
LaJzando le Google Web Api. Passiamo co-
me parametro le parole inviate dall'utente
nel canale. Comunichiamo i risultati tramite
la primitiva sendMessageQ.
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 45 ►
NETWORKING T I Java Internet Relay Chat
PircBot è scaricabile
dal sito
http://www.jibble.org
/pircbot.php
dove potete trovare
della documentazione
e molti link a progetti
che hanno utilizzato
questo framework per
sviluppare un Bot.
gli utenti in un canale. Ad esempio in un ca-
nale che parla di esami universitari si potreb-
bero salvare le dispense utili per poter passare
l'esame. In generale la tecnica per inviare file
è simile a quella della ricezione solo che dob-
biamo indicare una persona alla quale inviare
il file. In questo caso permetteremo al nostro
Bot di essere attivato dal trigger .'REGOLE per
poter inviare un file con le regole del canale a
chi le ha richieste. Inseriamo quindi il trigger
nel metodo onMessageQ che già abbiamo im-
plementato e poi inviamo direttamente un file
di regole caricato all'avvio del programma
else if (action.equals("!REGOLE")) {
dccSendFile(f,sender,120000);
}
Da notare che noi dobbiamo semplicemente
richiamare il metodo dccSendFileQ passando
come parametro il file, chi ha attivato il trigger
IREGOLE e un timeout per la richiesta.
ALTRE FUNZIONALITÀ
DI BASE
Oltre a quello che abbiamo già implementato
dobbiamo permettere al nostro Bot di svolge-
re le normali funzioni di utente IRC. Nel no-
stro caso specifico è interessante vedere come
possiamo comandarlo per rendere altri utenti
OP del canale o levare YOP del canale a qual-
cuno. Nei canali IRC che non abbiano una ge-
stione tramite Bot o tramite particolari fun-
zionalità di registrazione del canale sull'IRCD
ci sono elevate probabilità di perdere il cana-
le a causa di scorribande di persone che cer-
cano di rubarlo. Per gestire queste funziona-
lità base ci sono delle semplici metodi, già de-
finiti in PircBot, che devono essere semplice-
mente richiamati. Quindi come già abbiamo
fatto in precedenza inseriamo dei trigger nel
metodo onMessageQ
if (action. equals("!OP")) {
opAction(st,sender,channel); }
else if(action.equals("!DEOP")) {
deopAction(st,sender,channel); }
Poi definiremo i due metodi che prima di ese-
guire l'ordine eseguiranno un controllo sull'u-
tente. Infatti dobbiamo chiaramente controlla-
re che l'utente sia autorizzato ad eseguire de-
terminate azioni. Questo controllo è possibile
facendo identificare l'utente in query (mes-
saggio privato) col Bot. In questo messaggio l'u-
tente comunicherà al Bot la sua password e il
Bot vedendo se l'utente è registrato lo inserirà
tra gli utenti identificati. Ecco quindi la defini-
zione di uno dei due metodi richiamati prima
public void opAction(StringTokenizer st,String
sender,String channel) {
String who=st.nextToken();
if (utentildentificati.contains(sender))
op(channel,who);
}
Richiamando il metodo op() (e il metodo
deopQ nell'altro caso) comunichiamo al ser-
ver di rendere moderatore l'utente indicato.
Chiaramente tutti gli utenti possono registrar-
si e identificarsi, quindi potrebbero rubarci il
canale. Questa parte può essere implementa-
ta in diversi modi, quindi la lascio volutamen-
te non protetta. Infatti potremmo pensare ad
una registrazione lato web oppure ad un solo
utente (il primo) che può registrare pian pia-
no altri utenti. Questo dipende dalle esigenze
del canale che vogliamo gestire e quindi va ol-
tre allo scopo dell'articolo. Ora che abbiamo
implementato delle funzionalità base per il
nostro Bot incominciamo a divertirci, portan-
do nel nostro canale dei servizi di ricerca che
potrebbero risultare interessanti per gli utenti
nel canale.
GOOGLE E TUO AMICO
Abbiamo visto come sia semplice comandare
il nostro Bot tramite dei trigger, passandogli
anche dei parametri. Ora cerchiamo di svilup-
pare un'estensione che si interfacci al motore
di ricerca Google. L'idea dalla quale vogliamo
partire è quella di poter fare le ricerche su
Google anche quando sei in chat con altre
persone. Per potersi interfacciare con questo
motore di ricerca esistono già da tempo le
Google Web Api, ovvero delle API che ci per-
mettono di effettuare ricerche su Google gra-
zie ad un WebService che viene esposto. Una
volta registrati sul sito http://www.google.com
lapis/ ci viene inviata via email una chiave da
utilizzare nel nostro programma. Questa ser-
ve per limitare l'accesso al WebService, offren-
do massimo 1000 ricerche al giorno. Quindi
prima di interfacciare Google con il nostro
Bot costruiamoci una classe per effettuare la
vera e propria ricerca. Prima di tutto dobbia-
mo importare i seguenti package
import com. google. soap.search.GoogleSearch;
import com. google. soap.search.GoogleSearchResult;
import com. google. soap.search
y 46 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Java Internet Relay Chat H ▼ NETWORKING
.GoogleSearchResultElement;
della tua ricerca");
import com. google. soap.search.GoogleSearchFault;
Ora dobbiamo semplicemente istanziare la
classe base per le ricerche ovvero GoogleSearch
GoogleSearch search = new GoogleSearch();
search.setProxyHost("192.168.1.1");
search. setProxyPort(3128);
search. setKey(
"AhB7sA5QFHJ9ZasdH8ALLLOPOGBeTrQFe4L");
search. setQueryString(toSearch);
Oltre ad istanziare GoogleSearch bisogna set-
tare la chiave che ci è stata inviata via email
con il metodo setKeyO- Poi possiamo anche
settare un proxy, e relativa porta, attraverso il
quale vogliamo effettuare la ricerca ed infine
con il metodo setQueryStringO settiamo la
stringa che verrà ricercata su Google. Così ab-
biamo costruito la richiesta, ora dobbiamo
collegarci al Web Service e salvare i risultati (10
per default) in un vettore
Vector toReturn = new Vector();
GoogleSearchResult result = search. doSearch();
GoogleSearchResultElement[] re =
result. getResultElements();
for ( int i = 0; i < re.length; i++ ) {
toReturn.add(re[i].getURL());
Ora tutti risultati della ricerca sono presenti
all'interno del vettore. Possiamo quindi facil-
mente integrare questo servizio all'interno
del nostro Bot. Prima definiamo un trigger
nella solita maniera
else if (action.equals("!GOOGLE")) {
searchAction(st,sender,channel);
}
poi definiamo il metodo searchActionQ richia-
mando questa classe che abbiamo implemen-
tato. Praticamente avremo un vettore come
risultato quindi in questo metodo basterà
eseguire un ciclo for e stampare nel canale
tutti i risultati
public void searchAction(StringTokenizer st,String
sender,String channel) {
try{
String toSearch=st.nextToken();
while(st.hasMoreTokens())
toSearch=toSearch+" "+st.nextToken();
WebSearch ws=new WebSearch(O);
Vector result=ws.search(toSearch);
sendMessage(channel,sender+" ecco i risultati
for (int i=0;i<result.size();i++) {
String temp=(String)result.elementAt(i);
sendMessage(channel,temp); } }
catch(Exception e) { }
>
Il risultato di questa estensione la possiamo
ammirare nell'immagine qui a fianco. Allo
stesso modo in cui abbiamo inserito la ricerca
su Google possiamo inserire una ricerca avan-
zata su siti specializzati in articoli Java.
Sfrutteremo la possibilità che offre Google di
segnalare su quale sito effettuare una ricerca
con l'opzione site:nomesito. Ecco il semplice
metodo che riutilizza la normale ricerca
public Vector searchAdvanced(String toSearch){
String toSearchl="site: www.javaworld.com
"+toSearch;
String toSearch2="site:www.javalobby.org "+toSearch;
String toSearch3="site: www. alphaworks.ibm.com
"+toSearch;
tipo=0;
Vector temp=search(toSearchl);
temp=search(toSearch2);
temp=search(toSearch3);
return temp;
}
Sempre allo stesso modo definiremo un trig-
ger e il metodo associato per parsare la richie-
sta e restituire i risultati.
* Nou talking in ttjauastaff
<d«c> TGOOGLE jaua
<DeLiRiUn> dBc ecco i risultati della tua ricerca
<DeLiRiUn> http://jaua.sun.con/
<DeLiRiUn> http://jaua.sun.con/docs/books/tutorial/
<DeLiRiUn> http ://www. jaua .corn/en/download/ Windows automa tic . jsp
<DeLiRiUn> http://www.jaua.con/
<DeLiRiUn> http://www.anfytean.con/jaua/
<DeLiRiUn> http://jauaboutique.internet.con/
|<DeLiRiUn> http ://www. jauaworld .con/
.blackdown.org/jaua-linux.htnl
.deueloper. con/ jaua/
.nicrosoft .con/ nscorp/ jaua/
<DeLiRiUn> http://w>
<DeLiRiUn> http://w>
<DeLiRiUn> http://w>
Fig. 2: Risultato della ricerca su Google
CONCLUSIONI
Nel codice allegato alla rivista trovate anche
un'altra funzionalità, un semplice invio di
email tramite il Bot. Come avete potuto vede-
re si possono realizzare tantissime cose e per-
sonalizzare in maniera esclusiva un proprio
Bot. Nell'homepage di PircBot potete trovare
tanti progetti che hanno scelto come punto di
partenza questo framework ed hanno svilup-
pato dei Bot con delle interessantissime fea-
ture. Pircbot è un ottimo strumento per sup-
portare la nostra creatività.
Federico Paparoni
L'AUTORE
Federico Paparoni, può
essere contattato per
suggerimenti o
delucidazioni
all'indirizzo email
federico.paparoni@
javastaff.com
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 47 ►
GAMING T
Pixel Shader
Il pixel che ti
cambia i connotati
Alla scoperta del Pixel Shader, per realizzare animazioni vicine
alla qualità cinematografica, da utilizzare all'interno di videogames
o per creare presentazioni spettacolari
CI CD □ WEB
.zip
ro """"""" c """"""'
n
REQUISITI
H.I.I.W.MÌ.IW.H.M
i— ] Basi di C++, DirectX
Microsoft Visual C++,
Direct 9.1
E^j g^jj 1^3 _
Tempo di realizzazione
M f-0 CO
Le immagini che normalmente vediamo
sono il risultato dell'incidenza dei raggi lu-
minosi sulla retina, l'organo preposto alla
trasformazione di questi stimoli in impulsi da
inviare al cervello. I "raggi luminosi" sono onde
elettromagnetiche che viaggiano in un certo
campo di frequenze, detto, per evidenti motivi,
"campo del visibile". Il cammino di queste onde
comincia da una fonte luminosa, ad esempio il
Sole o una normale lampadina; passa attraverso
l'interazione con corpi solidi, che ne modificano
alcune caratteristiche, come il colore da noi per-
cepito e, infine, raggiunge i nostri occhi. Le rego-
le di queste interazioni sono tutte abbastanza
note nel campo scientifico e riproducibili attra-
verso computer.
Inutile dire che il sogno di ogni appassionato di
computer grafica in tempo reale sarebbe quello
di potere realizzare un algoritmo che calcoli "al
volo" il risultato di queste interazioni. In questo
modo si riuscirebbe ad ottenere una grafica asso-
lutamente fotorealistica per giochi, presentazio-
ni multimediali ecc. senza ricorrere ai vari "stra-
tagemmi". Purtroppo i moderni computer non
hanno ancora raggiunto l'efficienza di calcolo
del cervello umano. Perciò ci dobbiamo accon-
tentare di una simulazione ancora non del tutto
credibile. Il problema principale sta, come si può
intuire, nell'incredibile quantità di dati necessa-
ria per questi calcoli. Quantità che rende pratica-
mente impossibile, con le potenze di calcolo
odierne, ottenere effetti "in tempo reale". Esisto-
no però tecniche che consentono di avvicinarsi
molto a questo obiettivo di realismo, e quasi tut-
te fanno uso dei Pixel Shader.
I PIXEL SHADER
Qualcuno di voi avrà sentito parlare di Vertex
Shader. Per quelli che si sono persi il numero
scorso di ioProgrammo, è bene ricordare che si
tratta di una tecnica che consente di agire sui
singoli vertici dei poligoni che approssimano
una forma tridimensionale al fine di creare ef-
fetti spettacolari.
Il Pixel Shader si affianca al Vertex Shader e per
certi versi si può ritenere ancora più preciso.
Agisce infatti sul singolo Pixel che compone
una scena tridimensionale al fine di stabilirne
l'aspetto corretto e di simulare come il nostro
occhio percepirebbe quel pixel se, anziché
vederlo dentro un video, fosse parte di una sce-
na reale. Per ogni pixel sul monitor, viene stabi-
lito il colore in base a informazioni prese di-
rettamente dalla scena 3D come ad esempio le
caratteristiche del materiale che compone l'og-
getto visualizzato, l'angolo formato dalla dire-
zione dello sguardo e la sorgente luminosa ecc.
I Pixel Shader sono manipolabili attraverso veri
e propri mini-programmi, che possono essere
"caricati" ed "eseguiti" all'interno di codice
scritto in linguaggi come C++ o VB. Il linguaggio
che si utilizzava inizialmente per questi mini-
programmi era una specie di assembler, decisa-
mente poco leggibile.
Successivamente è stato introdotto un linguag-
gio molto più simile al C, che è presente in due
versioni, sostanzialmente uguali tra loro. Si
tratta di Cg di NVidia e HLSL di Microsoft.
I FILE AD EFFETTO
Programmare un Pixel Shader è una cosa abba-
stanza semplice, una volta che si comprende a
fondo la logica di come agiscono.
Cominciamo descrivendo un semplice effect file,
cioè il sorgente di un Pixel Shader che viene uti-
lizzato all'interno di altri programmi.
// matrici passata dall'applicazione "ospite"
+ 48 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Pixel Shader
T GAMING
float4x4 matWorldViewProj: WORLDVIEWPROJECTION;
float4x4 matWorld : WORLD;
// struttura per I dati di ciascun vertice
struct VS_OUTPUT
jC
float4 Pos : POSITION;
float3 Light : TEXCOORDO;
float3 Norm : TEXCOORD1;
};
// Vertex Shader - per ogni vertice riempie la relativa
// struttura
VS_OUTPUT VS(float4 Pos : POSITION, float3 Normal
: NORMAL)
s
VS_OUTPUT Out = (VS_OUTPUT)0;
Out.Pos = mul(Pos, matWorldViewProj);
// trasforma la posizione
Out. Light = normalize(vecLightDir);
// vettore raggio luminoso
Out. Norm = -normalize(mul(Normal, matWorld));
// normale
return Out;
// Pixel Shader - Calcola il coloe di ciascun pixel in
// base al raggio luminoso e alla normale
// di ciascun vertice
float4 PS(float3 Light: TEXCOORDO, float3 Norm :
TEXCOORD1) : COLOR
s
// Intensità della luce ambientale
float Aintensity=0.2f;
// Colore della luce ambientale
float4 Acolour=float4(0. 1,0. 1,0. 1,1.0);
// Intensità e colore variabili
float Dintensity=1.0f;
float4 Dcolour=float4(l. 0,0. 6,0. 6,1.0);
// Calcola il colore del pixel utilizzando il prodotto
// scalare tra raggio luminoso e normale del vertice
float4 result=Dintensity*Dcolour*(dot(Norm, Light));
// Add the diffuse result to the ambient below for
return
return Aintensity*Acolour+result;
>
Questo codice permette di visualizzare una mesh
come se fosse di un materiale opaco di colore
arancione carico, simile al rame.
Analizziamo di seguito la struttura del file appe-
na presentato.
ANATOMIA
DI Ul\l EFFECT FILE
I file fa sono organizzati in maniera abbastanza
rigorosa e ricordano la struttura di un program-
ma C. Le tre sezioni principali sono le seguenti:
• Dichiarazioni di variabili
• Funzioni
• Technique
La prima sezione è abbastanza esplicativa: qui
vengono dichiarate tutte le variabili o le strutture
che saranno utilizzate nel codice seguente.
Ad esempio nel nostro caso con la riga:
float4x4 matWorldViewProj: WORLDVIEWPROJECTION;
stiamo istruendo il compilatore a considerare
l'identificatore univoco matWorldViewProj
come una matrice 4x4 di float (float4x4). Il valo-
re finale di questa variabile sarà utilizzato come
matrice di proiezione tra le coordinate del
mondo (world) e quelle della visuale (view).
Questo è specificato attraverso la parola chiave
WORLDVIEWPROJECTION.
Le funzioni sono esattamente analoghe alle
funzioni presenti in tutti i linguaggi di program-
mazione. Attraverso un nome identificativo è
possibile svolgere una generica parte di codice,
specificando i parametri di ingresso e quelli di
uscita.
Nel nostro caso la funzione che esegue i calcoli
per il Pixel Shader è definita come segue:
float4 PS(float3 Light: TEXCOORDO, float3 Norm :
TEXCOORD1) : COLOR
{
>
DOVE OSANO
I PIXEL
SHADER
I Pixel Shader entrano
in gioco, nell'elabora-
zione dell'immagine
3D, dopo le trasforma-
zioni geometriche e
prima del disegno
finale dell'immagine.
La sequenza di tutti
questi procedimenti è
detta "pipeline
grafica", mentre il
disegno vero e proprio
dell'immagine è detto
"rasterization ".
La rasterization viene
effettuata come
operazione finale e si
occupa di applicare
tutti gli effetti, anche
quelli 2D come ad
esempio Vantialiasing.
Il Definisce la technique da usare
technique TVertexAndPixelShader
{
pass PO // singolo passo di elaborazione
{
VertexShader = compile vs_l_l VS();
PixelShader = compile ps_l_l PS();
Il valore restituito dalla funzione sarà un vettore
di 4 float (float4) al quale sarà associato il colo-
re (COLOR) del pixel per il quale la funzione
viene eseguita. I parametri in ingresso, tra
parentesi tonda, sono il vettore luminoso e la
normale del vertice, entrambi vettori di 3 float
(float3). Questi sono passati utilizzando l'asso-
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 49 ►
GAMING T
Pixel Shader
ciazione ai registri TEXCOORDO e TEXCOORD1,
fatta nella dichiarazione.
Le sezione dedicata alle technique definisce il
nome di una tecnica da utilizzare per applicare
lo shader. All'interno di ciascuna tecnica è pos-
sibile specificare più di un passo di elaborazio-
ne, con la parola chiave "pass".
float4Acolour=float4(0.1,0. 1,0.1,1-0);
definiscono l'intensità e il colore della luce am-
bientale. È sempre bene tenerne conto per non
fare risaltare troppo la mesh cui lo shader è ap-
plicato, rispetto allo "sfondo".
Successivamente con:
float Dintensity=1.0f;
LO SHADER
La funzione che effettua il calcolo vero e proprio
del valore finale del colore del pixel è PS().
Le righe:
float Aintensity=0.2f;
float4 Dcolour=float4(1.0, 0.6,0.6,1.0);
float4 result=Dintensity*Dcolour*(dot(Norm,Light));
si definiscono intensità e colore variabili del pixel
e si stabilisce il colore finale moltiplicando questi
valori per il prodotto scalare tra vettore luminoso
e normale.
GLI SHADER
OGGI
Lo stato attuale della
tecnologia consente
l'utilizzo di shader che
inglobano diverse
caratteristiche dei
linguaggi di
programmazione
comuni (ad esempio la
presenza del costrutto
"if-then-else").
La versione supportata
dall' hardware di
ultima generazione è
la 3.0. Tuttavia le
potenzialità degli
shader sono ancora per
la maggior parte da
esplorare, lasciate alla
creatività e capacità
dei programmatori. C'è
addirittura chi prevede
in futuro una
congiunzione tra
Vertex e Pixel Shader
in una sola entità.
CELL SHADING
In questo minitutorial realizzeremo qualcosa di molto vicino
a un cartone animato. Seguiteci e i risultati saranno sorprendenti
> COSA È IL CELL SHADING?
"Celi Shading" è un effetto visivo che am-
bente di disegnare la scena 3D come se fosse un
cartone animato. Questa tecnica è stata resa celebre
dal videogioco "XIII " e risulta di grande impatto.
> EFFETTI DI LUCE
// Divisione in 3 "soglie"
if (result
> 0.6) {
result
= 0.6;
}
else if (result > 0.3) {
result
= 0.3;
}
else {
result
= 0.0;
}
result * =
Dintensity*Dcolour;
I II segreto di questo effetto sta nel dividere
l in "soglie" i valori dell'incidenza del raggio
luminoso. Successivamente si assegna a "result"
un valore fisso, in base alla soglia in cui ricade.
> VIA CON LA PRIMA FORMULA
// Calcola il colore del pixel
// utilizzando il prodotto scalare
// tra raggio luminoso e normale
// del vertice
float result=dot(Norm,l_ight);
I Possiamo modificare il nostro codice in mo-
Ido da ottenere un effetto simile al Celi Sha-
ding. Per farlo agiamo sulla variabile "result" asse-
gnandole semplicemente il solito prodotto scalare.
> IL RISULTATO FINALE
J Lo stile "cartoon" è dato proprio dalla pre-
senza di campiture uniformi di colore, tipiche
dei fumetti. L'effetto che si ottiene è quello di una
figura disegnata a mano in bianco e nero.
* 50 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Pixel Shader
T GAMING
* COME PROVARE L'ESEMPIO
Per provare lo shader potrem-
mo scrivere un piccolo pro-
grammino con DirectX che
carichi il codice e lo applichi a
una mesh scelta da noi.
Una via molto più breve,
descritta anche nel preceden-
te articolo sui Vertex Shader,
è quella di utilizzare Effect
Edit, un tool presente nel
DirectX 9.0 SDK scaricabile a
partire dal sito
www.microsoft.com .
Caricando questo shader in
EffectEdit noteremmo subito
il risultato dei nostri sforzi.
E, modificando "al volo" il
codice che compare a scher-
mo, potremmo subito verifi-
care gli effetti di eventuali
cambiamenti.
Tanto per prenderci gusto
aggiungiamo nella funzione
PSQ le seguenti righe prima
„:k -.,■:..: ',.■'': s..;.lnr: r '.-el : : de
L'effetto viene ricompilato ai voto da Effect Edit e il risultato che si può
vedere è quello di una certa "lucidità" della mesh, rispetto a prima
del return:
if
(dot(Norm
,Light) > 0.6) {
result * =
1.3;
}
L'effetto viene ricompilato al
volo da Effect Edit e il risul-
tato che si può vedere è
quello di una certa "lucidità"
della mesh, rispetto a prima.
L'effetto è ottenuto aumen-
tando il valore dell'intensità
luminosa data da "result" di
un 30%, nel caso in cui il pro-
dotto tra Norm e Light sia
maggiore di un certo valore
di soglia (fissato a 0.6).
Questo è tipico dei materiali
lucidi, ad esempio i metalli,
che tendono a riflettere la
luce quasi totalmente quan-
do questa incide in maniera
diretta. Da notare, in ogni
caso, come un effetto com-
pletamente diverso sia stato
ottenuto modificando mini-
mamente il codice dello sha-
der. Questo è un grande van-
taggio dei Pixel Shader.
Questo valore è tanto più alto quanto la luce
"incide" sul pixel in maniera diretta.
L'effetto finale che si ottiene è proprio quello di
un oggetto abbastanza realistico, di un materia-
le piuttosto opaco.
Da notare come oltre a PSQ si è definita anche la
funzione VSQ che è un Vertex Shader (cioè un
manipolatore della geometria delle mesh).
Questi tuttavia non fa altro che applicare le tra-
sformazioni di default, per cui non compie nes-
sun lavoro "utile".
Gli shader così definiti vengono applicati trami-
te le direttive "compile" specificate nella te-
chinque.
In particolare le righe:
VertexShader = compile vs_l_l VS();
PixelShader = compile ps_l_l PS();
compilano gli shader con le funzioni PSQ e VSQ,
utilizzando le versioni 1.1 degli shader stessi.
CONCLUSIONI
Abbiamo visto in questo articolo come scrivere
il codice di un semplice Pixel Shader e come
provarlo immediatamente utilizzando il tool Ef-
fect Edit di DirectX. È facile intuire come picco-
le modifiche nei punti giusti dello shader possa-
no portare a risultati di grande effetto, anche
molto differenti tra loro.
Per una ulteriore modifica dello shader si legga
anche il tutorial riportato in queste pagine.
I codici completi sono presenti in ogni caso sul
CD allegato.
Alfredo Marroccelli
CARICARE UM FILE .FX CON DIRECTX 9
file .fx consentono di concentrare,
in una sorta di script praticamente
tutte le operazioni richieste per
inizializzare un effetto grafico.
Questo consente di eliminare
molte parti di codice C++, evitando
noiose e lunghe fasi di
ricompilazione. Utilizzando DirectX
9 il caricamento di un effetto si
riduce alla chiamata della funzione:
HRESULT WINAPI
D3DXCreateEffectFromFile(
LPDIRECT3DDEVICE9 pDevice,
LPCTSTR pSrcFile,
const D3DXMACRO *pDefines,
LPD3DXINCLUDE plnclude,
DWORD Flags,
LPD3DXEFFECTPOOL pPool,
LPD3DXEFFECT *ppEffect,
LPD3DXBUFFER
*ppCompilationErrors
);
pDevice è il puntatore al
dispositivo IDìrect3DDevìce9 che
rappresenta la scheda grafica;
pSrcFile è il nome del file che
contiene l'effetto e ppEffect
rappresenta l'effetto vero e
proprio come oggetto C++.
Gli altri parametri sono principal-
mente opzioni di compilazione
dell'effetto e possono essere
lasciati a NULL oppure 0, per effetti
semplici.
Per una guida più approfondita
consigliamo direttamente il sito
ufficiale http://www.microsoft.com
/windows/directx/default.aspx
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 51 ►
GRAFICA T I Estendere una datagrid
Le immagini le
faccio alla griglia
Sfruttiamo la potenza della programmazione ad oggetti per
estendere le potenzialità del Datagrid control di .NET. Creiamo una
galleria di thumbnail mostrando delle immagini nelle celle
QCDQ
CD J WEB
JO
REQUISITI
■«Ai.wj.ijjjjj.m4a
ra| I Principi di .NET
NET Framework, Visual
Studio 2003
\zM\zM\zM.
Tempo di realizzazione
fVÌ (VI
Molti programmatori che si addentra-
no nello sterminato mondo del
.NET Framework, magari provenen-
do dal buon vecchio VB6, si trovano disorien-
tati nell'uso dei controlli standard forniti dal
Framework e, molto spesso, rimangono mera-
vigliati dalla mancanza di funzionalità che a
prima vista sembrerebbero ovvie in un con-
trollo che si rispetti. Molti casi di "disorienta-
mento del programmatore" sono da attribuir-
si proprio al famoso (o famigerato) controllo
Datagrid, quello che viene comunemente uti-
lizzato per mostrare in tabelle dei dati magari
provenienti da un database. A prima vista
infatti sembrerebbe che nelle celle della tabel-
la di un Datagrid potrebbero trovar posto solo
testo o al massimo dei miseri Checkbox.
Il fatto è che Microsoft ha seguito, nella pro-
gettazione dei controlli standard, il paradig-
ma della programmazione ad oggetti, ovvero:
è inutile introdurre centinaia di funzionalità
in un controllo, tanto mancherà sempre quel-
la che ci serve, meglio allora fornire un "semi-
lavorato" personalizzabile a piacere. Questo
approccio accresce senza dubbio la flessibi-
lità. Il rovescio della medaglia è però che per
sfruttare in pieno questa flessibilità occorre
rimboccarsi le maniche per introdurre funzio-
nalità "estendendo" gli oggetti base. Estremiz-
zando proprio il concetto di estensione vedre-
mo in questo articolo come utilizzare un con-
trollo base, quale appunto la Datagrid, per
fare da contenitore non a dati bensì ad imma-
gini.
Il nostro obiettivo sarà quello di realizzare un
programma in grado di visualizzare sotto for-
ma di thumbnail le immagini contenute in
una directory del file system.
Naturalmente la tecnica illustrata si presta a
molte "variazioni sul tema" come, ad esempio,
inserire delle icone nella griglia dei dati ecc..
IL CONTROLLO
DATAGRID
System. Windows. Forms.DataGrid è un ogget-
to complesso che ha:
• un'origine dati (Dataset, Datatable, Data-
view, Matrice ecc.);
• degli oggetti DataGridTableStyle che con-
trollano l'aspetto delle griglie e apparten-
gono alla collection GridTableStylesCollec-
tion;
• a loro volta ogni oggetto DataGridTable-
Style ha una collection di oggetti che deri-
vano dall'oggetto astratto DataGridCo-
lumnStyle.
Gli oggetti derivanti da DataGridColumnStyle
che vengono comunemente impiegati nella
griglia sono DataGridTextBoxColumn (che
controlla le colonne con celle contenenti del
testo) e DataGridBoolColumn (che controlla
le colonne con valori True/False rappresentati
con checkbox). Fin qui quello che "passa il
convento" ovvero delle celle con textbox o
checkbox, ma il bello è che nulla ci vieta (an-
zi!) di creare un nostro oggetto derivante da
T^rTir*!
DataGridTableStyle
■M
Nome/
Cognome
indirizzo
*
\
\
\
V '
Dat*G ridColum nSty le
Fig. 1: Schema componenti Datagrid
+ 52 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Estendere una datagrid ■ v GRAFICA
DataGridColumnStyle per mettere nelle celle
tutto quello che vogliamo. Per far questo ab-
bandoniamo prima di tutto l'idea di lavorare
in ambiente visuale: il tutto deve essere gesti-
to scrivendo del codice, comunque vi accor-
gerete che l'operazione è più semplice di
quanto possiate pensare.
UNO STILE
PERSONALIZZATO
Mano al codice quindi, e andiamo a creare nel
progetto una nuova classe, che chiameremo
CellExt che eredita da DataGridColumnStyle:
Public Class CellExt
Inherits DataGridColumnStyle
Per motivi di spazio rimandiamo l'analisi del-
la struttura della classe CellExt al codice sor-
gente allegato nel CD, l'importante è aver
chiaro che la nostra classe è chiamata in cau-
sa ogni volta che la fonte dei dati passa al Da-
tagrid una path; in questo caso CellExt si oc-
cuperà di disegnare in quella determinata cel-
la l'immagine che rappresenta il Thumbnail di
quella originale. Il tutto sta nel metodo Paint
che compie le seguenti operazioni:
1. Dato il numero di riga trova il valore corri-
spondente nella fonte di dati (ovvero la
path di un'immagine).
2. Carica in memoria l'immagine.
3. Ridimensiona l'immagine proporzional-
mente alla grandezza della cella (che nel
nostro esempio abbiamo fissato in 100 X
100 pixel).
4. Disegna l'immagine nello spazio assegna-
to alla cella.
Il metodo si presenterà quindi grosso modo
così:
thumb = bmp.GetThumbnailImage(ResizeSize.Width,
ResizeSize.Height, Nothing, IntPtr.Zero)
Dim thumbX As Integer = bounds.X +
((bounds.Width - thumb.Width) / 2)
Dim thumbY As Integer = bounds.Y +
((bounds.Height - thumb. Height) / 2)
Dim r As New Rectangle(thumbX, thumbY,
thumb.Width, thumb. Height)
'disegna l'immagine nel rettangolo
g.DrawImage(thumb, r)
End Sub
Precisiamo che la funzione GetResizeSize l'ab-
biamo definita noi per calcolare la riduzione
proporzionale dell'immagine Thumbnail in
relazione allo spazio a disposizione. La fun-
zione GetColumnValueAtRow con cui invece
troviamo la path del file viene invece gentil-
mente fornita da DataGridColumnStyle da cui
la nostra classe eredita. Naturalmente nella
pratica le cose sono un po' più articolate e nel
codice sorgente di esempio presente nel CD
abbiamo messo anche il Caching delle imma-
gini per limitare i tempi di caricamento ed al-
tre cose a corredo, comunque grosso modo le
operazioni sono queste.
L'INTERFACCIA
DEL PROGRAMMA
A questo punto restano da compiere altre due
operazioni: creare una fonte di dati per la griglia
e dirle che i dati devono essere gestiti dalla nostra
classe CellExt. Cominciamo con il disegnare nel
progetto una Form con tutto il necessario:
1. Una Textbox che ospita la path della direc-
tory da visualizzare.
2. Un Button che apre una finestra di dialogo
che consenta all'utente di scegliere la di-
rectory.
3. Una Datagrid.
DATAGRID
E STILI
Il controllo System.Win-
dows.Forms.DataGrid
visualizza i dati in for-
ma di griglia. La classe
DataGridTableStyle rap-
presenta la griglia dise-
gnata. Questo oggetto
è da non confondere
con la classe DataTable
che può rappresentare
una origine di dati per
la griglia. La classe
DataGridTableStyle,
invece, rappresenta
esclusivamente la gri-
glia come disegnata nel
controllo.
I TUOI APPUNTI
Utilizza questo spazio per
le tue annotazioni
Protected Overloads Overrides Sub Paint(ByVal g As
System. Drawing. Graphics, ByVal bounds As
System. Drawing.Rectangle, ByVal source As
System. Windows. Forms.CurrencyManager, ByVal
rowNum As Integer)
Trova la path dato il numero di riga
Dim value = GetColumnValueAtRow(source, rowNum)
'disegna lo sfondo bianco della cella
g.FillRectangle(Brushes.White, bounds)
Dim thumb As Bitmap
'carica l'immagine
Dim bmp As New Bitmap(value.ToString)
Dim ResizeSize As Size = GetResizeSize(bmp)
'ridimensiona l'immagine alla grandezza della cella
SI E COMPONENTI NELL'AMBIENTE
VILUPPO
Creando in Visual Studio una
classe che eredita da DataGridCo-
lumnStyle noterete nella finestra
Esplora soluzioni l'icona che rap-
presenta il file è diversa da quella
utilizzata comunemente per rap-
presentare le classi, questo av-
viene perché Visual Studio
considera la classe come un
componente visto che DataGridCo-
lumnStyle a sua volta eredita da
System. ComponentModel
.Component. L'implicazione pratica
di tutto ciò è che tentando di
aprire il file con il doppio clic l'am-
biente di sviluppo genera un
errore perché tenta di aprire una
finestra di progettazione che per
DataGridColumnStyle non è
ammessa visto che il tipo è
dichiarato come Abstract. Per apri-
re il file occorre invece selezionar-
lo in Esplora soluzioni e premere
F7 o l'icona visualizza codice.
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 53 ►
GRAFICA T I Estendere una datagrid
OVERLOADS
E OVERRIDES
Il metodo Paint è stato
dichiarato con due paro-
le chiave Overloads e
Override. In pratica ciò
significa che va a sosti-
tuire lo stesso metodo
che è presente in Data-
GridColumnStyle, cioè
prende il suo posto e
modifica un comporta-
mento predefinito.
SUL WEB
Il controllo Datagrid
viene diffusamente
trattato nella library
Microsoft all'indirizzo:
http://msdn.microsoft.com
/library/en-us/cpref/html
/frlrfSystemWindowsForms
DataGridClassTopic.asp
Inseriremo poi, nella finestra di progettazione
anche un componente FolderBrowserDialog che
ci consentirà di disporre di un'interfaccia di
selezione della cartella
senza che l'utente
debba digitarla nella
textbox. La funzionali-
tà del programma sarà
quindi questa:
|g| FolderBrowserDialog 1
Fig. 2: II componente
FolderBrowserDialog
1. sull'evento Click sul Button si apre la fine-
stra di dialogo;
2. l'utente sceglie la directory;
3. si crea una Datatable contenente le Path
dei file grafici contenuti nella directory
prescelta;
4. si passa alla griglia la fonte dati appena
creata dicendole che deve essere gestita
dalla nostra classe CellExt anziché con i
componenti DataGridColumnStyle stan-
dard.
Rimandiamo, anche qui, al codice sorgente
contenuto nel CD di ioProgrammo per la
maggior parte di questi passaggi.
Analizziamo però alcuni passi salienti.
LA FONTE DI DATI
Questo è il codice che crea la Datatable a par-
tire dalla directory prescelta:
'il percorso impostato nella TextBox
Dim d As String = TextBox l.Text
'crea la nuova DataTable assegnandogli un nome
Dim tb As New DataTable("fs")
'stabilisce il numero di colonne
Dim colCount As Integer = 3
For n As Integer = To colCount - 1
'crea le nuove colonne e le aggiunge alla
DataTable
Dim e As New DataColumn("file" & n,
GetType(String))
c.ReadOnly = True
tb.Columns.Add(c)
Next
'crea un arrayList con le path dei file grafici
Dim files As New ArrayList
If Directory. Exists(d) Then
For Each f As String In Directory.GetFiles(d)
Dim ext = Path.GetExtension(f)
.Tol_ower.TrimStart(".")
Select Case ext
Case "bmp", "jpg", "gif", "png"
files.Add(f)
End Select
Next
End If
'crea un arrayList con le righe da inserire nella tabella
Dim rows As ArrayList = getRows(files, colCount)
For Each objs As Object() In rows
'inserisce gli oggetti contenuti nell'arrayList "rows"
'nella tabella
tb.Rows.Add(objs)
Next
Fin qui nulla di particolare, salvo notare che
sono state create 3 colonne per la tabella, ciò
per mostrare una griglia di thumbnail di tre
UNA GRIGLIA IN SEI PASSI
> LA FORM
WU In Visual Studio avviamo un proget-
mM to Visual Basic del tipo Windows Ap-
plication e su una Form disegniamo un
controllo textbox, un bottone e una da-
tagrid.
> LA CLASSE
Public Class CellExt
Inherits DataGridColumnStyle
Protected Overloads Overrides Sub Paint(
ByVal g As System. Drawing. Graphics,
ByVal bounds As System. Drawing
.Rectangle, ByVal so uree As System
.Windows. Forms.CurrencyManager, ByVal
rowNum As Integer)
I Provvediamo a creare una nuova clas-
Ise che eredita da DataGridColumn-
Style. Quindi provvediamo ad estendere
la nostra implementazione e sostituire il
metodo Paint.
> LA FONTE DATI
'crea un arrayList con le path dei file grafici
Dim files As New ArrayList
If Directory. Exists(d) Then
For Each f As String In Directory.GetFiles(d)
Dim ext = Path.GetExtension(f)
.ToLower.TrimStart(".")
Select Case ext
Case "bmp", "jpg", "gif", "png"
files.Add(f)
End Select
Next
End If
Otteniamo la fonte dei dati per la
Igriglia partendo dalla directory se-
lezionata dall'utente. Abbiamo reperito
questa informazione utilizzando un folder
BrowserDialog.
* 54 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Estendere una datagrid H ▼ GRAFICA
celle per riga. Un elenco del tipo:
• C:\documenti\filel.jpg
• C:\documenti\file2.jpg
• C:\documenti\file3.jpg
• C:\documenti\file4.jpg
Verrà mostrato come:
C:\documenti
\filel.jpg
C:\documenti
\file2.jpg
C:\documenti
\flle3.jpg
C:\documenti
\file4.jpg
I GESTORI DEI DATI
Dobbiamo poi dire alla griglia con cosa dovrà
gestire i dati che provengono dalla nostra Da-
tatable. Per questo sviluppiamo una funzione
che restituisce un gestore di tabella persona-
lizzato:
Private Function getTs(ByVal tb As DataTable) As
DataGridTableStyle
'dichiara un nuovo gestore di tabella
Dim ts As New DataGridTableStyle
'associa il gestore di tabella alla Datatable
ts.MappingName = tb.TableName
'imposta varie proprietà di stile della tabella
ts.ReadOnly = True
ts.ColumnHeadersVisible = False
ts.AllowSorting = False
ts.RowHeadersVisible = False
'personalizzato cellExt
Dim cs As New CellExt
cs.MappingName = c.ColumnName
cs.ReadOnly = True
cs.HeaderText = ""
ts.GridColumnStyles.Add(cs)
Next
Return ts
End Function
For Each e As DataColumn In tb.Columns
'associa ad ogni colonna il gestore
Possiamo notare come la nostra classe Cell-
Ext, derivata da DataGridColumnStyle, entra
in gioco mediante un'associazione operata a
livello di ogni colonna della DataTable di par-
tenza. Resta, a questo punto, soltanto da asso-
ciare a sua volta il gestore di tabella alla Data-
grid stessa, semplicemente con:
DataGridl.TableStyles.Add(getTs(tb))
Dove naturalmente la variabile tb che passia-
mo alla funzione sarà la Datatable che abbia-
mo visto in precedenza.
CONCLUSIONI
Abbiamo visto come partendo da un control-
lo base come la Datagrid sia possibile, attra-
verso il meccanismo dell'estensione, ottenere
funzionalità del tutto diverse dall' ordinario.
Naturalmente quello illustrato è solo un
esempio della tecnica dell'estensione; una
volta acquisiti i concetti di base l'unico limite
è la fantasia!
Francesco Smelzo
L'AUTORE
Francesco Smelzo è do-
cente di Informatica
presso l'ateneo di Siena
ed è specializzato nello
sviluppo in ambiente
.NET. Si occupa anche
del coordinamento del-
lo sviluppo Software
della Eurosoft Italia. È a
disposizione per rispon-
dere a quesiti e suggeri-
menti all'indirizzo
francesco@smelzo.it
> CREIAMO LO STILE
Dim ts As New DataGridTableStyle
'associa il gestore di tabella alla Datatable
ts.MappingName = tb.TableName
'imposta varie proprietà di stile della tabella
For Each e As DataColumn In tb.Columns
'associa ad ogni colonna il gestore
'personalizzato cellExt
Dim cs As New CellExt
ts.GridColumnStyles.Add(cs)
Next
Return ts
Impostiamo lo stile con cui verrà vi-
talizzata la fonte dati da parte di Da-
tagrid associando ad ogni colonna il gesto-
re personalizzato che abbiamo creato in pre-
cedenza.
> PASSIAMO STILI E DATI > IL RISULTATO FINALE
Private Function getTs(ByVal tb As
DataTable) As DataGridTableStyle
'dichiara un nuovo gestore di tabella
'associa il gestore di tabella alla Datatable
'imposta varie proprietà di stile della tabella
'associa ad ogni colonna il gestore
'personalizzato cellExt
End Function
DataGridl.TableStyles.Add(getTs(tb))
JDove getTs(tb) contiene i gestori di
colonna controllati dalla classe che ab-
biamo visto al passo 2, mentre tb è la Da-
taTable che abbiamo creato al passo 3. Im-
postiamo il DB al variare delle directory.
Directory
|F:\documenti^RTICOLI\DGRIDEXT\DGRIDEXT_SRC\imrnagini
keyboard.ji
Il risultato sarà quindi una griglia
Iche ospita i thumbnail delle imma-
gini contenute nella directory seleziona-
ta. Esattamente quello che fa al caso no-
stro!
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 55 ►
DATABASE T I Ottimizzare SQL Server
Paginazione su milioni
di record in SQL Server
Costruiremo la pagina di ricerca di un sito e-commerce che può
contare su di un'anagrafica di un milione di articoli. Vedremo come
ottenere prestazioni da competizione grazie a SQL Server e Asp.NET
IJ CD Li WEB
paginazione.zip
^
W7 "'""" J """*"""""
REQUISITI
■dmijmiJWWM
T~7 Principi di ASP.NET
9
Visual Studio 2003
113.
P
Tempo di realizzazione
, aginare significa presentare i record restituiti
da una query {resultset) in piccoli gruppi di
elementi ordinati secondo un certo criterio.
La paginazione è una tecnica utilizzata tipicamente
sul web (motori di ricerca, siti di e-commerce) dove
la trasmissione in blocco di migliaia di righe al client
non sempre è possibile, agevole o opportuna.
Offrire resultset paginati permette di rendere meno
difficoltosa agli utenti la consultazione dei risultati
ottenuti. Dal punto di vista applicativo il problema
della paginazione su SQL Server non ha ancora tro-
vato una soluzione definitiva, soprattutto in presen-
za di grosse moli di dati. Neppure in questa sede
sarà posta la parola fine al problema ma ugualmen-
te verranno offerti alcuni spunti preziosi su cui lavo-
rare.
I LAYER
DELL'APPLICAZIONE
Consideriamo l'architettura di una classica applica-
zione web a tre livelli: livello dei dati, logica di busi-
ness, livello di presentazione (architettura Windows
Distributed InterNet Applications o DNA). Il proble-
ma che ci si pone nell'ottica della paginazione è: su
che layer paginiamo? Il programmatore pigro deci-
derà di paginare nell'ambito della logica di business,
magari utilizzando le capacità intrinseche di una
datagrid, ottenendo risultati mediocri: tutto il re-
sultset andrà infatti ad occupare la memoria del web
server appesantendolo inutilmente, soprattutto nel
caso in cui ogni utente acceda a grossi resultset per-
sonalizzati. Una buona soluzione è la paginazione al
livello dei dati: in questo modo faremo lavorare
molto di più il database, ma visto che il trattamento
dei dati è il suo mestiere. . .
LO SCENARIO
L'idea è creare la pagina web di ricerca articoli di un
sito di e-commerce. Il nostro sito di e-commerce si
COME INIZIARE
Chi non potesse speri-
mentare le tecniche di
paginazione qui illu-
strate su SQL Server,
non si scoraggi. Ecco
come dotare la pro-
pria cassetta degli at-
trezzi di strumenti
gratuiti e facilmente
reperibili:
• MSDE 2000 - è SQL
Server 2000 con alcu-
ne limitazioni ma
completamente gra-
tuito. Fate attenzione
all'installazione dove
viene richiesta obbli-
gatoriamente una pas-
sword per l'utente
amministratore.
http://www.microsoft.co
m /sql/msde/downloads
/download.asp
• Dbamgr2k - ottimo
tool f ree per la gestio-
ne di MSDE 2000 crea-
to dal nostro Andrea
Montanari.
http://www.asql.biz/Dba
Mgr.shtm
• Query commander -
per chi volesse strafa-
re, ecco un tool simile
a Query Analizer con
cui potrete scrivere le
vostre query SQL con
syntax highligthing e
autocompletamento.
Naturalmente free
http://querycommander
.rockwolf.com/
baserà su un catalogo di 1.000.000 di articoli. Un bel
numero direi. Dalla ricerca, l'utente dovrà ottenere
pagine suddivise in gruppi di 20 articoli ordinati per
descrizione.
L'obiettivo finale è quello di ottenere tempi di rispo-
sta nell'ordine di qualche secondo anche su mac-
chine non particolarmente dotate.
La tabella su cui lavoreremo avrà una forma del ge-
nere:
Articoli
idArticolo
int
4
descrizioneArticolo
varchar
250
categoriaArticolo
varchar
250
prezzoArticolo
decimai
9
immagineArticolo
image
16
L'esempio è semplificato e la tabella andrebbe nor-
malizzata, ma il caso è adatto al nostro scopo. Il
campo su cui pagineremo, cioè il campo di ordina-
mento, sarà descrizioneArticolo.
* 56 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Ottimizzare SQL Server ■ ▼ DATABASE
I SISTEMI
DI PAGINAZIONE
SU DATABASE
Esistono molte soluzioni di paginazione su databa-
se. Noi focalizzeremo l'attenzione sulla paginazione
attraverso query SQL implementabili sia lato appli-
cazione che lato dabatase sotto forma di store pro-
cedure.
La prima soluzione proponibile è la seguente:
SELECT TOP 20 * FROM Articoli WHERE idArticolo • —
NOT IN
— # (SELECT TOP 60 idArticolo FROM Articoli ORDER BY
descrizioneArticolo ASC)
ORDER BY descrizioneArticolo
Lag^ffiflj seleziona i primi 60 record (3 pagi-
ne per 20 record) della tabella e ne torna le chiavi
(idArticolo). Lapffffifflj ^utilizza le chiavi prece-
denti per escludere dalla sua ricerca i primi 40 re-
cord e tornando i successivi 20. Quindi nell'esempio
il risultato della query è la pagina numero 4 (record
da 61 a 80).
Questo genere di soluzione comporta due tipi di
problemi. Il primo è facilmente aggirabile: la query
pagina solo su campi IDENTITY cioè dotati di chiavi
univoche. Il secondo limite è invece più grave per-
ché causa un decadimento notevole delle prestazio-
ni: la query più esterna opera un filtro con WHERE
NOT IN su un numero di idArticolo sempre più alto
man mano che le pagine aumentano. Già alla pagi-
na 4 del nostro esempio l'SQL si tradurrà in:
così estratti i record appartenenti alla pagina 4 cioè i
record dall'80 al 61 (DESC). La terza query, si occupa
di rimettere nel giusto ordine (ascendente) i record
ottenuti dalla query blu restituiti in tab2.
In buona sostanza il funzionamento di questa que-
ry si basa sugli ordinamenti prima ascendenti, poi
discendenti e poi ancora ascendenti che 'spostano'
al top del recordset i 20 record da restituire nella
pagina. Il lato negativo di questa soluzione è, anche
in questo caso, il decadimento precoce delle presta-
zioni (la crisi si manifesta già dopo alcune centinaia
di pagine). Vediamo come ottenere un buon com-
promesso tra le due soluzioni e come aggirare i limi-
ti evidenziati.
DIAMO UNA MANO
A SQL SERVER
Il primo collo di bottiglia che incontriamo risiede
nella query interna ed è senz'altro la clausola
ORDER BY
SELECT TOP 80 * FROM Articoli ORDER BY
descrizioneArticolo ASC
Paginazione vuol dire estrazione di gruppi di dati or-
dinati, da questo concetto non si può prescindere, e
quindi qualunque query di paginazione soffrirà del-
la sindrome di ORDER BY. Fortunatamente SQL Ser-
ver ci offre la possibilità di aggiungere un indice clu-
ster alla nostra tabella.
A
.net
GLOSSARIO
DBAMGR2K
E LE QUERY
Per lanciare una query
in dbamgr2k è necessa-
rio attivare lo strumen-
to Query disponibile
nel menu Activity.
Con le Query si
possono caricare file
.sql già pronti o editare
le istruzioni SQL diret-
tamente nell'editor.
SELECT TOP 20 * FROM Articoli WHERE idArticolo
NOT IN (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,
57,58,59,60)
Fate qualche prova e vedrete i tempi di risposta au-
mentare geometricamente. WHERE NOT IN non è
certo il comando più agile da eseguire per un data-
base di enormi dimensioni. La seguente soluzione è
molto più interessante e offre maggiore flessibilità
nel lavoro successivo di rifinitura.
MYSQL VS SQL SERVER
È importante sottolineare che in
mySQL esiste un'istruzione del tipo
SELECT * FROM TABELLA LI MIT
40,10 che restituisce 10 record a
partire dal 41, e che risolverebbe in
un colpo solo tutti i nostri
problemi. In SQL Server almeno
fino alla versione attuale questa
clausola non è disponibile e
dobbiamo accontentarci cella
clausola top.
Resta da vedere quanto l'istruzione
limit sia perf ormante in MySQL,
tuttavia è comunque innegabile
almeno la comodità dell'esistenza
di questo costrutto.
SELECT * FROM Articoli WHERE idArticolo IN
(SELECT TOP 20 * FROM
(
-# SELECT TOP 80 * FROM Articoli
ORDER BY descrizioneArticolo ASC
) AS tabi
ORDER BY tabi. descrizioneArticolo DESC
) AS tab2 ORDER BY tab2. descrizioneArticolo ASC
Lagffl ^ restituisce i primi 80 record della
tabella ordinati in modo ascendente per descrizione
articolo, cioè le prime 4 pagine. La seconda query
utilizza tabi per estrarre i primi 20 record dello stes-
so resultset ordinato in senso discendente: vengono
Per capire cosa sia un indice cluster immaginiamo la
disposizione fisica dei dati della tabella Articoli.
Hanno certamente un ordine "naturale" che sarà
l'ordine di inserimento dei dati. Creando un indice
cluster sulla colonna descrizioneArticolo diremo a
SQL Server che l'ordine dei record non dovrà più
essere quello di inserimento ma l'ordine alfabetico
(ascendente nel nostro caso) della colonna cluste-
rizzata che quindi potrà essere ordinata con ORDER
BY in modo molto veloce. L'indicizzazione avrà ef-
fetto anche per gli inserimenti di nuovi record. In
realtà il meccanismo adottato da SQL Server per la
creazione degli indici non è così semplice ma tanto
basta sapere per i nostri scopi. L'indice, grazie a
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 57 ►
DATABASE T I Ottimizzare SQL Server
dbamgr2k, può essere creato attraverso il menu con-
testuale della tabella Articoli oppure attraverso l'i-
struzione:
CREATE CLUSTERED INDEX idSuDescrizioneArticolo
ON dbo. Articoli (descrizioneArticolo)
I due procedimenti sono esattamente equivalenti.
Naturalmente, per sua stessa natura, ad una tabella
può essere associato un solo indice cluster. Ma nulla
ci vieta di creare altri indici (non cluster) per ognuno
dei campi che vorremo abilitare all'ordinamento e
quindi alla paginazione.
AUTENTICAZIONE MISTA E INTEGRATA
SQL Server permette l'accesso ai
suoi database attraverso la ge-
stione delle utenze interna
oppure delegando il controllo
delle stesse a Windows.
Il primo tipo di autenticazione è
sempre disponibile mentre il
secondo va attivato attraverso il
menu di connessione di
Dbamgr2k spuntando la voce
Trusted NT Connection. Il menu
di connessione si ottiene con un
doppio click sull'iconcina di SQL
Server
BIBLIOTECA
• PROGRAMMARE
VISUAL BASIC.NET
F. Balena
• C# GUIDA PER LO
SVILUPPATORE
S.Robinson
• MICROSOFT SQL
SERVER 2000 SYSTEM
ADMINISTRATION,
(Exam)
70-228
OTTIMIZZIAMO
LA QUERY
Torniamo ora a testare la nostra query principale
estraendo un numero di pagina più alto.
SELECT TOP 400000 * FROM Articoli ORDER BY
descrizioneArticolo ASC
Estraendo pagine molto alte, in questo caso la pagi-
na 20.000 (400.000/20), vi accorgete che le prestazio-
ni, grazie agli indici, migliorano di molto ma non
raggiungono ancora risultati soddisfacenti.
Consideriamo che nel tracciato record di Articoli ci
sono campi molto pesanti da restituire. Nella fatti-
specie i campi descrizioneArticolo e categoriaArtico-
lo sono dei varchar da 250 caratteri e il campo im-
magineArticolo è addirittura un campo image con-
tenente le immagini dei nostri prodotti (blob).
Nell'esempio che segue
SELECT idArticolo, descrizioneArticolo FROM
(SELECT TOP 20 idArticolo, descrizioneArticolo FROM
(
SELECT TOP 400000 idArticolo,
descrizioneArticolo FROM Articoli
ORDER BY descrizioneArticolo ASC
) AS tabi
ORDER BY tabi. descrizioneArticolo DESC
) AS tab2 ORDER BY tab2. descrizioneArticolo ASC
la nostra query si limita ad estrarre gli unici due
campi indispensabili alla paginazione: il campo in-
dice (idArticolo) e il campo di ordinamento (descri-
zioneArticolo) ottenendo finalmente delle prestazio-
ni accettabili. Purtroppo la nostra pagina di ricerca
ha bisogno di un resultset completo per visualizzare
tutti i dati relativi agli articoli della pagina. Come
risolvere il problema?
Consideriamo il seguente esempio:
SELECT * FROM Articoli WHERE idArticolo IN
(SELECT TOP 20 tabl.idArticolo FROM
(
SELECT TOP 400000 idArticolo,
descrizioneArticolo FROM Articoli
ORDER BY descrizioneArticolo ASC
) AS tabi
ORDER BY tabi. descrizioneArticolo DESC
)
La nostra query ora è completa. La query SQL più in-
terno si occupa di restituire gli idArticolo che appar-
tengono alla pagina. La query più esterna (la WHE-
RE IN) ci restituisce invece l'intero resultset che ver-
rà utilizzato dalla nostra pagina di ricerca. Perché la
query sia performante va indicizzata anche la chia-
ve di paginazione {idArticolo) oltre che il campo di
ordinamento {descrizioneArticolo). La query più
esterna sfrutta gli idArticolo restituiti dalle query più
interne per estrarre il resultset completo attraverso
la WHERE IN. Notiamo anche che descrizioneArtico-
lo, restituito dalla query interna (rossa) viene solo
utilizzato per l'ordinamento dalla query intermedia
(blu) ma non più restituito alla vera query di estra-
zione (nera). La pesantezza del comando WHERE IN
è molto mitigata dal fatto che questo agirà solo su 20
idArticolo a prescindere dal numero di pagina
estratto. Consiglio di creare un indice anche sul
campo di paginazione (idArticolo) oltre all'indice
cluster già creato.
CREATE UNIQUE INDEX idSuIdArticolo ON dbo.Articoli
(idArticolo)
Se in un caso reale non aveste una chiave univoca,
l'introduzione della WHERE IN produrrebbe result-
set non corretti. Per ovviare al problema potete crea-
re un id univoco da utilizzare solamente come chia-
ve di paginazione:
ALTER TABLE Articoli ADD id int IDENTITY
Attenzione ad un altro problema: Y ORDER BY del-
la query intermedia inverte l'ordinamento dei
campi descrizioneArticolo (clausola DESC) basan-
dosi solo sul loro ordine alfabetico e non più sul-
l'ordine naturale dei record (ricordate il funziona-
mento dell'indice cluster?). Siccome non abbiamo
la sicurezza che descrizioneArticolo sia univoco,
due o più articoli potrebbero avere la stessa de-
scrizione e quindi dobbiamo garantirci da ordina-
menti non corretti. Lo stesso discorso vale per la
* 58 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Ottimizzare SQL Server ■ ▼ DATABASE
query più esterna. Il risultato di queste ultime
considerazioni può essere quello che segue:
SELECT * FROM Articoli WHERE idArticolo IN
(SELECTTOP 20 tabl.idArticolo FROM
(
SELECT TOP 400000 idArticolo,
descrizioneArticolo FROM Articoli
ORDER BY descrizioneArticolo ASC
) AS tabi
ORDER BY tabi. descrizioneArticolo DESC,
tabl.idArticolo DESC)
ORDER BY descrizioneArticolo ASC,idArticolo ASC
Aggiungendo i due doppi ordinamenti (in prima
istanza per descrizioneArticolo e in seconda istan-
za per idArticolo) ci preserviamo da ordinamenti
scorretti dovuti alla non univocità del campo de-
scrizioneArticolo. Il campo idArticolo essendo
un'identità garantirà sempre l'esistenza di un
ordinamento.
LA PAGINA DI RICERCA
L'applicazione che mette in atto la nostra strategia
di ricerca si compone di due semplici pagine
aspx: WebFormRicerca.aspx e Getlmg.aspx. La
prima contiene la logica di paginazione, la secon-
da renderizza nella prima le immagini contenute
nel campo immagineArticolo se presenti.
Passiamo a commentare il codice C# della pagina
WebFormRicerca.aspx. La stringa di connessione
presuppone l'accesso a SQL Server in modalità di
autenticazione mista; va p ararne trizzata con il
nome del server in cui risiede il vostro SQL Server
(Data Source), il nome del database (Initial
Catalog), l'utente di connessione (User Id) e rela-
tiva password (Password).
SqlConnection sqlConnectionl = new
SqlConnection("Data Source=vmcomp;
Initial Catalog = 1000000;
User Id=sa;Password = ;
Connect Timeout=100");
Nel caso voleste accedere a SQL Server in moda-
lità di autenticazione integrata, la specificazione
dell'utente e della password lasciano il posto alla
voce Integrated Security-SSPI;. La query che
viene passata al SqlCommandl può contenere il
parametro di ricerca {WHERE descrizioneArticolo
LIKE@ descrizioneArticolo) per permettere effet-
tivamente di filtrare gli articoli che rispondono
ad una determinata descrizione.
La query così com'è implementata ha però un di-
fetto: pagina all'infinito. Gli articoli dell'ultima
pagina vengono ripresentati anche in quella suc-
cessiva e così via. Il motivo risiede nel fatto che
quando si richiede ad una SELECT di estrarre i
primi 1000 record (TOP 1000), se il resultset è
costituito di 800 record, non ci sarà modo di
sapere che mancano 200 record. Per ovviare al
problema va fatto un preventivo conteggio dei
record costituenti il recordset. Dal numero di
articoli così ottenuti va calcolato il limite massi-
mo di pagine ottenibili dalla query.
CREARE UM DB DA UN MILIONE DI RECORD
Nel ed allegato potrete trovare
una store procedure che lanciata
in SQL Server permetterà di
creare il database ShopOnLine
con la tabella Articoli.
Il contenuto dei campi è casuale
e la tabella è sprovvista delle im-
magini. Per diminuire il tempo di
attesa necessario alla creazione
della maxi tabella si può limitare
il numero di record intervenendo
sulla riga:
SET @NumeroRecord = 1000000
I blob delle immagini estratti dai campi immagi-
neArticolo vengono riposti in un hashtable espo-
sto dalla proprietà static hashTablelmmagini;
servirà alla pagina Getlmg.aspx per estrarre le
immagini senza dover interrogare nuovamente il
database. Per presentare i risultati si è utilizzato
un Repeater costituito di tutti i campi del data-
base.
CONCLUSIONI
A questo punto la domanda potrebbe essere: la
soluzione può essere implementata con successo
in un ambiente di produzione?
L'esperienza insegna che ogni caso reale merita
una attenta analisi, considerazioni specifiche e
soluzioni ad hoc.
Posso anticipare qualcuna delle vostre questioni
dicendo che la soluzione si comporta molto bene
anche con query in join e con filtri WHERE mul-
tipli ma va lavorata e rivista ad ogni modifica e
soprattutto vanno usati correttamente gli indici
sulle varie tabelle coinvolte.
Una considerazione che finora ho accuratamente
evitato ma che è forse la più importante riguarda
la potenza dell'hardware che è il vero fattore fon-
damentale della paginazione. Un db server do-
vrebbe sempre essere ben carrozzato e molto
veloce per garantire risposte in tempi accettabili
soprattutto in ambito web.
Non bisogna poi dimenticare che una soluzione
di paginazione su SQL Server può appoggiarsi a
store procedure, che garantiscono prestazioni
anche migliori ma una complessità nella costru-
zione più elevata.
Gianluca Negrelli
SUL WEB
http://www.codeproject.com
/aspnet/PagingLarge.asp
http://lorenzobraidi.blogsp
ot.com/2004/1 2/gestire-la-
paginazione-dei-dati.html
http://www.aspfaq.com
/show.asp?id=2120
http://www.devleap.com
/SchedaArticolo.aspx?
ldArticolo=10690
http://www.aspitalia.com
/articoli/dna.aspx
Gianluca Negrelli si
occupa di analisi
architetturale e
programmazione di
applicazioni enterprise
winform e web con
f ramework .NET sia in
C# che in VB. Si occupa
di sistemi di accesso e
gestione dati di
database in ambiente
Windows.
gianluca.negrelli@gmail.com
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 59 ►
VISUAL BASIC T
Gestione funzionalità Multimediali
Un player audio
in visual Basic
Vi guideremo all'uso del controllo "media control interface" il cuore
pulsante di VB per la gestione di suoni e filmati. Infine realizzeremo
un'applicazione completa per la riproduzione di CD ed MP3
Ci CD □ WEB
players.zip
jn
■d.l.l.WJ.klJJiJ.IU4M
Basi di Visual Basic
^3 L^3 L^3 d^3 L^
Tempo di realizzazione
In Visual Basic per amministrare le perife-
riche ed i file multimediali si possono uti-
lizzare diverse tecnologie, in questo arti-
colo mostreremo come farlo con gli strumen-
ti base quali il controllo MCI e la funzione API
MCISendString.
I device audiovisivi controllabili con l'MCI
(Media Control Interface) spaziano dal lettore
CD /DVD al videoregistratore. In questa pun-
tata ci occuperemo soltanto del lettore CD e
dell' esecuzione di file sonori quali Wav, Mp3,
Wma e Mid; nel successivo appuntamento, in-
vece, continueremo l'esplorazione delle ca-
ratteristiche multimediali di Windows occu-
pandoci tra l'altro dei file video e della regi-
strazione di file sonori e video.
Ricordiamo che i file con estensione Wav (for-
mato wavé) non sono altro che la pura regi-
strazione in digitale dei suoni reali, non sono
cioè compressi, essi sono supportati in Win-
dows già dalla versione 3.1. Dato che i file Wav
occupano molto spazio disco è quasi impossi-
bile utilizzarli nelle comunicazioni. Per que-
sto scopo, invece, sono adatti i file in formato
Mp3 e Wma (Windows Media Audio) ricavati
con degli algoritmi di compressione (CoDec)
che riducono le dimensioni dei file e contem-
poraneamente salvaguardano la qualità del-
l'audio.
API E CONTROLLO MCI
Il controllo MCI e la funzione MCISendString,
possono pilotare le periferiche audiovisive at-
traverso dei semplici comandi in formato
stringa, questi nel caso del controllo MCI sono
anche associati ai suoi pulsanti; infatti, il con-
trollo Multimedia MCI quando è trascinato su
un form si presenta come un pannello con
nove pulsanti, di default disabilitati.
Il controllo MCI è contenuto nella libreria Mi-
crosoft Multimedia Control 6.0, di default è
nominato MMControll.
' ' iti inseribili
□ LayoutDTC 1 .0 Type Library
□ LE D M eter i- rol module
□ Microsoft ActiveX Plugin
□ Microsoft ADO Data Control G.O (SPG) (OLEDB)
□ Microsoft Agent Control 2.0
Microsoft ntrol 3.0
□ Microsoft Chart Control G.O (SP4) (OLEDB)
□ Microsoft Cornm Control G.O
Microsoft Ci I G.O (SPG)
□ Microsoft Data Bound Grid Control 5.0 (SP3)
□ Microsoft Data Bound List Controls G.O (SPG)
□ Microsoft DataGrid Control G.O (SPG) (OLEDB)
Microsoft DataList Controls G.O (SP3) (OLEDB) v
< ^L_ >
•Solo dementi selezionati
:■) VideoSoft vsFle:-:3 Controls
Percorso: C: \WI N D OWS \System32WS FLEX3. CX
Fig. 1: Il controllo è contenuto nella libreria Microsoft
Multimedia control 6.0
Per selezionare il device (cioè la periferica) con il
quale si vuole interagire, il controllo MCI forni-
sce la proprietà DeviceType. I tipi di device sup-
portati dal controllo sono: CDAudio, WaveAu-
dio, MPEGVideo, Sequencer, Other, AVIVideo,
DAT, DigitalVideo, MMMovie, Overlay, Scanner,
VCR o Videodisc. S'intuisce che per controllare
un lettore CD bisogna utilizzare il device "CD-
Audio". Viceversa, per eseguire un file Mp3 o
Wma è necessario usare il Device MPEGVideo,
dato che YMp3 nasce dal MPEG.
Per inviare, un comando base, al device, a livel-
lo di programmazione, bisogna utilizzare la pro-
prietà Command, che ha la seguente sintassi:
MMControll. Command = "Comando"
Dove la stringa Comando può essere imposta-
ta con uno dei comandi base presentati nella
Tabellal.
+ 60 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Gestione funzionalità Multimediali
T SISTEMA
LE PROPRIETÀ BASE
Gli altri elementi notevoli dell' MMControl, so-
no le proprietà Wait, AutoEnable, TimeFor-
mat, Track, TrackLength, TrackPosition, From,
Updatelnterval e gli eventi StatusUpdate, But-
tonClick. La proprietà Wait determina se il
controllo deve attendere la fine di un coman-
do prima di eseguire il successivo. AutoEnable
stabilisce se il controllo deve attivare i propri
pulsanti automaticamente in base al tipo di
periferica. La proprietà TimeFormat specifica,
il formato di ora utilizzato per la posizione
(position) di esecuzione del brano. Per questa
proprietà sono disponibili vari formati, nel
nostro esempio abbiamo utilizzato il valore
mciFormatMilliseconds (millisecondi) .
Con la proprietà Updatelnterval, invece, si
stabilisce quanti millisecondi devono inter-
correre tra due eventi StatusUpdate; evento,
quest'ultimo, utilizzato per recuperare in
tempo reale i dati sull'esecuzione dei brani
musicali. Le proprietà Track, TrackLength e
TrackPosition, invece, forniscono informazio-
ni sul numero e lunghezza della traccia, in
esecuzione, e sulla posizione d'inizio della
traccia. La proprietà From si utilizza per spe-
cificare la posizione d'inizio dell'esecuzione
del prossimo comando Play o Record. Infine
gli eventi ButtonClick (dove button è il nome
di un pulsante, per esempio Play_Click) sono
generati, quando l'utente clicca un pulsante
del controllo; in realtà ai pulsanti sono asso-
ciati altri eventi e proprietà come ButtonCom-
pleted, ButtonGotFocus, ButtonEnabled ecc.
LE FUNZIONI API
Per la gestione dei file Mp3 è utile descrivere
brevemente le seguenti funzioni API: mci-
SendString, GetShortPathName e mciGetEr-
rorString. Più avanti sarà chiaro il ruolo della
GetShortPathName, che serve per recuperare
il path corto dei file e della mciGetErrorString
che restituisce la descrizione degli errori MCI.
Le dichiarazioni dettagliate delle funzioni si
trovano nel progetto inserito nel CD, allegato
alla rivista. La mciSendString, come accenna-
to, guida le periferiche attraverso l'invio di co-
mandi. La sintassi della funzione è la seguen-
te:
mciSendString (comando, Return Stri ng,
ReturnLength, hwnd)
Il primo parametro è la stringa di comando da
inviare alla periferica, il secondo è il buffer
che conterrà i dati restituiti dalla funzione, il
terzo è la lunghezza del buffer, l'ultimo para-
metro è l'handle della finestra che riceverà i
messaggi di notifica. Di solito gli ultimi due
parametri si utilizzano, quando si chiedono
informazioni sullo stato della periferica (posi-
zione, numero traccia ecc.). I comandi che è
possibile inviare con la mciSendString sono
molto complessi, per questo preferiamo sche-
matizzarli con il seguente modello:
mciSendString ("ComandoBase & Device or Alias &
altrivalori", , ,)
r
Pulsante
Descrizione
Open
Non previsto
Apre un device
Close
Non previsto
Chiude un device
Play
Play
Esegue un file sonoro
Pause
Pausa
Sospende la riproduzione o la registrazione
Stop
Stop
Interrompe la riproduzione o la registrazione
Seek 85 to
Dietro
Passa all'inizio della traccia corrente o della
precedente
Seek 85 to
Avanti
Passa all'inizio della traccia successiva
Record
Record
Registra
Eject
Espelli
Espelle il supporto della periferica
Save
Non previsto
Salva il file registrato
Set
Non previsto
Imposta diversi parametri, per esempio il
formato ora
Status
Non previsto
Restituisce informazioni sull'esecuzione
(traccia, lunghezza, posizione, 85)
I comandi base, principali, sono presentati
nella Tabella 1. Device può essere un nome di
file o un tipo di periferica, a tal proposito si
controlli la proprietà DeviceType del controllo
MCI. Alias è un nome alternativo per indivi-
duare il device. Altrivalori sono dei parametri
specifici dipendenti dal comando base.
Per esempio per aprire un file Mp3 possiamo
utilizzare il seguente comando:
Err = mciSendString ("OPEN " & filename & " TYPE "
& tipo & " ALIAS " & mioalias, 0, 0, 0)
Dove OPEN è il comando base, filename è il
nome del file Mp3 da aprire, tipo è il tipo di
Device e mioalias è l' alias che si dà al file. Err,
invece, è il valore restituito dalla funzione,
questo è uguale a zero se il comando è esegui-
to correttamente, altrimenti è uguale al nu-
mero di errore generato.
Per conoscere la descrizione dell'errore biso-
gna passare il valore di Err alla mciGetError-
String, come mostreremo negli esempi. Fac-
ciamo notare che il path del file (cioè filena-
me) deve essere quello corto, cioè del tipo "C:\
DOCUME~l\mioute\DOCUME~l\Musica\
esemp.MP3" altrimenti il comando non viene
eseguito correttamente, questo giustifica la
I TUOI APPUNTI
Utilizza questo spazio per
le tue annotazioni
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 61 ►
VISUAL BASIC T
Gestione funzionalità Multimediali
GLOSSARIO
FREQUENZA DI
CAMPIONAMENTO
Rappresenta il numero
di campioni di segnale
sonoro presi per
secondo, si misura in
KHz, per esempio una
frequenza di
campionamento
comune per file
musicali è 44.1 KHz.
necessità della GetShortPathName. Dopo aver
aperto il file per eseguirlo si può utilizzare
un'istruzione come la seguente:
mciSendString "PLAY " & mioalias & " from " & pos,
0, 0,
lì from, di solito, viene utilizzato quando si de-
ve eseguire un brano a partire da una data
posizione, (specificata con pos), quindi non è
sempre necessario. Per chiudere il device
aperto, invece, si può usare la seguente:
mciSendString "CLOSE " & mioalias, 0, 0,
DALLA TEORIA
ALLA PRATICA
Nei paragrafi precedenti abbiamo descritto in
linea teorica il controllo MCI, in questo para-
grafo presentiamo un lettore di CD audio che
utilizza quanto abbiamo detto fin qui. Il letto-
re oltre a permettere la selezione dei brani
musicali, visualizza alcune informazioni sullo
stato dell'esecuzione e consente di spostare
(in avanti o indietro) la posizione di lettura
corrente. Quest'ultimo verrà gestito con un
controllo Slider, o dispositivo di scorrimento,
uno strumento che permette di associare lo
scorrimento o il trascinamento di un cursore,
su una barra graduata, ai valori di una deter-
minata proprietà.
1 Create un nuovo progetto e referenziate le
librerie: Microsoft Multimedia Control 6.0
e Microsoft Common Controls, quest'ultima
contiene il controllo Slider. Sul formi dispo-
nete un controllo MCI, delle Label e un con-
trollo Slider come mostrato in Figura 2.
Le label servono per visualizzare il numero di
traccia (Lab eltr accia), la durata in minuti della
traccia (LabelTot) e il numero di minuti rima-
sti alla fine dell'esecuzione (Labelposizione).
2Inizializziamo le dichiarazioni globali e
definiamo la Form_Load.
Dim pos As Long
Dim DaPausa As Boolean
Private Sub Form_l_oad()
With MMControll
.Wait = False
.AutoEnable = True
.Updatelnterval =
.DeviceType = "CDAudio"
.Command = "Open"
.TimeFormat = mciFormatMilliseconds
End With
End Sub
La variabile pos contiene la posizione corren-
te, in millisecondi, durante l'esecuzione del
brano. DaPause, invece, è una variabile che
indica se è stato premuto il tasto Pause. Nella
Form_Load s'impostano le proprietà princi-
pali del controllo MCI e si apre la periferica
CDAudio con il comando Open. Se nel lettore
c'è un CD audio, eseguendo la Form_Load, si
abilitano i pulsanti: Play, Next, Prev ed Eject.
Se, invece, il lettore è vuoto si abilita solo il
pulsante Eject.
3 Lo Slider, come accennato, viene utilizza-
to per controllare la posizione del lettore
istante per istante. A tal fine nella procedura
ImpostaSlider inseriamo le istruzioni che im-
postano le proprietà Min, Max. La proprietà
Value, dello Slider, invece, viene incrementata
ogni secondo nell'evento MMControll _Status
Update, così il cursore del controllo si sposte-
rà man mano che il brano viene eseguito. Del-
lo Slider, inoltre, utilizzeremo l'evento Sli-
derl_Scroll, generato, quando si trascina, con
il Mouse o la con la tastiera, il suo cursore.
Private Sub ImpostaSlider()
Sliderl.Value =
pos = MMControll.TrackLength
LabelTot = CStr((Round(pos / 60000, 2)))
Sliderl.Min =
Sliderl.Max = pos / 1000
End Sub
Fig. 2: II lettore CD in fase di progettazione
Private Sub Sliderl_Scroll()
Dim Ispos As Long
Dim traccia As Integer
With MMControll
If .Updatelnterval = Then
.Updatelnterval = 1000
impostaslider
► 62 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Gestione funzionalità Multimediali ■ T VISUAL BASIC
End If
Ispos = Sliderl.Value
traccia = .Track
.Command = "dose"
.Command = "open"
.Ti me Format = mciFormatMilliseconds
.Track = traccia
.From = .TrackPosition + Ispos * 1000
Labelposizione
& CStr((Round((lspos * 1000) / 60000, 2)))
pos = (Me.Sliderl.Max - Ispos) * 1000
.Command = "play"
End With
End Sub
Nella ImpostaSlider le proprietà Max è impo-
stata in base alla lunghezza della traccia che si
sta eseguendo, essa è pari alla lunghezza in
millisecondi della traccia diviso 1000 (1 sec
= 1000 m sec). Viene anche impostata LabelTot
con la lunghezza in minuti della traccia (nota-
re che 1 min = 60000 m sec). Nella Sliderl_
Scroll, invece, utilizzando la proprietà From,
s'imposta la posizione da dove eseguire il
comando Play. La posizione da impostare con
il From è data dalla posizione iniziale della
traccia (cioè TrackPosition) più la posizione
impostata sullo Slider La TrackPosition è ne-
cessaria dato che la posizione fornita da Ipos è
quella relativa rispetto all'inizio della traccia.
La posizione del lettore, ricavata in base allo
Slider, è anche utilizzata per impostare la
Labelposizione e la variabile globale pos.
Nell'evento MMControll _StatusUp date come
accennato inseriamo il codice da eseguire man
mano che il brano musicale viene riprodotto.
Private Sub MMControll_StatusUpdate()
Labeltraccia = " " + CStr(MMControll. Track)
Sliderl.Value = Sliderl.Value + 1
pos = pos - 1000
If CStr((Round(pos / 60000, 2))) < Then
MMControll. Command = "prev"
impostaslider
End If
Labelposizione = " " _
& CStr((Round(pos / 60000, 2)))
End Sub
In particolare in MMControll _StatusUp date
incrementiamo il valore (value) dello Slider,
decrementiamo la variabile pos (inizialmente
impostata sulla lunghezza della traccia) ed
impostiamo, con i valori correnti, la Label-
Traccia e la LabelPosizione. Inoltre, in base al
valore della variabile pos controlliamo quan-
do la traccia in esecuzione termina (cioè
quando arriviamo ad avere valori negativi per
pos). Questo controllo è necessario dato che,
anche se il lettore passa all'esecuzione del
brano successivo il numero di traccia non vie-
ne aggiornato automaticamente. Quando si
raggiunge la fine della traccia vengono reim-
postati i valori dello Slider e delle Label, que-
sto si fa con l'esecuzione del comando "Prev".
4 Rimangono da im-
plementare i co-
mandi "play", "prossi-
ma traccia", "traccia
precedente". Faccia-
mo notare che se il
comando "prev" è
eseguito una sola vol-
ta il controllo non
passa alla traccia pre-
cedente ma all'inizio
di quella corrente.
Per passare alla trac-
cia precedente il co-
mando deve essere
eseguito due volte entro tre secondi. Un altro
evento utilizzato è PlayClick.
Private Sub MMControll_PlayClick(Cancel As Integer)
MMControll. Updatelnterval = 1000
If Not DaPausa Then
MMControll. Command = "prev"
UMERO DI CANALI E DI BIT
" CAMPI
I canali possono
essere Stereo o
mono, il numero di
bit può essere 8, 16
ecc. Ad esempio per
una conversazione
telefonica sono
sufficienti campioni
a 8 bit, campionati a
8 kHz mentre per un
brano musicale è
necessario un
campionamento
stereo a 16 bit con
una frequenza di
44,1 kHz. Per quanto
riguarda il BitRate,
maggiore è il suo
valore, migliore è la
qualità del suono,
però, di conseguen-
za maggiore è lo
spazio di memoria
occupato.
impostaslider
End If
DaPausa = False
End Sub
Nell'evento PlayClick oltre ad impostare V Up-
datelnterval si stabilisce, in base al valore di
DaPausa, da quale posizione iniziare l'esecu-
zione. Gli eventi legati ai pulsanti Pause e
Stop, invece, bloccano l'esecuzione del brano
musicale (e quindi StatusUpDate) .
Private Sub MMControll_PauseCompleted(Errorcode
As Long)
MMControll. Updatelnterval =
DaPausa = True
End Sub
Private Sub MMControll_StopCompleted(Errorcode As
Long)
MMControll. Updatelnterval =
End Sub
Negli eventi Prev e Next, invece, basta soltan-
to reimpostare lo Slider.
Private Sub MMControll_PrevCompleted(Errorcode As
Long)
impostaslider
GLOSSARIO
BITRATE
È il numero di bit per
ogni secondo di suono,
si misura in Kbps (kilo
bit per secondo), di
solito un segnale audio
qualità CD ha un
BitRate di 1411,2 Kbps;
un file Mp3, invece, ha
un BitRate che spazia
da 112 Kbps a 320
Kbps.
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 63 ►
VISUAL BASIC T ■ Posta elettronica con Visual basic
End Sub
MCISEND
COMMAND
Un'altra funzione API
che permette di con-
trollare le periferiche
audiovisive è la
mciSendCommand che,
a differenza della
mciSendString, invia
comandi basati su
particolari strutture
dati; per questo la ge-
stione con la mciSend-
Command è più mac-
chinosa e non
attinente con quella
del controllo MCI. Nel
progetto Visual Basic
fornito con la rivista
trovate un semplice
esempio basato su
questa funzione, si
tratta di un form con
due pulsanti che
inviano i comandi per
aprire e chiudere il
supporto del lettore
CD.
Private Sub MMControll_NextCompleted(Errorcode
As Long)
impostaslider
End Sub
Infine nella FormJJnload, per sicurezza, con-
viene stoppare e chiudere la periferica aperta.
Private Sub Form_Unload(Cancel As Integer)
MMControll.Command = "stop"
MMControll.Command = "dose"
End Sub
Si Lettore CD
N
M
V-
II
T [M ■
±
- i—
;
1 Rimasti
1 Durata 1
[Tracciai
3,22
13,3
Fig. 3: Ecco come si presenta il nostro lettore CD in
esecuzione
MP3, WMA E ALTRO
HMPEG {Moving Picture Export Group) è rite-
nuto uno dei primi metodi intelligenti per la
compressione e decompressione dei file au-
dio (CoDec audio), da questo sistema è nato
l'Mp3.
Attualmente ci sono valide alternative al for-
mato Mp3, tra queste citiamo il formato
Windows Media Audio (Wma) che a parità di
qualità audio riesce a ridurre ulteriormente le
dimensioni dei file; OGG Vorbis un ottimo co-
dec open source; il codec della Global Music
Outlet ed infine il formato MPEG-4 AAC che è
supportato dalla Apple con i famosi iPod e
iTunes Music Store.
CREAZIONE FILE MP3
Un modo semplice ed efficace per creare file
compressi, da un CdAudio, è quello di utiliz-
zare le funzionalità di conversione di Win-
dows Media Player (WMP) o di iTuner, que-
st'ultimo è il Player lanciato dall'Apple.
Con WMP la creazione dei file Mp3 o Wma
s'imposta dalla scheda copia file da CD della
finestra opzioni (selezionabile dalla voce di
Play I ! Stop I ! Pausa ! Close
■I
menu Strumenti/Opzioni). Con ITuner, invece,
dopo aver definito le preferenze di conversio-
ne, si può convertire un file (non necessaria-
mente contenuto in un CD) utilizzando un
semplice menu PopUp attivabile con il tasto
destro del Mouse.
IL LETTORE MP3
In questo paragrafo presentiamo un lettore di
file musicali realizzato con la sola funzione
mciSendString. Il lettore permette di ricercare
un file musicale, di eseguirlo, di stopparlo o di
metterlo in pausa.
INel progetto bisogna referenziate la libre-
ria Microsoft Common Dialog Control che
contiene il controllo CommonDialog da utiliz-
zare per ricercare i file.
Sul form bisogna inserire un CommonDialog,
un textbox, un con-
trollo Timer e cin-
que pulsanti.
Questi ultimi van-
no nominati: Play,
Stop, Pause, Close e
Cerca. Il Timer è
utilizzato per far
scorrere, durante
l'esecuzione, il no-
me del file contenuto nel TextBox (il codice da
prevedere nel Timer lo trovate nel CD allegato
alla rivista) .
2 Nella parte dichiarativa del form è previ-
sta una costante, che contiene il nome
dell' alias da attribuire al device. Nella form
load, invece, è previsto il codice che blocca il
Textbox.
Const mioalias = "nomealias"
Private Sub Form_l_oad()
txtfile.Locked = True
End Sub
3 La ricerca di un file è fatta con la cerca_
Click. La riproduzione del brano musicale,
invece, si avvia con la Play_Click. In quest'ul-
tima procedura, in particolare, si valuta il per-
corso corto del file, si individua il tipo di peri-
ferica da aprire (in base all'estensione del fi-
le), si apre la periferica con OPEN, s'imposta
il formato dell'ora in millisecondi, si esegue il
brano con PLAY e si gestiscono gli errori MCI.
Gli errori sono catturati con la GestErr che ri-
chiama la mciGetErrorString.
Private Sub cerca_Click()
Fig. 4: Ecco come si presen-
ta la form per la realizzazio-
ne del lettore MP2
* 64 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Posta elettronica con Visual basic ■ T VISUAL BASIC
With CommonDialogl
.DialogTitle = "Cerca un file sonoro"
.Filter = "File multimediali |*.mpg;*.mpeg;" _
& "*.avi;*.mpa;*.mpv;*.mlv;*.mp2;*.mp3" &_
";*.mpe;*.mpm;*.au;*.aif;*.wav;*.wma;*.midi;
.mid"
i file avi generano un errore mei
.ShowOpen
If .filename <> "" Then
txtfile.Text = .FileTitle
Stop_Click
End If
End With
End Sub
Private Sub Play_Click()
Dim tipo As String
Dim filename As String
Dim IResult As Long
Dim OpenMP3 As String
If Me.txtfile = "" Then
MsgBox "Specificare un file", vbCritical, "Attenzione"
Exit Sub
End If
filename = pathnomecorto(
CommonDialogl. filename)
Select Case UCase(Right(filename, 3))
Case "MP3", "WMA":
tipo = "MPEGVideo"
Case "WAV":
tipo = "Waveaudio"
Case "MID":
tipo = "Sequencer"
Case Else
MsgBox "Il file selezionato non è MP3, Wav,
Wma o Mid"
tipo = "Other"
End Select
IResult = mciSendStringC'OPEN " & filename
& " TYPE " _& tipo & " ALIAS " & mioalias, 0, 0, 0)
If IResult <> And IResult <> 289 Then
' NBO 289 generato se prima era in Pausa
OpenMP3 = GestErr(IResult)
MsgBox OpenMP3
End If
mciSendString "SET " & mioalias & " TIME FORMAT
MS", 0, 0,
MS = millisecondi
mciSendString "PLAY " & mioalias, 0, 0,
Timerl.Interval = 500
End Sub
Private Function GestErr(IError As Long) As String
Dim sBuffer As String
sBuffer = String$(255, Chr(0))
mciGetErrorString lError, sBuffer, Len(sBuffer)
GestErr = Replace$(sBuffer, Chr(0), "")
End Function
Il codice da prevedere per i pulsanti Pausa,
Stop e Close è il seguente.
Private Sub Pausa_Click()
mciSendString "PAUSE " & mioalias,
0,
0,
Timerl.Interval =
End Sub
Private Sub Stop_Click()
Timerl.Interval =
mciSendString "CLOSE " & mioalias,
0,
0,
End Sub
Private Sub close_Click()
Timerl.Interval =
mciSendString "CLOSE " & mioalias,
0,
0,
txtfile = ""
End Sub
Fig. 4: II lettore mp3 in ese-
cuzione
Notare che la Stop
Click () e Close
ClickQ inviano lo
stesso comando e
che le due opera-
zioni si differen-
ziano grazie a txt-
file = "". Questa
scelta è motivata dal fatto che per la
mciSendString il comando Stop ha lo stesso
effetto del comando Pausa! Inoltre notate il
Timerl.Interval -0 serve per bloccare il movi-
mento della stringa. Infine per evitare proble-
mi con la chiusura del form conviene preve-
dere il seguente codice.
Private Sub Form_Unload(Cancel As Integer)
Stop_Click
End Sub
CONCLUSIONI
Il lettore CD potrebbe essere potenziato con
l'introduzione di un file di testo (o di un data-
base) che associa delle stringhe al numero di
traccia. Il Player Mp3, invece, nel successivo
appuntamento, sarà potenziato con la gestio-
ne della posizione di lettura e dei dati di ese-
cuzione, con i tasti per controllare il volume
degli altoparlanti e con l'aggiunta delle fun-
zionalità che consentono di registrare dei file
sonori. Nel successivo appuntamento, inoltre,
mostreremo come riprodurre i file video, i
DVD, come controllare una WebCam e come
masterizzare.
Massimo Autiero
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 65 ►
BACKSTAGE T ■ Intelligenza artificiale
Reti Neurali
PC che pensano
La replicazione dell'intelligenza umana da parte di una macchina è
da sempre il sogno di scrittori di fantascienza e scienziati. In questo
articolo "addestreremo" un computer a pensare...
REQUISITI
ip^ Principi di Java
, J2SE 1.5, SNARLI
E^3 HH3 GH3 1^3 \^J
Tempo di realizzazione
(V> tfV> f Yì f yi m
(IO
o
ognerò?" è la domanda che HAL9000, il
primo computer intelligente della storia
cinematografica, pone al dottor diandra
nella consapevolezza di stare per essere spento. Ci-
tazione scontata, ma dovuta per un film che ha
aperto il sogno dell'intelligenza artificiale. Macchine
che pensano, in grado, nel bene e nel male, di pren-
dere in autonomia le proprie decisioni. Macchine
che elaborano dati non secondo una sequenza clas-
sica di if/then/else come siamo abituati a vedere in
programmazione, ma che viceversa replicano il
complesso meccanismo di neuroni, assoni, stimola-
zioni sinaptiche, che producono infine l'output: il
pensiero umano.
In questo articolo tenteremo di replicare una rete
neurale del tutto simile a quella presente nel cervel-
lo umano. Il nostro scopo finale sarà creare una rete
tale che riesca a decidere da sola la risposta a una sti-
molazione di tipo booleano. Forniremo cioè alla no-
stra macchina in input due numeri, e "lei" sarà in
grado di decidere sulla base di una relazione di "OR"
logico qual è il valore booleano dell'operazione OR.
Sembra una banalità, eppure la nostra sarà una pic-
cola macchina pensante, una porta aperta verso l'af-
fascinante oceano dell'intelligenza artificiale.
PARALLELISMO
E PLASTICITÀ
Quando sono nate, le reti neurali si prefiggevano co-
me obiettivo quello di "emulare" il comportamento
del cervello umano. Ora sappiamo che tale obiettivo
è ben lontano dall'essere raggiunto e probabilmente
mai sarà raggiunto. Ad ogni modo il modello biolo-
gico rimane la fonte primaria d'ispirazione per la
replicazione dell'intelligenza umana, per cui vale la
pena comprenderne i principi base.
Tutti sappiamo che il nostro cervello è costituito da
cellule chiamate neuroni. Queste unità computazio-
nali di base formano tra loro delle interconnessioni
che danno vita al più spinto ed efficiente "calcolato-
re parallelo" mai realizzato. Inoltre la grande quan-
tità di cellule e di connessioni fanno si che il mal-
funzionamento di una di essa sia ininfluente ai fini
del funzionamento del sistema complessivo.
Secondo un modello estremamente semplificato
ogni neurone è costituito da:
1 Un corpo cellulare dove avvengono le reazioni
bio- chimiche della cellula.
2 Dei dendriti, che sono dei corti filamenti, dai
quali la cellula riceve gli stimoli elettrici da tutte
le altre cellule a cui è collegata.
3 Un assone, per mezzo del quale la cellula pro-
paga il suo segnale elettrico generato nel corpo.
4 Le sinapsi, che sono delle ramificazioni poste
sulla parte terminale dell' assone, grazie alle
quali può connettersi ai dendriti delle altre cel-
lule.
Fig. 1: Uno schema sintetico della rappresentazione
dei collegamenti neurali
Cerchiamo ora di capire come funziona questo mo-
dello del neurone: molto semplicemente potremmo
dire che ogni neurone riceve degli stimoli elettrici
dalle altre cellule mediante i dendriti a cui, a loro
volta, sono collegate le sinapsi di queste ultime. In
* 68 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Intelligenza artificiale ■ ▼ BACKSTAGE
base agli stimoli ricevuti, il potenziale elettrico del
corpo della cellula assume un certo valore; se tale
valore supera una certa soglia, a sua volta la cellula
propagherà un impulso elettrico lungo il proprio
assone. In questo sistema un ruolo di fondamentale
importanza lo rivestono le sinapsi: a seconda di
quanto spesso due cellule scambiano segnali tra
loro la relativa sinapsi può presentare un carattere
eccitatorio od inibitorio. Infatti una sinapsi che
viene "utilizzata" frequentemente tende a condurre
il segnale molto meglio di quanto non lo faccia una
utilizzata di rado. Da ciò si può comprendere come,
benché l'uscita elettrica di un neurone (cioè l' asso-
ne) sia unica, grazie alle sinapsi ogni altra cellula
riceverà un segnale d'intensità differente.
Come potete già cominciare a capire un'architettura
che preveda migliaia di miliardi di unità di calcolo di
questo genere presenta due caratteristiche fonda-
mentali:
1 Dato un numero qualsiasi di neuroni e fissato il
modo in cui essi sono interconnessi (ossia fissa-
ta la topologia della rete) il comportamento
complessivo del sistema cambia radicalmente in
funzione della conduttività delle sinapsi. Questo
sarà anche più chiaro tra un po' quando sfrutte-
remo questa proprietà per addestrare la rete.
2 Ogni neurone lavora contemporaneamente a
tutti gli altri ed allo stesso tempo dipende da tutti
gli altri. In questo modo l'elevato parallelismo
non comporta neanche problemi di comunica-
zione tra le unità di elaborazione.
DALLA BIOLOGIA
AL SOFTWARE
Ora che abbiamo visto quali sono i principi base di
una cellula neurale biologica tentiamo di imitare
madre natura!
Fig. 2: Uno schema di come il nostro neurone sarà
definito al livello informatico
Per quanto detto fino ad ora, abbiamo bisogno di tre
elementi per "costruire" un neurone:
• Un insieme di pesi sinaptici (da ora in poi li chia-
meremo semplicemente pesi per brevità) che
modellizzino la conducibilità delle sinapsi. Na-
turalmente ad ogni peso sinaptico è associato
anche un segnale d'ingresso proveniente da un
altro neurone.
Un sommatore che si occupa di moltiplicare
ogni segnale al suo relativo peso e sommi tutti i
prodotti così ottenuti in modo tale da determi-
nare il potenziale del neurone.
Una funzione d'attivazione che riceva in ingres-
so il valore del potenziale e restituisca l'uscita del
neurone stesso.
LA LIBRERIA SMARLI
SNARLI è una libreria Java svilup-
pata dal prof Simon D. Levy della
Washington and Lee University ed
ospitato su sourceforge
all'indirizzo http://snarli.sourcefor-
ge.net/ .
Tale libreria si basa, a sua volta, su
un'altra libreria di algebra lineare
(che magari avremo occasione di
scoprire in uno dei prossimi arti-
coli), che si chiama JLinAIg
anch'essa ospitata su sourceforge
all'indirizzo http://jlinalg.sourcefor-
ge.net/ .
Per questo motivo avrete bisogno
di entrambe le librerie per far
funzionare correttamente le
vostre reti. Il mio consiglio è
quello di, se non siete interessati
all'implementazione di tali
librerie, di scaricare o prelevare
da CD semplicemente i jar.
Questi tre aspetti sono ben rappresentati in Figura
2. Un po' d'attenzione va prestata al terzo punto da-
to che esso sarà di fondamentale importanza anche
per il proseguo. La domanda che potrebbe sorgere è:
che bisogno c'era di un'ulteriore funzione d'uscita?
Senza tante considerazioni matematiche facciamo
una considerazione puramente qualitativa. Suppo-
niamo che un neurone cominci ad essere eccitato
più di altri; quello che succederebbe è che i suoi pesi
comincerebbero ad aumentare provocando a loro
volta un aumento dell'uscita del neurone. Allo stes-
so modo, prima o poi, i neuroni eccitati ulterior-
mente da quest'ultimo andrebbero ad eccitare il
neurone stesso instaurando così un ciclo infinito
che porterebbe il sistema ad "esplodere" (o più pro-
priamente a divergere): per evitare ciò si utilizza la
funzione d'attivazione. La proprietà principale di
questa funzione e quella di avere un'uscita limitata
pur accettando qualsiasi range di valore in ingresso ,
per questo motivo tale tipo di funzioni è anche detto
squashing.
DA PROGRAMMATORI
AD INSEGNANTI!
Fino ad ora ci siamo concentrati sulle caratteristiche
di un singolo neurone "isolato", che come avrete
ben capito preso da solo non è poi così intelligente!
Ora quello che dobbiamo fare è cominciare a cam-
biare completamente il nostro approccio alla prò-
COSA CE
DENTRO
AL CERVELLO
Si pensi che nella sola
corteccia di un cervello
umano adulto si hanno
all'inarca 10 miliardi di
neuroni e che formano
tra loro circa 60 mila
miliardi di connessioni
con le loro sinapsi.
Questo elevatissimo
parallelismo è
sicuramente uno dei
fattori che rende il
nostro cervello
l'organo più complesso
mai studiato. Infatti,
benché gli attuali
transistor siano anche
6 - 9 ordini di
grandezza più veloci
dei neuroni, a tuttoggi
non si è riusciti a
realizzare nulla che si
avvicini alle capacità di
un cervello anche di un
animale.
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 69 ►
BACKSTAGE T I Intelligenza artificiale
COME
ADDESTRARE
LA RETE
In realtà fornire alla re-
te gli esempi è una del-
le parti più critiche per
quanto riguarda le reti
neurali. Ci sono due
aspetti che dovete sem-
pre tenere in considera-
zione: gli esempi devo
essere il più generali
possibile in modo tale
che la rete possa poi
comportarsi corretta-
mente anche di fronte
a casi mai visti finora.
Il secondo aspetto da
tenere in considerazio-
ne è che una rete tende
a "specializzarsi", cioè
impara molto meglio
gli ultimi esempi rispet-
to ai primi; quindi è im-
portante prestare at-
tenzione all'ordine in
cui essi vengono pre-
sentati.
LA FUNZIONE D'USCITA
PER LO SQUASHING
grammazione. Indipendentemente da quale sia il
vostro linguaggio preferito ed indipendente da
quanto siano complessi i vostri programmi, su cosa
si basa la cosiddetta programmazione dichiarativa o
imperativa? Tutto si basa su quello che viene chia-
mato in linguaggio tecnico -informatico un algorit-
mo: che poi non è altro che una serie finita d'istru-
zioni per risolvere un problema dato. Quindi chiun-
que di voi abbia scritto un programma, per quanto
piccolo esso fosse, ha scritto un algoritmo, pur non
sapendolo! Il massimo della dinamicità pensabile
per un algoritmo è una serie di if/else che permette di
scegliere quale istruzione eseguire a seconda del
verificarsi o meno di alcune condizioni. In altre pa-
role siamo noi programmatori a prevedere il com-
portamento deterministico del programma stesso.
Da adesso faremo qualcosa di diverso: ci occupere-
mo solamente di costruire una struttura (la rete neu-
rale) in grado di "imparare" da esempi che fornire-
mo; non vi preoccupate tutto sarà più chiaro tra un
po'. In effetti se pensiamo alla nostra esperienza co-
mune possiamo dire che abbiamo due fondamenta-
li modalità di apprendimento: una è quella che vie-
ne tipicamente applicata a scuola, ossia c'è un inse-
gnante che ci trasmette delle informazioni e starà a
noi assimilarle e farne buon uso. La seconda invece
riguarda di più la vita quotidiana: se pensiamo ad
esempio ad un'azione banalissima come quella del
camminare ci accorgiamo che a nessuno di noi è
stato detto di mettere un piede dopo l'altro ma con
il tempo e dopo numerosissimi tentativi ed insuc-
cessi abbiamo trovato il modo migliore per cammi-
nare. Anche in questo caso ingegneri e matematici
hanno tratto ispirazione da queste semplici consta-
tazioni per elaborare varie tecniche d'addestramen-
to per le reti neurali. Come già accennato sopra pos-
siamo dividere tutte le tecniche d'addestramento in
due grandi categorie: su-
Solitamente la funzio-
ne più utilizzata è la
sigmoide. La sua
espressione analitica è
f(x) = 1/(1+exp(-a*x)).
Come si vede questa
funzione ha un range
compreso tra Oe 1.
Un'altra proprietà
utile è quella che
per-visionate e non su-
per-visionate che grosso
modo corrispondono ri-
spettivamente ai due
esempi sopra riportati.
In questo articolo ci oc-
cuperemo solo di reti
super-visionate che forse
sono quelle più facili sia
da implementare che da
capire.
DAI PROPRI ERRORI
L'ultima cosa che ci rimane da fare prima di mettere
mano ad un po' di codice è quella di riuscire a capi-
re in che modo la nostra brava rete neurale possa
venire addestrata. Esiste un'infinità di topologie di
tramite il parametro a
possiamo modulare la
"ri pi dita" della curva.
Ci sono anche altre
caratteristiche che
rendono questa
funzione molto utile,
ma casomai ci
torneremo in un
prossimo articolo.
y
t
w1 w5
w2 W 3 w4
1 input layer
1 output layer
2 layer
t t t t t
Fig. 3: Una rappresentazione della rete di tipo "per-
ceptrone"
reti, quella che qui utilizzeremo si chiama perceptro-
ne (o in inglese perceptron) ed è una delle prime
comparse in letteratura. Pur avendo grossi limiti, ha
dato il via ad altre reti più sofisticate, una delle quale
avremo il piacere di presentare in un futuro articolo.
Solitamente i vari i neuroni sono raggruppati in stra-
ti, o meglio layers. In un perceptrone ci sono esclusi-
vamente due layers: uno di input l'atro di output.
Quello che dobbiamo fare prima di tutto è decidere
quanto sia grande la rete, in questo caso di quanti
ingressi e quante uscite abbiamo bisogno. Una volta
stabilito ciò non dobbiamo far altro che fornire alla
rete "alcuni" esempi; cioè è necessario fornire alla
rete un serie d'ingressi e d'uscite desiderate. In que-
sto modo possiamo definire una quantità che misu-
ri l'errore per ciascuno dei neuroni d'uscita:
e[n] = d[n]-y[n]
dove n rappresenta il numero dell'esempio, y l'usci-
ta effettiva della rete e d l'uscita desiderata. Ora pos-
siamo sfruttare questa quantità per modificare il
comportamento della rete in modo tale che "impari
dai propri errori". In particolare, come abbiamo vi-
sto all'inizio, basterà modificare i pesi sinaptici in
funzione dell'errore commesso in modo tale da far
avvicinare il più possibile l'uscita effettiva a quella
desiderata.
Più dettagliatamente definiamo un incremento (o
decremento) di ogni peso come:
kw i [n] = 'ne[n]x i [n]
dove x è un ingresso e l'indice i indica il numero del-
l'ingresso e del relativo peso, mentre r] è detto lear-
ning rate. Questo parametro indica la percentuale di
quanto i pesi debbano cambiare rispetto all'ingresso
e all'errore, ma per ora tralasciamo questo aspetto;
quello che ci interessa sapere è che all'esempio suc-
cessivo i pesi verranno aggiornati in questo modo:
w 1 [n + l] = w i [n] + Aw t [n]
* 70 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Intelligenza artificiale ■ ▼ BACKSTAGE
L'ultimo problema è stabilire quando una rete ha
terminato la fase d'apprendimento e può essere
quindi pronta per essere utilizzata. Si dice che ogni
volta che una rete abbia scorso tutti gli esempi e
quindi aggiornato di volta in volta i pesi sia passata
un epoca. Naturalmente occorre iterare più epoche
prima di poter considerare la rete pronta. Determi-
nare il numero di epoche non è un scienza esatta e
dipende da molti fattori, solitamente si procede au-
mentandolo progressivamente fino a raggiungere le
performance desiderate. Tenete conto anche che
non sempre un numero elevato di epoche dia risul-
tati migliori di un numero minore per motivi che
vedremo (forse) la prossima puntata.
Ora siamo pronti per passare alla pratica!
uni PO 1 DI CODICE...
Ci appoggeremo alla libreria SNARLI, per ulteriori
informazioni vi rimando al box La libreria SNARLI, e
vedremo come creare una rete funzionante con un
relativamente breve "script" Java. Quello che faremo
è addestrare la nostra rete in modo tale che si com-
porti come una porta logica OR. Tale porta ha due
ingressi ed una sola uscita ed è definita come segue:
( X1
X2
Y ^
1
1
1
1
1
La classe che utilizzeremo è BPLayer che rappresen-
ta un layer di cui abbiamo prima parlato. Quindi
possiamo procede così:
// creiamo un layer d'ingresso ed uno d'uscita
// specificando quanti neuroni essi debbano contenere
public static void main(String[] args) {
BPLayer in = new BPI_ayer(2);
BPLayer out = new BPLayer(l);
//connettiamo l'uscita all'ingresso
out.connect(in);
//inizializziamo ogni peso sinaptico ad un numero
casuale
in.randomize(System.currentTimeMillis());
out.randomize(System.currentTimeMillis());
// prepariamo gli esempi come coppia
// ingresso-uscita desiderata
double[][] inPattern = {{0,0},{1,0},{0,1},{1,1}};
double[][] outPattern = {{0},{1},{1},{1}};
// forniamo gli esempi alla rete e diciamole di imparare
in.attach(inPattern);
out.attach(outPattern);
out.batch(10, 0.1,0);
il metodo batch riceve tre argomenti: il numero delle
epoche, il learning rate ed il momento. Di quest'ulti-
mo parametro non abbiamo discusso fino ad ora,
sappiate comunque che ponendolo a zero non com-
prometterete mai il funzionamento della rete.
Facciamo lavorare la rete passandogli gli ingressi e
stampando a schermo le risposte
double[][] inVal = {{1,0},{0,1},{1,1},{0,0}}, outVal;
in.attach(inVal);
outVal = out.testQ;
for(int i=0; i<inVal.length; i++)
System. out. println(inVal[i][0]+" "+inVal[i][l] +
"\t"+outVal[i][0]);
Non male vero? Ora proviamo a sfruttare tutte le po-
tenzialità di una rete neurale cambiando ad esem-
pio il training set (gli esempi) ha soli tre casi e verifi-
cate se successivamente la rete risponderà corretta-
mente, in fase di test, anche al quarto caso che non
ha mai visto. In altre parole modifichiamo le righe
della fase d'addestramento; una delle possibili scel-
te è:
double[][] inPattern = {{0,0},{1,0},{0,1}};
double[][] outPattern = {{0},{1},{1}};
Lasciando inalterato tutto il resto, come risponde la
rete? Se avete settato in maniera opportuna i para-
metri dovrebbe rispondere correttamente anche al-
l'ingresso 1 1.
Quello che vi consiglio è divertitevi a sottrarre alla
rete esempi e vi accorgerete che un esempio non va-
le l'altro!
CONCLUSIONI
In questo articolo abbiamo cominciato ad adden-
trarci nel mondo delle reti neurali cercando di met-
tere in evidenza le caratteristiche che le distinguono
dai paradigmi della programmazione classica ed
abbiamo anche visto anche come grazie a SNARLI
implementare una rete non sia poi così difficile.
Vi lascio con un quesito: al posto dell' OR provate ad
addestrare la rete con uno XOR ossia
( X1
X2
Y ^
1
1
1
1
1
In questo caso la rete non si comporta proprio bene
vero? Bé per chi non conoscesse già tutta la storia, se
volete, potete confrontare le vostre idee in merito sul
forum di ioProgrammo e comunque ci torneremo
su nel prossimo articolo.
Andrea Galeazzi
IL CALCOLO
PARALLELO NON
È SEMPLICE
Uno delle più grandi
sfide della teoria del
calcolo parallelo è
proprio l'efficienza nel
coordinare le varie
unità elaborative. In
linea di principio si
potrebbe pensare ad
esempio che utilizzan-
do un'architettura
costituita da un altissi-
mo numero di proces-
sori fornisca prestazio-
ni elevatissime.
In realtà, oltre che a
problemi energetici e
di dissipazione de calo-
re, esiste un problema
di gestione del paralle-
lismo stesso: è infatti
necessario impiegare
uno o più algoritmi che
gestiscano tutti i pro-
blemi che possono in-
sorgere nel calcolo pa-
rallelo, come ad esem-
pio conflitti d'accesso
alla memoria etc...
Così alla fine si arriva
in un punto in cui, in
termini computazio-
nali, nell'esecuzione di
tali algoritmi si spende
più di quanto si guada-
gni con l'aggiunta di
un altro processore.
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 71 ►
ADVANCED EDITION T ■ Garbage Collector
Far funzionare
bene la memoria
La gestione della memoria, è stato uno dei compiti più pesanti
per gli sviluppatori. Con l'avvento dei meccanismi di gestione
automatica, sarà il garbage collector a fare il lavoro sporco
Ci CD □ WEB
listati_gc.zip
jn
REQUISITI
■ imi i —
p^ Basidi
UJ J programmazione.NET
.NET Framework SDK
Ogni applicazione del mondo reale, duran-
te il suo ciclo di vita, dovrà utilizzare delle
risorse, siano esse dei file, delle connes-
sioni di rete, dei database o delle generiche loca-
zioni di memoria. Nel paradigma di programma-
zione orientato agli oggetti, una generica risorsa è
rappresentata da un oggetto di una data classe. Se
abbiamo bisogno di connetterci ad un database
SqlServer, utilizzeremo in .NET la classe SqlCon-
nection, che rappresenta la connessione ad una
sorgente dati. Per creare una connessione, è ne-
cessario creare un'istanza della classe, in C# ad
esempio mediante la famigerata istruzione new.
L'istruzione non fa altro che allocare una certa
quantità di memoria, tanto grande da contenere
l'istanza da creare e che rappresenterà la risorsa
stessa. Prima di utilizzare la connessione, l'area di
memoria deve essere inizializzata, in maniera che
l'istanza trovi tutto ciò di cui ha bisogno, e di ciò si
occupa il costruttore della classe. A questo punto
possiamo utilizzare la risorsa allocata, cioè l'istan-
za della classe, invocando ad esempio i suoi meto-
di, e quando non c'è più bisogno di essa ripulirne
la memoria. Ecco, di quest'ultima operazione non
dobbiamo preoccuparci, sarà il Garbage Collector
a stabilire quando la connessione non è più utiliz-
zata e quindi a liberare la memoria da essa occu-
pata. Semplice, ma non sempre è così. Per tipi
semplici come String o Int32, non è necessario
nessun trattamento particolare, ma quando si ha a
che fare con risorse come file o connessioni di rete
o genericamente risorse che sono wrapper di
risorse non gestite, il discorso cambia, e bisognerà
prestare particolari cure all'operazione di clean-
up della memoria.
LA MEMORIA HEAP
Tempo di realizzazione
-/ (-/ ~0
Il Common Language Runtime alloca la memoria
necessaria ad ogni oggetto da creare, in un'area di
memoria detta heap. La memoria heap è gestita
totalmente dal CLR, nel senso che quando un og-
getto non serve più, esso libera automaticamente
la memoria da esso occupata, senza necessità di
intervento dello sviluppatore da codice. Il CLR
mette in funzione il Garbage Collector, che deter-
mina quando l'oggetto ha terminato il suo ciclo di
vita. Ma qual è il momento giusto per attivare la
garbage collection? E come fa il garbage collector a
sapere che nessun altro avrà bisogno di un ogget-
to in seguito? Il CLR assume che la memoria heap
sia infinita. Ogni volta che viene utilizzata l'istru-
zione new per creare un'istanza, esso calcola la
quantità di byte necessari ad ospitare l'oggetto
nello heap e verifica che tale memoria sia disponi-
bile. Se ciò non fosse vero verrebbe generata
un'eccezione di tipo OutOfMemoryException. In
caso contrario invece l'oggetto viene allocato nel
punto indicato da un puntatore detto NextObjPtr,
che indica sempre in quale posizione dovrà essere
posizionato un nuovo oggetto da creare. L'istruzio-
Free Heap
1
objB
objA
C
NexlObjPtr
Fig. 1: La memoria heap con due oggetti
+ 72 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Garbage Collector ■ T ADVANCED EDITION
ne new restituirà l'indirizzo di memoria puntato
da NextObjPtr ed il puntatore verrà spostato nel
punto immediatamente successivo, all'oggetto
appena creato nello heap. Proviamo a immaginare
la memoria heap come un rettangolo, che viene
allocato dal basso verso l'alto, e supponiamo che
vi siano in memoria due oggetti objA e objB. La
situazione apparirebbe come in Figura 1.
/
/■
Free Heap
objC
objB
objA
C
NexlObjPlr
Fig. 2: Allocazione di un nuovo oggetto
A questo punto immaginiamo che l'applicazione
abbia creato un nuovo oggetto objC. Esso viene
posizionato nel punto indicato da NextObjPtr, il
quale verrà poi spostato subito dopo l'oggetto
objC. Il garbage collector però non può entrare in
azione solo quando il puntatore è oltre lo spazio
disponibile, cioè non aspetta l'ultimo momento,
ma per semplificare per ora pensiamo che sia così.
OGGETTI VIVI E NON
Quando si finisce di utilizzare un oggetto è bene li-
berarne la memoria, onde evitare cosiddetti me-
mory leaks, a questo punto è necessario fare atten-
zione a non usare oggetti già "disposed", incorren-
do ad esempio in errori di puntatori nulli. Il garba-
ge collector verifica che nella memoria heap non
ci siano oggetti inutilizzati, in maniera da poterli
rilasciare e quindi liberare la memoria che essi
occupavano.
Per stabilire se un oggetto non è utilizzato da nes-
suno, ogni applicazione possiede un insieme di
riferimenti roots (radici), che sono delle locazioni
di memoria contenenti dei riferimenti a oggetti
nello heap oppure degli oggetti nuli II Common
Language Runtime distingue inoltre fra riferimen-
ti root e riferimenti non root. Sono root ad esem-
pio tutte le variabili statiche di un tipo riferimen-
to, o ancora tutte le variabili locali di tipo riferi-
mento o i parametri di metodi sullo stack, mentre
riferimenti non-root sono ad esempio i campi di
un'istanza.
L'esistenza di un riferimento root ad un oggetto è
già sufficiente per affermare che esso è utilizzato
dall'applicazione, mentre un oggetto senza riferi-
mento root è un potenziale oggetto non più in uso.
Prima di andare avanti facciamo un esempio che
chiarisca le idee, utilizzando un po' di codice, in
maniera da togliere qualche dubbio su oggetti
necessari e non necessari, e su come essi vengono
giudicati tali. Consideriamo il seguente codice C#:
class Auto {
public int velocita;
public void Accelera(int v)
{
velocita=v;
>
public class MyApp
{
public static Auto stAuto= = new AutoQ;
static void Main()
{
111. crea l'oggetto auto nello heap, alfa è un
riferimento root
Auto alfa = new AutoQ;
for(int i=0;i<100;i++)
{
alfa.Accelera(i);
>
111. Creo un nuovo oggetto mai referenziato
Auto beta=new AutoQ;
113. alfa è ancora un riferimento root
Console. Writel_ine(alfa. velocita);
//4. alfa è ancora nello scope, ma non è più
referenziata
Console. ReadLineQ;
>
Il concetto di variabile "viva" o "non viva" non è
basata sullo scope. Il CLR si basa su informazioni
che il compilatore JIT gli fornisce, in maniera da
sapere per ogni istruzione da eseguire, in anticipo,
se un oggetto sarà ancora referenziato o meno.
• Nel punto 1 l'oggetto alfa viene creato sullo
heap, ed è un riferimento root, il compilatore
JIT e il CLR infatti sanno che l'oggetto alfa ver-
rà utilizzato nel ciclo for seguente.
• Nel punto 2 viene creato un oggetto beta, ma
esso non è un riferimento root, in quanto non
sarà mai utilizzato, è il compilatore JIT ancora
una volta che permette al CLR di conoscere
I TUOI APPUNTI
Utilizza questo spazio per
le tue annotazioni
GLOSSARIO
FINALIZATION
QUEUE
Il meccanismo di garba-
ge collection non effet-
tua direttamente il rila-
scio della memoria de-
gli oggetti non più uti-
lizzati, ma inserisce tali
oggetti in una coda di
finalizzazione, da cui un
thread secondario li
pescherà e si occuperà
della finalizzazione vera
e propria.
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 73 ►
ADVANCED EDITION T ■ Garbage Collector
DISPOSE
PATTERN
Il processo di Garbage
Col lect ioti è non deter-
ministico. Nel senso
che non possiamo mai
sapere quando esso
entrerà in funzione, an-
che se siamo noi a for-
zarlo, con i metodi del-
la classe GC, ed inoltre
non è prevedibile l'or-
dine con cui verranno
"finalizzati" gli oggetti
non più utilizzati.
Se si ha necessità di un
meccanismo determini-
stico che effettui il
Dispose di un oggetto
e delle risorse da esso
utilizzate, soprattutto
se si tratta di risorse
unmanaged, dobbiamo
implementare il cosid-
detto pattern Dispose.
questa informazione.
• Nel punto 3, alfa è ancora riferimento root, in
quanto verrà utilizzato subito dopo nell'istru-
zione WriteLine.
• Nel punto 4, alfa è ancora nello scope, però
non sarà più referenziata, quindi il Garbage
Collector può anche liberarne la memoria.
Al di fuori del metodo Mairi, è stato creato un altro
oggetto statico stAuto, in quanto statico esso po-
trebbe essere utilizzato in qualunque momento
durante il ciclo di vita dell'applicazione, quindi è
un riferimento root.
L'ALGORITMO DI
GARBAGE COLLECTIOIU
Ci sono dei problemi che i sviluppatori C/C++ o di
un altro linguaggio senza meccanismi di gestione
automatica della memoria si trovano a dover af-
frontare giornalmente, è che sono una delle prin-
cipali fonti di bug e/o malfunzionamenti.
Nel framework .NET, il compilatore JIT e il CLR si
occupano di mantenere la lista di tutti i riferimen-
ti root dell'applicazione.
Per trovare gli oggetti non più utilizzati, il garbage
collector utilizza un meccanismo a due fasi, detto
di Mark/Compact.
Nella prima fase, il garbage collector di default as-
sume che nessun oggetto fra quelli in memoria
heap sia utilizzato, cioè parte dal presupposto che
non esista nessun riferimento root verso oggetti
nello heap.
Quindi inizia dal primo elemento della collezione
di root e marca tutti gli oggetti dello heap che sono
direttamente raggiungibili e, mediante una proce-
dura ricorsiva, ogni oggetto che è riferito attraver-
so altri oggetti (Figura 3).
objE
-
NextObjPi
objC
objB
objA
E così per ogni riferimento root. Per incrementare
le performance dell'algoritmo, quando si incontra
un oggetto che è già stato inserito nel grafo, l'algo-
ritmo continua con il root successivo, evitando di
ripetere percorsi già visti e di entrare in qualche
percorso circolare. Alla fine del procedimento,
sarà stato creato un grafo contenente tutti gli
oggetti in qualche maniera raggiungibile da un'ap-
plication root, cioè tutti gli oggetti ancora utilizza-
ti nell'applicazione.
Nella seconda fase, quella di Compact, il garbage
collector può percorrere lo heap e liberare le loca-
zioni di memoria di oggetti senza riferimenti. Se
vengono trovati grossi spazi di memoria contigui,
allora la memoria viene compattata e gli applica-
tion root sono aggiornati con i nuovi indirizzi nello
heap. I piccoli blocchi di memoria invece vengono
saltati per incrementare la velocità dell'algoritmo.
Alla fine il puntatore NextObjPtr punterà al primo
spazio libero immediatamente successivo all'ulti-
mo oggetto in memoria, come in Figura 4, il bloc-
co in rosso indica invece un oggetto non più utiliz-
zato, cioè garbage da rimuovere.
objC
objA
Fig. 3: Riferimenti root in memoria heap
Fig. 4: La memoria heap dopo la creazione di un
nuovo oggetto
È chiaro che con un meccanismo del genere, i mal-
funzionamenti accennati all'inizio del paragrafo
possono essere dimenticati, in quanto non ci sarà
necessità di ricordarsi di ripulire la memoria da
oggetti non più utilizzati, e sarà impossibile utiliz-
zare oggetti già rimossi, in quanto è il CLR che sa
già quali oggetti devono ancora essere utilizzati.
GENERAZIONI
DI OGGETTI
Il garbage collector di .NET utilizza dei concetti
che si sono dimostrati utili e necessari per miglio-
rare le performance di tutto il meccanismo. Esso
non entra in azione quando tutta la memoria heap
è esaurita , ed inoltre non tutti gli oggetti hanno la
stesa importanza, magari ne esistono alcuni in
* 74 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Garbage Collector ■ T ADVANCED EDITION
memoria da molto tempo, e dovranno ancora re-
starci per ancora più tempo. In definitiva, il garba-
ge collector di .NET appartiene alla famiglia di
garbage collector generazionali, che si basano su
semplici ma dimostrabili assunzioni.
• Da meno tempo esiste un oggetto, tanto più
breve sarà la sua vita.
• Da più tempo esiste un oggetto, tanto più lun-
ga sarà la sua vita.
• Ripulire una piccola porzione di memoria ri-
chiede meno tempo che ripulirla tutta.
Quando la memoria heap viene inizializzata, essa
sarà naturalmente vuota. I primi oggetti istanziati
sullo heap faranno parte della prima generazione
di oggetti, detta generazione 0, alla cui dimensione
il CLR assegna anche una soglia massima, na-
turalmente minore della dimensione totale dello
heap.
Supponiamo che la dimensione massima per la
generazione sia di 256KB, e quindi di creare un
po' di istanze, come in Figura 5:
Free Heap
/k
1
Free Heap
/ Soglia max B
k generazione D L
objF
^^^
objE
ObjD
ObjD
objC
ObjC
objB
objB
objA
objA
©
©
'
Free Heap
'
objF
Generazione
Generazione 1
1
objE
!
objC
'
, m
®
Fig. 5: Nuova generazione dopo una garbage collec-
tion
utilizzati [objB e objD) e farà incrementare la ge-
nerazione degli oggetti restanti {objA e objQ, in
questo caso passeranno tutti alla generazione 1.
Se a questo punto vengono creati nuovi oggetti,
essi faranno parte di una nuova generazione
{objE e objF). Quindi la generazione contiene
sempre l'insieme delle istanze più giovani, quelle
create più di recente, mentre, come detto, dopo
ogni garbage collection la generazione è sempre
vuota. Creando delle nuove istanze a questo
punto si avrà una nuova generazione 0, e quando
si raggiunge nuovamente la soglia di 256K della
generazione 0, il garbage collector inizierà una
nuova "passata".
Così come per la 0, anche la generazione 1 avrà
una sua soglia massima, supponiamo di 1MB, ed
ogni volta che si ha una garbage collection, deve
essere deciso su quale generazione o generazioni
di oggetti agire. Ciò dipende dalla memoria occu-
pata da ognuna di esse. Se ad esempio la genera-
zione 1 ha una soglia di 1MB, ed è occupata solo
per 500Kb, la garbage collection avrà luogo solo
sulla generazione 0. Questo per ottimizzare le per-
formance del meccanismo agendo su una memo-
ria più piccola.
Dopo questa seconda garbage collection, anche
altri oggetti sopravvissuti, passeranno alla gene-
razione 1, che quindi avrà una dimensione mag-
giore. Se il processo avesse interessato anche la
generazione 1 , cosa che avviene solo quando viene
raggiunta la soglia di 1MB, ed in genere dopo
molte garbage collection di generazione 0, i
sopravvissuti della 1 sarebbero passati ad una
generazione 2. La generazione 2 quindi conterrà
degli oggetti che sono stati esaminati come mini-
mo 2 volte, cioè gli oggetti con l'età più alta.
Il processo di garbage collection attualmente im-
plementato nel CLR supporta le generazioni 0,1 e
2 (vedi il prossimo paragrafo per ulteriori infor-
mazioni) ognuna con una dimensione massima,
stabilita durante l'inizializzazione del Runtime, ed
aggiornate ad ogni garbage collection in modo da
ottimizzare le performance.
LA CLASSE SYSTEM.GC
Il garbage collector può essere controllato pro-
grammaticamente utilizzando la classe System.GC
del .NET framework.
Come detto nel precedente paragrafo, il massimo
numero di generazioni implementato attualmen-
te è 3, che potete verificare leggendo la proprietà
MaxGeneration della classe GC.
BIBLIOTECA
• C# AND .NET PLATFORM
Troelsen
(APress)
• APPLIED MICROSOFT
.NET PROGRAMMING
Richter
(Microsoft Press)
• ESSENTIAL .NET,
VOLUME I: THE
COMMON LANGUAGE
RUNTIME
Don Box, Chris Sells
(Addison Wesley)
Se la creazione di un nuovo oggetto farà superare
la soglia della generazione 0, verrà azionato il gar-
bage collector, che rimuoverà gli oggetti non più
int maxGen=GC. MaxGeneration;
Console. Writel_ine("max generation number:
{0}\maxGen);
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 75 ►
ADVANCED EDITION T ■ Garbage Collector
Attenzione al fatto che la proprietà restituisce il
valore 2, ad indicare il numero identificativo mas-
simo, e cioè abbiamo tre generazioni possibili, la
generazione 0, la 1 e la 2.
La classe GC mette poi a disposizione il metodo
GetGeneration, che restituisce la generazione di
cui fa parte un oggetto.
Ad esempio definiamo una nostra classe MyClass,
con il solo distruttore in maniera da avere un ri-
scontro della sua effettiva finalizzazione:
class MyClass
i
int id;
public MyClass(int id)
{
this.id=id;
>
-MyClassQ
Console. Writel_ine("oggetto distrutto");
mente, e ad un certo punto effettuiamo un salva-
taggio su database. Potrebbe essere utile invocare
il metodo Collect per forzare una garbage collec-
tion su tutte le generazioni, che certamente saran-
no piene di oggetti non più necessari perché sal-
vati fisicamente.
La classe GC inoltre permette di conoscere la me-
moria approssimativa allocata. Il metodo statico
GetTo talMemory restituisce il numero di byte allo-
cati. Per esempio proviamo a farci restituire la me-
moria occupata prima e dopo un ciclo for che
istanzia qualche migliaio di oggetti.
long freeMem=GC.GetTotalMemory(false);
Console. Writel_ine( "Total Memory: {0}",freeMem);
for(int i = l;i<100000;i + + )
{
MyClass o=new MyClass(i);
}
freeMem=GC.GetTotalMemory(false);
Console. Writel_ine( "Total Memory: {0}",freeMem);
Potete contattare
l'autore per
suggerimenti, critiche
o chiarimenti
all'indirizzo e-mail
antonio.pelleriti@
ioprogrammo.it.
oppure sul sito
www.dotnetarchitects.it
>
Se creiamo un'istanza della classe essa entrerà a
far parte della generazione 0, ed infatti se invo-
chiamo il metodo GetGeneration ne avremo la
conferma:
MyClass obj = new MyClass(O);
int objGen=GC.GetGeneration(obj);
Console. WriteLine("obj Generation: {0}",objGen);
Il meccanismo di garbage collection può essere
forzato per mezzo del metodo Collect, di cui esiste
una versione senza parametri per agire su ogni
generazione, mentre passando un argomento in-
tero, minore o uguale a GCMaxGeneration, si agi-
sce solo sulla generazione indicata.
Se dopo la costruzione dell'oggetto, eseguissimo
ad esempio il codice seguente:
GC.Collect(Q);
objGen=GC.GetGeneration(obj);
Console. WriteLine("obj Generation after Collect(O):
{0}",objGen);//passa alla gen 1
Otterremmo lo spostamento di obj alla generazio-
ne 1. Ed analogamente utilizzando ora il parame-
tro 1, l'istanza obj passerebbe in generazione 2, e
non oltre naturalmente, anche con ulteriori invo-
cazioni di Collect.
È bene non abusare del metodo Collect, in quanto
sarà il CLR a stabilire il momento opportuno di
farlo, sulla base di ciò che avviene nell'applicazio-
ne. In alcuni casi può però servire.
Ad esempio supponiamo di avere grosse moli di
dati in memoria, sotto forma di oggetti natural-
provando ad eseguire il codice noterete un netto
incremento della variabile mem.
Per completare la rapida carrellata sulla classe GC,
mostriamo anche i metodi SuppressFinalize e
WaitForPendingFinalizers. Quando degli oggetti
posssono essere rimossi dalla memoria, essi ven-
gono inseriti in una coda di finalizzazione.
Il garbage collector ad un certo punto invocherà il
distruttore, cioè il metodo Finalize, di tali oggetti.
Il metodo WaitForPendingFinalizers sospende il
thread in esecuzione fino a quando la coda di fi-
nalizzazione non è stata svuotata. Il metodo Sup-
pressFinalize invece permette di rimuovere un
metodo dalla coda di finalizzazione.
CONCLUSIONI
Normalmente non c'è alcun motivo di utilizzare
manualmente il meccanismo di Garbage Collector,
sarà sempre .NET a fare il lavoro sporco al posto
nostro. Nei casi in cui sia opportuno ricorrere ad
un'ottimizzazione dell'uso della memoria, la clas-
se GC è l'unico ponte che ci viene offerto per en-
trare in contatto con il sistema. Senza contare che
l'esistenza di questa classe ci consente di svilup-
pare applicazioni che possono produrre un'out-
put contenenten il monitoring del controllo della
memoria.
Si tratta di una delle funzioni più delicate del siste-
ma, pertanto va utilizzata con cautela e tuttavia la
conoscenza di come avvengono i meccanismi di
gestione della memoria in .NET può condurci alla
creazione di applicazioni estremamente ottimiz-
zate.
Antonio Pelleriti
* 76 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Persistenza dei dati ■ T ADVANCED EDITION
Castor il gemello
minore di Hibernate
Impareremo a realizzare applicazioni Java che fanno largo uso
di database. Affideremo però tutta la gestione di SQL e delle relazioni
a un tool OpenSource per poterci dedicare esclusivamente agli oggetti
In questo articolo realizzeremo in Java una pic-
cola rubrica di indirizzi. Al di la dell'utilità in-
dubbia di questo genere di applicazione, sarà
importante notare come verranno risolti i proble-
mi di "persistenza dei dati". In particolare, utilizze-
remo Castor, una libreria OpenSource che im-
plementa un framework per mappare i dati pre-
senti in un database relazionale in corrispondenti
classi e oggetti conformi alla programmazione
OOP. Il meccanismo di funzionamento è del tutto
simile a quello implementato da Hibernate.
DALL'IDEA
AL PROGETTO
Nella nostra rubrica l'elemento principale è il Sog-
getto - il nominativo da registrare; Il soggetto sicu-
ramente sarà dotato di due attributi: nome e co-
gnome. Opzionalmente potranno essere presenti
indirizzi email, indirizzi fisici e numeri di telefono.
Per mantenere la più alta flessibilità possibile
(considerando anche la persistenza sarà eseguita
in automatico tramite Castor e dunque non richie-
derà particolari sforzi di programmazione), si è
deciso che questi elementi saranno implementati
in entità diverse. Il modello dei dati della rubrica è
dunque composto dalle seguenti classi
Soggetto;
• Email;
• Indirizzo;
• NumeroTelefono.
Il Soggetto contiene i seguenti elementi:
• id
• nome;
• cognome;
• telefoni;
• email;
• indirizzi.
Email, Indirizzo e NumeroTelefono derivano da
una superclasse comune astratta, DatoSoggetto,
che contiene gli attributi:
• id;
• soggetto;
• oggetto.
In queste classi il campo id è la chiave univoca, di
tipo long, che identifica univocamente ciascun
soggetto, email, indirizzo o numero di telefono. In
DatoSoggetto, l'attributo soggetto è un riferimento
al soggetto a cui appartiene quell'elemento. Si im-
magini l'indirizzo email mario@rossi.it: l'oggetto
che rappresenta questa email è legato direttamen-
te al nominativo "Mario Rossi". Questo legame
permette di sapere che, ad esempio, questo indi-
rizzo non appartiene al nominativo "Alberto Bian-
chi". Il campo oggetto contiene invece una de-
scrizione che permette di distinguere diversi ac-
count di email, indirizzi o numeri di telefono. Ad
esempio, Mario Rossi potrebbe avere anche la mail
mariorossi@soletech.it; in questo caso V oggetto
varrà "lavoro", mentre nell' oggetto che contiene
mario@rossi.it oggetto sarà "casa". Si può pensare
COME INIZIARE
È necessario avere ov-
per il common log-
viamente installato il
ging commons-log-
J2SE1.5, gli altri file
ging.jar che fa parte
notevoli sono castor-
del pacchetto
0.9.6.tgz da scompat-
commons-logging-
tare e porre in una di-
1.0.4.tar.gz -
rectory del classpath
http://jakarta.apache.org
di Java. All'interno
/site/down loads
del classpath sono
/downloads commons
necessarie il file
.html - Potete scarica-
xerces.jar che fa parte
re tutto dal web op-
del pacchetto Xerces-
pure ovviamente uti-
J-bin.1 .4A.tar.gz -
lizzare il software
http://xml.apache.org
contenuto nel ed alle-
/xerces-j/ - e la libreria
gato alla rivista.
^
J
Ci CD □ WEB
RubricaCastor.zip
jn
REQUISITI
■ imi i ^m
j=ji Basidi
tMf ) programmazione Java
Java2 SDK 1.4.2
^^a^as^i
Tempo di realizzazione
v x-o
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 79 ►
ADVANCED EDITION T ■ Persistenza dei dati
PER SAPERNE
DI PIÙ
DOCUMENTAZIONE
DI CASTOR
La documentazione
della struttura dei file
XML di Castor è
presente all'indirizzo
http://www.castor.org
/jdo-mapping.html, dove
è presente il reference
di tutti i tag che è
possibile posizionare
nei file di
configurazione.
all' oggetto come una dicitura che fa distinguere le
diverse email, indirizzi o numeri di telefono.
In particolare, queste tre entità dispongono di una
serie di attributi. La classe Email contiene solo il
campo email che contiene il nome dell'account;
la classe Indirizzo contiene invece:
• via;
• numero;
• cap;
• città;
la classe NumeroTelefono contiene invece solo:
• prefisso;
• numero;
L'IMPLEMENTAZIONE
La classe Soggetto è implementata come una nor-
male classe Java, che però deve rispettare, al fine di
ottenere la persistenza automatica con Castor,
una serie di convenzioni. In particolare:
• deve essere presente la chiave univoca, infatti
è implementato l'attributo id di tipo long;
• deve essere presente un costruttore di default
(quello senza parametri);
• ogni attributo che deve essere reso persistente
deve avere setter/getter (in realtà Castor per-
mette anche un accesso diretto, ma si è scelto
di seguire comunque la strada che prevede
l'uso di getter e setter, in quanto più allineata
alle convenzioni del mondo Java;
La classe è così implementata:
package it.edmaster.rubrica. model;
public class Soggetto {
long id;
String nome;
String cognome;
NumeroTelefono[] telefoni;
Email[] email;
Indirizzo[] indirizzi;
public Soggetto() {
_>
public Soggetto( String nome, String cognome ) {
this.nome = nome;
this. cognome = cognome;
_>
public String getlntestazioneQ {
return getlntestazioneQ;
>
//... getter e setter omessi
return nome
+ cognome;
>
public String toStringQ {
>
si noti della presenza del metodo getlntestazioneQ,
che ritorna una concatenazione del nome e del
cognome del soggetto, che verrà utilizzata come
intestazione della scheda del nominativo; è pre-
sente anche l'implementazione del metodo to-
StringQ, che non fa altro che ritornare l'intestazio-
ne. Quando è possibile, è utile implementare que-
sto metodo, definito nella classe Object, in modo
che, stampando un oggetto, si ottenga una descri-
zione dello stesso. Ad esempio:
Soggetto s = new Soggetto("Mario Rossi");
System. out.println(s);
stampa:
Mario Rossi
Se non fosse stato implementato toStringQ, si
avrebbe ottenuto il comportamento standard,
presente nella classe Object, che presenta una cosa
del tipo:
Soggetto@B382
Il codice completo (meno getter e setter) della clas-
se DatoSoggetto è il seguente:
package it.edmaster.rubrica. model;
public abstract class DatoSoggetto {
Soggetto soggetto;
String oggetto;
long id;
/** richiesto dalle sottoclassi */
public DatoSoggetto() {}
public DatoSoggetto(Soggetto soggetto, String
oggetto) {
this. soggetto = soggetto;
this. oggetto = oggetto;
_>
public abstract String[] getContenuto();
//getter e setter omessi
}
si noti il costruttore vuoto, richiesto dalle sotto-
classi, ed il metodo astratto getContenutoQ, che
viene implementato dalle sottoclassi in funzione
del tipo di dato che contengono. Questo metodo
ritorna un array di stringhe che rappresentano il
contenuto; nel caso delle email, viene ritornato
semplicemente un array di un elemento con, ad
esempio, mario@rossi.it. Nel caso degli indirizzi la
cosa è più complessa, visto che il contenuto si di-
+ 80 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Persistenza dei dati ■ T ADVANCED EDITION
spone su più righe. Il fatto di avere questo metodo
a livello di superclasse permette di strutturare le
classi che implementano l'interfaccia utente in
modo da trattare in generale con DatoSoggetto, e
non in particolare con Email, Indirizzo o Nume-
roTelefono.
Questo è uno degli aspetti potenti della OOP, che
consente di risparmiare codice, si vedrà più avan-
ti come.
CONFIGURARE CASTOR
La cosa "magica" nei toolkit di persistenza come
Castor, Hibernate o Apache ObjectRelational Brid-
ge è che sono in grado di eseguire la creazione,
cancellazione ed aggiornamento delle entità su
base dati semplicemente analizzando le classi del-
l'applicazione con la reflection ed utilizzando una
manciata di informazioni di configurazioni pre-
senti in file XML di supporto.
In particolare, Castor utilizza un file che per de-
fault si chiama database. xml, e che nel caso di
questo progetto è il seguente:
<!DOCTYPE databases PUBLIC "-//EXOLAB/Castor
JDO Configuration DTD Version 1.0//EN"
"http://castor.exolab.org/jdo-conf.dtd">
<database name="rubrica" engine="mysql" >
<driver url = "jdbc:mysql://localhost/rubrica"
class-name="org.gjt.mm.mysql. Driver" >
<param name="user" value="root" />
<param name="password" value="mysql" />
</driver>
<mapping href="support/mapping.xml" />
</database>
come si nota osservando il contenuto, Castor è
configurato per memorizzare i dati in un database
MySQL. Si notano infatti gli URL relativi al server,
il nome del database ("rubrica") e le informazioni
di autenticazione necessarie ad accedere al server.
Al termine di database.xml è presente poi un rife-
rimento ad un ulteriore file: mappingxml. Questo
contiene la configurazione sulla persistenza di
ciascuna classe. Ad esempio, Soggetto è definito
come segue:
<class name="it.edmaster. rubrica. model. Soggetto"
identity="id" key-generator="IDENTITY">
<description>Soggetto</description>
<map-to table="SOGGETTI" />
<field name="id" type="long">
<sql name="ID" type="integer" />
</field>
<field name="nome" type="string">
<sql name="NOME" type="char" />
</field>
<field name=
"cognome"
type='
'string"
'>
<sql name=
"COGNOME
" type=
= "char'
'/>
</field>
come si nota, l'elemento principale <class> si
aspetta un attributo "name" che specifica la clas-
se, completa di package, a cui si sta facendo riferi-
mento. Molto importante sono i due attributi se-
guenti:
• identity - Indica qual è il campo Java (non la
colonna del database) che contiene la chiave
dell'identità della classe. L'identità è il campo
che individua univocamente una istanza di
questa classe e corrisponde sul database alla
chiave primaria;
• key-generator - Questo attributo è indispensa-
bile per definire come Castor crea una nuova
chiave primaria per questa classe. In questo
caso è stato utilizzato l'attributo IDENTITY,
che sostanzialmente utilizza la strategia nativa
del database, ma ci sono anche altri algoritmi
dove è Castor il principale responsabile per la
creazione della nuova chiave. Utilizzando My-
SQL è possibile utilizzare i campi interi con at-
tributo autojncrement, che hanno la caratte-
ristica di generare sempre un nuovo numero
ad ogni inserimento nella tabella.
All'interno di <class> è presente l'elemento <map-
to> che indica la tabella su cui memorizzare i dati.
Sono presenti anche uno o più elementi <field>,
che definiscono, insieme agli elementi figli <sql>,
quali campi della classe devono essere resi persi-
stenti e come. Specificano anche il tipo nativo
(presente in <field>) e quello SQL (presente in
<sql>). L'attributo "name" specifica il nome del
campo (in <field>) ed il relativo nome di colonna
{in <sql>).
Oltre ai campi basilari visti sopra, il mapping della
classe Soggetto prevede anche degli oggetti figli,
che sono in relazione l:n. Per configurare questa
relazione è sufficiente specificare l'attributo col-
lection, indicandone il tipo e, all'interno di <field>,
specificare il tag <sql> indicando la chiave esterna
nell'attributo "many-key". In questo caso, il cam-
po delle tabelle per gli elementi figli contengono
sempre un campo ID_SOGGETTO che contiene
l'ID del soggetto a cui appartengono. Questa è una
informazione indispensabile per porre in relazio-
ne la tabella SOGGETTO con le altre del database:
<field name="email" type="it.edmaster
.rubrica. model. Email" collection =
CASTOR, XML
ED LDAP
Castor non è solo
persistenza, ma anche
esportazione ed
importazione dei dati
in XML ed accesso ad
LDAP. Quest'ultima è
una tecnologia per
accedere a server
specifici che
memorizzano
informazioni in modo
gerarchico.
array >
<sql many-key="ID_SOGGETTO"/>
</field>
<field name="telefoni" type="it.edmaster.rubrica
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 81 ►
ADVANCED EDITION T ■ Persistenza dei dati
.model. NumeroTelefono" collection = "array">
<sql many-key="ID_S0GGETT07>
</field>
</field>
LAYOUT
MANAGER
Una delle caratteristi-
che più potenti - ma
complesse - di Java è
che questi non posizio-
na i componenti
dell'interfaccia utente
in modo assoluto, ma
utilizzando degli og-
getti - i layout mana-
ger - che contengono
una logica di dimensio-
namento e posiziona-
mento degli oggetti.
Questo garantisce la
portabilità delle inter-
facce sui diversi siste-
mi operativi, ma richie-
de anche più codice.
<field name="indirizzi" type="it.edmaster.rubrica
.model. Indirizzo" collection = "array">
<sql many-key="ID_SOGGETTO"/>
</field>
</class>
in questo caso le collezioni sono array, ma Castor
supporta anche Vector, Hashtable, Collection,
ArrayList, Set e Map. La configurazione della rela-
zione deve essere completata nella definizione
della classe collegata a Soggetto; ad esempio, si
analizzi la configurazione della classe Indirizzo:
<class name="it.edmaster.rubrica. model. Indirizzo"extends=
"it.edmaster.rubrica. model. DatoSoggetto" depends=
"it.edmaster.rubrica. model. Soggetto"identity="id">
<descnption>indirizzo</description>
<map-to table="INDIRIZZO" />
<field name="via" type="string">
<sql name="VIA" type="char" />
</field>
<field name="numero" type="string">
<sql name="NUMERO" type="char" />
</field>
<field name="cap" type="string">
<sql name="CAP" type="char" />
</field>
<field name="citta" type="string">
<sql name="CITTA" type="char" />
</field>
<field name="soggetto" type="it.edmaster
.rubrica. model. Soggetto">
<sql name="ID_SOGGETTO" />
</field>
</class>
si noti il campo "soggetto ": viene indicato che è di
tipo it.edmaster.rubrica. model Soggetto, cosa che
chiude, insieme all'attributo "depends" dell'ele-
mento <class>, il giro della relazione l:n con la
tabella SOGGETTI.
A questo punto è necessario far capire a Castor
che le classi Email, Indirizzo e NumeroTelefono so-
no in realtà delle sottoclassi di DatoSoggetto, la cui
definizione è la seguente:
<class name='
it.edmaster.rubrica. model
.DatoSoggetto" identity="id"
key-generator="IDENTITY">
<map
-to table="DATI_SOGGETTO" />
<field
name=
"id" type="long">
<sql
name=
"ID" type="integer" />
</fielc
>
<field
name=
"oggetto" type="string">
<sql
name=
"OGGETTO" type="char" />
</class>
come si nota, anche in questo caso viene utilizza-
ta l'identità del database; nella colonna OGGETTO
viene memorizzato l'oggetto, mentre ci si potrà
chiedere perché manca ID_SOGGETTO. D'altra
parte, se l'ID di DATI_SOGGETTO permette di an-
dare sulle tabelle delle sottoclassi, ad esempio
EMAIL, per ottenere la riga relativa, perché ID_
SOGGETTO non è in DATI_SOGGETTO ma in cia-
scuna tabella delle sottoclassi? Questa è una limi-
tazione di Castor, che costruisce le chiavi SQL in
modo da puntare direttamente alle tabelle figlie;
costruire una query che accedesse anche a DATI_
SOGGETTO sarebbe stato molto più complesso e
forse non portabile su tutti i database server.
L'ultimo passo è creare un file .SQL con i campi
che servono all'applicazione. Per brevità ne ripor-
tiamo un estratto. Il db completo è comunque di-
sponibile nel codice allegato alla rivista
CREATE TABLE dati_soggetto (ID int(ll) NOT NULL
autojncrement, OGGETTO varchar(40) default NULL,
PRIMARY KEY (IP)) TYPE=MyISAM;
CREATE TABLE email (ID int(ll) NOT NULL
autojncrement, EMAIL varchar(40) NOT NULL
default ", ID_SOGGETTO int(ll) NOT NULL default
'0', PRIMARY KEY (ID)) TYPE=MyISAM;
Esiste qualche metodo per creare automatica-
mente il file .SQL partendo dal file di mapping,
tuttavia al momento utilizzeremo una configura-
zione manuale.
PERSISTENZA DEI DATI
Per introdurre i dati nel database sarebbe neces-
saria una complessa interfaccia utente, dunque in
questa fase vengono creati dei soggetti di prova,
istanziando direttamente le classi di dominio. Ad
esempio, il codice seguente crea il soggetto Marco
Mari, dotato di due indirizzi email, un numero di
telefono e dell'indirizzo di casa e del lavoro:
Soggetto s2
= new Soggetto("Marco",
'Mari");
emails = new Email[2];
emails[0] =
new Email(s2, "personale",
"mmari@email.it");
emails[l] =
new Email(s2, "lavoro",
"mmari@genertech.com");
s2.setEmail(
emails );
telefoni = new NumeroTelefono[l];
telefoni[0] =
new NumeroTelefono( s2,
"347"
"cell.l",
, "1236475" );
s2.setTelefoni( telefoni );
indirizzi = new Indirizzo[2];
^ 82 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Persistenza dei dati ■ T ADVANCED EDITION
indirizzi[0] = new Indirizzo(s2, "casa", "Via Larici",
"7", "Corsico (MI)", "20100");
indirizziti] = new Indirizzo(s2, "lavoro", "Via De
Amicis", "200", "Milano", "20131");
s2.setlndirizzi( indirizzi );
la classe CreaSoggetti crea tre soggetti, e li persiste
utilizzando le API di Castor. In particolare, è ne-
cessario utilizzare una istanza dell'oggetto Data-
base, avviando la transazione con beginQ, creando
l'oggetto con createQ e concludendo con commitO
e closeQ. Castor delimita ogni operazione sulla
base dati racchiudendole tra i metodi beginQ e
commitO:
db.begin();
Soggetto[] soggetti = new Soggetto[] { si, s2, s3 };
for (int i=0; i<soggetti.length; i++) {
db.create( soggettai] );}
db. commitO;
db.close();
l'oggetto Database necessario ad operare con Ca-
stor viene ottenuto nel programma utilizzando
una classe di supporto, CastorUtils:
package it.edmaster.rubrica.util;
import org.exolab.castor.jdo. Database;
import org.exolab.castor.jdo
.DatabaseNotFoundException;
import org.exolab.castor.jdo.*;
import org.exolab.castor.mapping.MappingException;
public class CastorUtils {
JDO jdo;
private CastorUtils() {}
public static CastorUtils getlnstance() {
return new CastorUtils();
_}
public Database createSession() throws
DatabaseNotFoundException,
PersistenceException {
if( jdo == nuli ) {
jdo = new JDOQ;
jdo.setDatabasel\lame("rubrica");
jdo.setConfiguration("support/database.xml");
jdo.setClassl_oader(
getClass().getClassl_oader() );
}
Database database = jdo.getDatabase();
return database;
CastorUtils è un singleton, infatti implementa il
metodo getlnstanceQ che ritorna un oggetto di
questa classe, che contiene anche il metodo crea-
teSessionQ. Questo metodo crea una istanza di
Database utilizzando un oggetto JDO che viene
inizializzato impostando il nome del database, il
percorso del file di configurazione ed un class loa-
der, che sarà quello utilizzato per accedere alle
classi rese persistenti.
INTERFACCIA UTENTE
Arrivati a questo punto è abbastanza semplice
creare un'interfaccia utente adeguata. I dati pos-
sono essere recuperati dal database attraverso ca-
stor con una sintassi del tipo:
soggetto. getTelefoni()
soggetto. getEmail().length + 1
Una query al database può essere fatta attraverso
OQL (Object Query Language), molto simile a SQL,
con una sintassi del tipo
public ElencoSoggetti() throws
DatabaseNotFoundException, PersistenceException {
OQLQuery query;
Database db = CastorUtils. getlnstance()
.createSession();
db.begin();
query = db.getOQLQuery("SELECT s FROM it
.edmaster. rubrica. model. Soggetto s");
QueryResuIts results = query.execute();
Il risultato di una query è un oggetto QueryResuIts,
che viene convertito in una lista in questo modo:
elenco = new Arrayl_ist( results
size() );
while( results. hasMore() ) {
elenco. add( results. next() );
}
alla fine di tutto, nonostante questa sia una opera-
zione di lettura e non di scrittura, è necessario
eseguire una commitO ed un closeQ:
db. commitO;
db. closeQ;
CONCLUSIONI
Castor è un ottimo tool di persistenza dei dati.
Non ha la classe e le capacità di Hibernate ma è
senza dubbio più semplice da configurare e utiliz-
zare e i definitiva rappresenta un'ottima soluzione
per chi necessita di flessibilità senza voler installa-
re un tool dalla complessità elevata quale è Hiber-
nate. A fronte di un minimo di difficoltà iniziale si
hanno prestazioni elevate.
Massimiliano Bigatti
SUL WEB
IL PROGETTO
CASTOR
È disponibile
all'indirizzo
http://www.castor.org/,
da cui si può accedere
alla documentazione di
progetto, al Wiki,
forum ed al download
del codice e degli
esempi.
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 83 ►
ADVANCED EDITION T ■ Usare le wxWidgets
Codice C++ per
Windows e Linux
L'obiettivo di C++ fin dalla sua nascita è stato, tra gli altri, quello
di essere multipiattaforma. Oggi grazie a wxWidgets è davvero
possibile scrivere codice complesso che funzioni su tutti i sistemi
Ci CD □ WEB
zip
jn
REQUISITI
■ imi ' i —
Basidi
' I programmazione C++
Il punto è il seguente: programmare applica-
zioni facilmente portabili da un sistema ope-
rativo a un altro. È per questo che è nato C++,
è questo che ha fatto la fortuna di Java, ed è
ancora questo che si tenta di fare con Mono.
Scrivere applicazioni multipiattaforma significa
abbracciare l'intero mercato e non una nicchia
con la conseguenza di allargare il proprio baci-
no d'utenza in modo esponenziale.
In questo articolo abbiamo scelto di realizzare
un programma C++, assolvendo alle esigenze di
multipiattaforma tramite l'uso un MiddleWare
che si pone come ponte fra il sistema operativo e
le funzionalità di alto livello, come la gestione
della grafica, il file system, i servizi di rete. Tale
MiddleWare risponde al nome di WxWidgets. In
parole molto povere tutte le nostre chiamate e le
nostre routine di programmazione faranno
adesso riferimento a funzioni esposte da Wx-
Widgets. Sarà questa libreria ad interfacciarsi con
COME INIZIARE
E necessario avere
DevCPP, in
installato le
ambiente Linux
wxWidgets relative
potere utilizzare
al proprio ambiente
ovviamente gcc.
di programmazione.
Infine sempre in
Trovate ovviamente
ambiente Windows
tutto sul ed di
abbiamo preferito
ioProgrammo,
l'ottimo wxDevCpp
oppure sul sito
che consente di
http://www.wxwidgets
sviluppare applica-
.org/. Se siete in
zioni wxWidgets in
ambiente Linux è
modo del tutto
utile installare tutto
visuale, in ambiente
da uno dei reposito-
Linux ci siamo fatti
ry disponibili per la
aiutare da Anjuta
vostra distribuzio-
che dispone di
ne. Ancora, in
comodi wizard per
ambiente Windows
la realizzazione di
abbiamo utilizzato
applicazioni
il compilatore
wxWidgets.
v
J
il sistema e a trasformare tutto in modo tale che
le nostre applicazioni funzionino sia su Win-
dows sia su GTK+, Motif e Mac. Ovviamente bi-
sogna ricompilare l'applicazione per il sistema
corretto, e bisogna disporre della libreria instal-
lata sul sistema. Niente paura, è tutto OpenSour-
ce. In questo articolo utilizzeremo come stru-
mento di sviluppo DevCpp in una versione un
po' particolare che è già integrata con WxWid-
gets, perciò davvero non ci sono costi da soste-
nere per sviluppare utilizzando questa tecnica,
altro punto a suo favore.
PRIMI PASSI
IN WXWIDGETS
La struttura delle classi e delle applicazioni di
wxWidgets richiama molto quella di MFC.
Risulta, dunque, molto semplice effettuare il
porting di applicazioni scritte con il framework
di Visual c++ in wxWidgets. L'intera applicazione
viene gestita da un oggetto di tipo ^T ^fl La
classe wxApp è astratta e definisce due metodi
mfm HmSfi . di cui effettueremo l'override in
fase di implementazione.
Il contenuto del main.h della nostra prima ap-
plicazione assomiglierà a qualcosa del genere:
#include <wx/wxprec.h>
#include <wx/wx.h>
class myApp : public wxApp#
{
public:
-• bool OnlnitQ;
int OnExitQ;
};
Ovviamente la classe sarà implementata nel file
mairi. cpp. L'implementazione della classe
astratta wxApp inizia con la riga
* 84 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Usare le wxWidgets ■ T ADVANCED EDITION
IMPLEMENT_APP( myApp )
e segue con la definizione dei metodi
bool myApp: :OnInit()
{
• wxFrame *myWin = new
wxFrame(NULL,-l,
SetTopWindow(myWin);
"Hello wxWorld");
myWin->Show(TRUE);
return TRUE;
}
int myApp: :OnExit()
{
return 0;
}
wxBoxSizer* box =
new wxBoxSizer(wxHORIZONTAL);
La vista principale viene gestita dalla classe
^. Questa classe fornisce i metodi per la
gestione di una finestra, la cui dimensione e po-
sizione può essere gestita dall'utente. Inoltre la
finestra sarà dotata di bordi e di una barra del ti-
tolo. Compilando, il risultato è quello di ottene-
re una finestra di dialogo vuota che sulla caption
bar mostra la scritta "Hello World". Tanto per
prendere confidenza con la logica di wxWidgets,
aggiungiamo alla finestra una status bar e defi-
niamo un testo di stato. Il codice è il seguente:
#include "main.h"
IMPLEMENT_APP(myApp)
bool myApp ::OnInit()
{
wxFrame *myWin
new wxFrame(NULL, -1, "Hello
wxWorld");
myWin->CreateStatusBar();
myWin->SetStatusText("Hello World");
myWin->Show(TRUE);
SetTopWindow(myWin);
return TRUE;
}
Anche in questo caso il codice è abbastanza
esplicativo. La classe wxFrame ci mette a dispo-
sizione i metodi CreateStatusBar e SetStatus che
rispettivamente creano la barra di stato e vi ag-
giungono un testo.
QUALCOSA
DI PIÙ COMPLESSO
La nostra applicazione è ancora scarna. Andia-
mo ad aggiungere un po' di elementi.
Per prima cosa associamo al frame un Sizer poi
gli altri controlli. I Sizer sono particolari oggetti
che si occupano della disposizione dinamica dei
controlli e somigliano molto ai Layer di Java.
box->Add( new wxTextCtrl(myWin, ID_TEXTBOX ,
"Testo", wxPoint(0,0),wxSize(100,20) ),
0,wxALIGN_CENTER_VERTICAL | wxALL, 5);
box->Add( new wxButton(myWin, ID_BOTTONE,
"Bottone", wxPoint(0,0), wxSize(80,20)),
0,wxALIGN_CENTER_VERTICAL | wxALL,5);
my Win- >SetSizer(box) ;
Abbiamo definito un sizer orizzontale che occu-
pa l'intero spazio della finestra.
All'interno del sizer abbiamo aggiunto due con-
trolli uno di tipo TextBox, che abbiamo riempito
con un testo di prova: "testo", abbiamo poi ag-
giunto un bottone, la cui caption, con grande
fantasia, sarà "Bottone".
Il risultato del codice appena scritto è un frame
contenente un textbox e di seguito, sulla stessa
riga, un bottone distanziati tra loro di 5 pixel.
Ovviamente se provate a compilare il codice in
questione, non funzionerà. Manca la definizio-
ne degli ID delle risorse. L'abbiamo inserita in
un file myFrame.h che contiente, per ora:
#define ID_TEXTBOX 1001
#define ID_BOTTONE 1002
definiremo qui gli ID che utilizzeremo nel corso
dell'applicazione. Questi ID devono essere uni-
voci e avranno un ruolo fondamentale nella ge-
stione degli eventi.
QUALCHE CONTROLLO
ll\l PIÙ
Alla nostra applicazione mancano ancora un
una combobox e per finire una listbox. Il codice
che le implementa è il seguente:
wxString scelte []
{ "Ciao" , "Questa", "è" , "una",
"ComboBox" };
wxString scelteLista [] = { "Ciao" , "Questa", "è" ,
"una", "ListBox" };
box->Add( new wxComboBox(myWin , ID_COMBO,
M ",wxDefaultPosition, wxDefaultSize, 5, scelte,
wxCB_DROPDOWN ),1, wxEXPAND| wxALL,5);
box->Add( new wxListBox(myWin, ID_LISTA,
wxDefaultPosition, wxDefaultSize, 5, scelteLista,
wxLB_SINGLE ),1, wxEXPAND| wxALL,5);
Anche qui il tutto è abbastanza intuibile. Si deve
porre attenzione alla dichiarazione dei due array
che contengono le stringhe che dovranno riem-
pire la combobox e la listbox. I due array vengo-
I TUOI APPUNTI
Utilizza questo spazio per
le tue annotazioni
COMPILARE
IN AMBIENTE
LINUX
In ambiente linux è
ovviamente possibile
compilare a linea di
comando. Per quanto
riguarda i nostri esempi
è possibile compilare
con la seguente stringa.
Tenendo conto che i
caratteri prima e dpo
wx-config sono apici
rovesciati ottenibili
tramite la
combinazione di tasti
ALTGR+7
gcc "wx-config — cxxflags
-libs" main.cpp myFrame
.cpp -o main.o
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 85 ►
ADVANCED EDITION T ■ Usare le wxWidgets
no passati come parametro rispettivamente per
i controlli wxComboBox e wxListBox.
Ancora una volta poniamo l'accento sull'ID che
identifica il controllo e che viene definito con un
numero nel file di inclusione myFrame.h, sarà
indispensabile ricorrere a questo id nella gestio-
ne degli eventi. Perciò tenete sempre come rego-
la base che ogni controllo deve essere identifica-
to da un ID univoco.
MENU E
DEGLI STRUMENTI
Risulta molto semplice anche aggiungere i me-
nu e le toolbar. Vediamo subito un esempio sui
menu. Andiamo nel corpo del costruttore della
LEGGERE IL CONTENUTO
DEI CONTROLLI A RUNTIME
Per leggere il contenuto dei control-
li a runtime possiamo utilizzare gli
id a loro associati.
wxWindow *win = wxWindow ::
FindWindowById( ID_TEXTBOX );
wxTextCtrl *text =
wxDynamicCast(win, wxTextCtrl);
if ( text ){
wxMessageBox(
"Il Testo è il Seguente : \n" +
text->GetLabel() );
>
Ogni controllo è ereditato dalla
classe wxWindow. La funzione wx-
Window::FindWindowByld restitui-
sce un puntatore di tipo wxWindow
tramite l'utilissima macro per il cast
dinamico andiamo ad effettuare la
conversione in wxTextCtrl. Se il cast
è andato a buon fine leggiamo il te-
sto del textbox e lo inseriamo in un
finestra di messaggio. Gli ID sono le
targhe dei controlli è importante,
quindi, associarne uno, in modo
univoco, ad ogni oggetto della GUI.
classe myFrame vista in precedenza ed aggiun-
giamo il menu.
wxMenuBar * menu = new wxMenuBar();
wxMenu * esempio = new wxMenu();
esempio->Append(ID_MENU , "&Apri\tCtrl-A" ,
"Apri un File" );
esempio- >AppendSeparator();
esempio->Append( ID_ESCI , "&Esci\tEsc",
"Fine del Programma" );
menu->Append( esempio , "&File" );
myWin->SetMenuBar( menu );
La prima cosa da fare è creare una Menubar,
all'interno della quale creeremo i singoli me-
nu. Ogni elemento dei menu è caratterizzato
da un ID, un titolo ed una descrizione. l'ID vie-
ne utilizzato per identificare l'oggetto, il titolo
è il testo visualizzato mentre il testo della de-
scrizione appare sulla statusbar quando la vo-
ce del menu in questione è selezionata. Il tito-
lo della voce del menu può contenere una serie
di caratteri speciali. Il primo che incontriamo è
la &. Con questo carattere possiamo definire la
lettera selezionabile con la combinazione
Maiuscolo-Tasto.
Poi troviamo la sequenza \tTasto la quale ci
permette di definire un tasto di scelta rapida.
Ormai dovreste avere appreso perfettamente
come fare per aggiungere un toolbar. Sempre
nel corpo del costruttore andiamo ad inserire il
seguente codice:
IL MIO PRIMO PROGETTO CON WXDEVCPP
Per il nostro articolo in ambiente Windows abbiamo utilizzato l'ottimo wxDevCpp che ci consente di usare i controlli
esposti da wxWidgets in modo completamente visuale, esattamente come avremmo fatto da Visual Studio, ma ad un
costo decisamente inferiore
>SI INIZIA
@ & B H U
Descrizione
■■ ' . : ■ '
— Opzioni del Progetta:
Nome:
Progettai
0C ++
Lll i, laCCI n '' =-d-l 1 1 ■ )
| </0k | | X Annulla | | 7 Aiuto
I Una volta eseguito wxDevcpp sele-
zioniamo dal menu File la voce Nuo-
vo->Progetto. Dalla sezione basic sceglia-
mo di creare un progetto dal template "wxWid-
gets Dialogs" inseriamo un nome per il no-
stro lavoro e diamo l'ok! Siamo già a buon
punto.
> IMPOSTAZIONI INIZIALI
Class Name
File nanne
Save To
Title
Àuthor
Default Style
3ttoProva
ProgettoProva (Without Extension)
D:\Stefano || ... |
dgets Intro
Stefano Vefia
Use Caption □ Resize Border System Menu
□ T hick B order □ S tay n T op N o Parent
Min Button □ Man Button Dose Button
1 Create
Cancel
I II prossimo passaggio è definire il no-
Ime dei file e delle classi che compon-
gono il progetto di base. Una volta comple-
tate le procedure di configurazione siamo
pronti per iniziare a lavorare. DevCpp creerà
per noi uno scheletro di applicazione da cui
iniziare
> L'EDITOR VISUALE
m Dev-C++4.9.9.2-WH-beta-6/;
File Modifica Cerca Visualizza 1
m& □ y ^ * a
Progetto Classi Debug
+ JS Progetto!
wxDevcpp salva le informazioni sul
I layout grafico in file di estensione
wxform.
È sufficiente andare nel visualizzatore dei
file del progetto e selezionare i file con ta-
le estensione per accedere all'editor di dia-
loghi e lavorare in modo visuale.
* 86 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Usare le wxWidgets ■ T ADVANCED EDITION
wxToolBar * tool = myWin->CreateToolBar();
wxBitmap *bitmap = new wxBitmap() ;
bitmap-> LoadFile("bottone.bmp",
wxBITMAP_TYPE_BMP );
tool->AddTool(ID_ESCI, "Esci", *bitmap, "Fine del
Programma");
tool->Realize();
Per prima cosa andiamo a creare la toolbar poi
creiamo la bitmap da usare come immagine per
il bottone della toolbar. Una volta pronta la bit-
map andiamo a creare il pulsante della barra.
La ToolBar si comporta come la MenuBar, ovve-
ro gestisce lo stesso tipo di eventi.
Per questo motivo è possibile condividere gli ID
delle voci del menu con la barra delle utilità.
Con questo stratagemma è possibile eseguire
una data operazione sia dal menu che dalla
toolbar gestendo gli eventi una sola volta.
o e e
□
~A
Ciao
Gì està
GLI EVENTI
La gestione degli eventi in wxWidgets è allo stes-
so tempo semplice e potente.
Il primo passo sarà estendere le classi base di
wxWidgets ed aggiungervi le funzionalità neces-
sarie ai nostri scopi. Proprio per far fronte alle
nostre esigenze estendiamo la classe wxFrame
per poter ricevere le notifiche degli eventi.
class myFrame : public wxFrame
{
public:
myFrame( wxWindow
* parent,
int id, const
wxString &title);
void OnTextChange(
wxCommandEvent &event );
void OnPressButton(
wxCommandEvent &event );
private:
DECLARE_EVENT_TABLE()
};
Fig. 1: L'applicazione di esempio in esecuzione su
un'installazione di Ubuntu Linux
Ciò che abbiamo appena fatto è stato inserire le
due funzioni OnTextChange e OnPressButton le
quali accettano come parametro una variabile
di tipo wxCommandEvent. Queste funzioni si
comportano come dei callback.
La prima funzione viene richiamata quando il
testo in un box di testo cambia, la seconda viene
invocata nel momento in cui l'utente preme un
pulsante.
La macro DECLARE_EVENT_TABLE() racchiude
l'abilitazione alla ricezione degli eventi.
Occupiamoci ora dell'implementazione di my-
Frame. All'interno del file di implementazione è
necessario inserire la seguente lista di macro
GLOSSARIO
CROSS-
PLATFORM
Con il termine cross-
platform vengono
indicati i progetti che
possono essere
ricompilati sotto
piattaforme differenti.
Scrivere codice
completamente
portabile è molto
complicato poiché
ogni sistema operativo
mette a disposizione
strumenti per lo
sviluppo differenti
(Win32, gtk, kde, ...).
Anche la struttura e la
dimensione dei tipi di
base può cambiare tra
compilatori diversi .
wxWidgets ci viene
incontro mettendoci a
disposizione tutti gli
oggetti e gli strumenti
considerati "critici"
evitandoci fastidiosi
> DISEGNARE INTE RFACCIA
W I
I In questa modalità è possibile in-
Iserire nel dialogo tutti gli oggetti
messi a disposizione dal framework.
Nell'esempio in figura abbiamo aggiun-
go un bottone, un radiobutton una label
e un textbox, in pratica tutti i controlli ba-
se a disposizione.
> COSA POSSIAMO FARE? > L'EDITOR DI PROPRIETÀ
«. * m\ <*£ lj ^ l> m m *
aTitololFrm.cpp HSenzaT < ►
Controls
-
:::: m
A StaticTewt
-
: D :::: n :::: Q ::
foTl Butteri
1 ' ■
■ WxButtonl ^M
[ffj BitrmapButton
SETI Edit
^| Menno
[x CheckBon
I Tutti i controlli (e non solo) inseri-
I bili nei frame sono elencati nella bar-
ra a destra dell' IDE. Ovviamente stiamo par-
lando di un editor visuale che ci consente
di disegnare semplicemente posizionan-
do gli elementi sul frame che caratterizza
la finestra.
'" vR. l'(, l,,1 ■■!•.•£. (li ni.
Properties Events
Background Colo;
IP
I
m e
Ili
Foregrou
El General Styles
Height
HelpText
1
| ^_
)efauli
; !,:.:i-
. : .e rtp lato o ■ imi-» . .; og di : .orripilazione ';"' 'ebug
I È possibile modificare i parametri di
(ogni controllo selezionato attraver-
so l'utile editor di proprietà in stile Visual
Studio, lavorando sui diversi parametri ot-
terremo comportamenti anche sensibil-
mente differenti e senza avere messo ma-
no al codice.
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 87 ►
ADVANCED EDITION T ■ Usare le wxWidgets
////Event Table Start
{
SUL WEB
Il progetto wxWidgets
è disponibile
all'indirizzo
http://www.wxwidgets.org/
Tutte le informazioni su
wxDevCpp sono
reperibili al seguente
indirizzo.
http://wxdsgn .soli rcef orge
.net/
BEGIN_EVENT_TABLE(myFrame,wxFrame)
EVT_TEXT( ID_TEXTBOX , myFrame: :OnTextChange)
EVT_BUTTON(ID_BOTTONE, myFrame: :OnPressButton)
EVT_MENU(ID_MENU, myFrame: :OnApri)
wxPaintDC dc(this);
END_EVENT_TABLE()
////Event Table End
La macro BEGIN_EVENT_TABLE prepara una
lista di eventi e l'associa ad una classe. Nella
suddetta macro è necessario specificare, come
secondo parametro, la classe padre. Dopo è il
turno della dichiarazione degli eventi EVT_
TEXT,EVT_BUTTON. Il primo evento viene
generato ogniqualvolta il testo della textbox
con ID ID_TEXTBOX cambia. Il secondo è ge-
nerato dalla pressione del pulsante con ID ID_
BOTTONE. Gli eventi in questa fase vengono
associati alle funzioni di callback della classe
che stiamo implementando. Nel corpo delle
funzioni di callback possiamo andare a gestire
il carattere delle nostre applicazioni. Andiamo
a gestire ad esempio l'evento OnTextChange.
void myFrame: :OnTextChange(wxCommandEvent
&event )
{
SetLabel(event.GetString());
}
L'esempio è banale, leggiamo la stringa asso-
ciata alla variabile event e la utilizziamo per
cambiare il testo nella barra del titolo della no-
stra applicazione. Per un elenco completo dei
metodi della classe wxCommandEvent vi ri-
mando alla guida in linea di wxWidgets, tutta-
via wxCommandEvent non è l'unica tipologia
di evento gestibile.
• È possibile catturare la pressione dei tasti
attraverso gli eventi di tipo wxKeyEvent.
• Le voci dei menu generano eventi di tipo
wxNewEvent.
• Il mouse da origine a eventi di tipo
wxMouseEvent.
• Possiamo anche "ridisegnare", secondo il
nostro gusto, gli sfondi degli oggetti , quan-
do lo permettono, attraverso l'intercetta-
mento degli eventi di tipo wxPaintEvent.
Ecco un esempio: per prima cosa aggiungiamo
l'evento alla "Event Table"
EVT_PAINT( myFrame: :0n Paint)
poi lo implementiamo
void myFrame: :OnPaint(wxPaintEvent& event)
de. DrawRectangle(10, 10, 100,50);
dc.DrawText("Io Programmo",, 25 );
>
La dichiarazione di una variabile di tipo wx-
PaintDC all'inizio di un evento OnPaint è ob-
bligatoria. Essa contiene il device context nel
quale si può disegnare e scrivere.
DIALOGHI DI INPUT
Tra le centinaia di classi messe a disposizione
dal framework di cui ci stiamo occupando c'è
ne sono una serie dedicate all'input.
Le più interessanti ci sono il selettore di file, il
DirBrowser, e il dialogo per la scelta dei colori.
Andiamo subito a vedere il FileSelector
wxString filename
= wx
FileSelector("Scegli
il file");
if ( !filename.empty() )
{
wxMessageBox(
'Il file
è :" + filename );
}
semplice no? Da notare l'utilità dell'operatore
+ per la concatenazione delle stringhe.
Stessa sintassi per il selettore di directory.
L'unica cosa che cambia è il nome della fun-
zione. In questo caso dobbiamo utilizzare
wxDirSelector.
Per quanto riguarda la selezione di un colore
bisogna aggiungere qualche elemento. In
wxWidgets i colori sono codificati con il me-
todo RGB e gestiti tramite la classe wxColour.
wxColour colore ( 27, 127, 255 );
colore = wxGetColourFromUser(this,colore);
CONCLUSIONI
In questo articolo non siamo scesi nel detta-
glio dei parametri relativi ai singoli metodi,
questo tipo di informazione è facilmente repe-
ribile nella documentazione allegata alle
wxWidgets, abbiamo preferito invece dare uno
sguardo d'insieme alla logica di funzionamen-
to della libreria, fornendovi un tutorial che vi
offre tutte le informazioni necessarie per co-
struire da soli un'intera applicazione.
Le wxWidgets sono uno strumento piuttosto
potente e consentono di costruire codice real-
mente multipiattaforma, particolarità assolu-
tamente non trascurabile.
Stefano Vena
+ 88 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Visual Basic .NET
i
T CORSI BASE
Una foriti
per ogni finestra
Le Windows Form rappresentano la struttura di base per lo sviluppo
di applicazioni Microsoft Windows. Impariamo come utilizzarle,
di quali proprietà godono e gestirle senza problemi
All'interno di un progetto di tipo Applicazione
per Windows (abbiamo già visto come inVB
.NET sia possibile creare altri tipi di progetti,
ad esempio le Applicazioni Web), le Windows form
(finestre Windows) rappresentano l'oggetto di base
per l'interazione con l'utente. La corretta progetta-
zione del posizionamento dei controlli sul form sta-
bilisce il successo di un'interfaccia utente.
Le form sono degli oggetti e come tale espongono le
proprietà che ne definiscono l'aspetto, ed i metodi e
gli eventi che definiscono l'interazione con l'utente.
LA PRIMA FORM
Siamo ormai degli esperti nel creare nuovi progetti
di tipo Applicazione per Windows, ed ogni volta ab-
biamo osservato come VB .Net crea automatica-
mente una form per iniziare a disegnare l'interfaccia
utente. In concreto VB .NET crea una soluzione dal
nome assegnatogli, costituita da un progetto dallo
stesso nome, composto a sua volta da una finestra
vuota denominata Formi. VB. In automatico, inoltre,
mostrerà la finestra Progettazione Windows Form in
cui sarà visualizzata la finestra Formi. Nella finestra
Progettazione Windows Form, è possibile progettare
rapidamente e visivamente il disegno di una form
trascinando semplicemente i controlli, selezionati
n-.
.
hi :.::. :[ p OÌ
ni ir
nella Casella degli strumenti, nella form. L'ambiente
di progettazione di Visual Basic .Net è diventato, in
realtà, un potente generatore di codice. Quando si
disegna un controllo o si imposta una sua proprietà
(dalla finestra delle proprietà), vengono generate, in
automatico, le istruzioni che consentono di disegna-
re ed assegnare i valori delle proprietà. Tale codice
viene racchiuso in una regione "collapsed" in modo
che non sia possibile modificarlo per sbaglio. Per
verificare quanto abbiamo appena detto, possiamo
aprire l'editor di codice facendo doppio clic sulla
form (oppure nella finestra Esplora soluzioni, sele-
zionando Formi e scegliendo la voce Visualizza co-
dice) e cliccare sulla casellina con all'interno il segno
di più (+) a sinistra della casella con scritto Codice
generato da Progettazione Windows Form. Con que-
ste operazioni verrà visualizzato il codice che VB
.Net 2003 genera in automatico. Per i più curiosi,
consiglio di dare uno sguardo al codice, ma ricordia-
moci di non modificarlo.
Fi
.edifica Visuali
:= --oce;ìc Ge-ea Dc-lvc 1 : '-"'
ntì Fine*. 1
P-fe-
> Debug - rjp mass ™
• Stì
m%%,*
%
Pa
:-■- -3?e -y- .:.■:■ |--:.;e::?: y*\- Forml.vb* |
l
ì
|-*£ Formi
_J |H\ {Dichiarazioni)
3 P-Jblic Class Formi
—
h\ Codice 5 er.era
ndowsForr,!
3 Priva
End S
-End Class
:■■■- :■=:-. j:-=""
E-rS,:-.
[f d posino Then
Windows Form '
alizzazione dopo la chiamata a InitiafaeCompone , , .
■=E - :::o:.:--.: -E--
<
J
Output
Fig. 1: La finestra di progettazione
Fig. 2: La regione "collapsed" del codice
LE PROPRIETÀ
La proprietà tipica di una form è la proprietà Form-
BorderStyle che ci consente di definire l'aspetto del
bordo della finestra. I valori che può assumere sono:
• None - non viene visualizzato nessun bordo e
La proprietà Name
consente di definire il
nome con cui verrà
fatto riferimento alla
form nel progetto.
Ogni volta che viene
creata una nuova
form, VB per imposta-
zione predefinita,
assegna i nomi Formi,
Fornii e così via.
È consigliabile impo-
stare, tramite la pro-
prietà Name, un nome
più significativo.
REQUISITI
■ imi imi imi m
H5T1 Elementi Visual Basic
Visual Studio 2003
lE^^L^^
Tempo di realizzazione
Luglio-Agosto 2005/ 89 ►
CORSI BASE T
I
Visual Basic .NET
Microsoft Windows
gestisce il concetto di
forni secondaria. Se tra
due finestre esiste una
relazione di tipo padre-
figlio, la form seconda-
ria apparirà sempre da-
vanti a quella principa-
le, indipendentemente
da quale delle due sia
la form attiva. Per di-
chiarare che la finestra
chiamata appartiene
alla finestra chiamante
occorre invocare il me-
todo AddOwnedForm
Scegliendo la voce di
menu progetto/
Aggiungi Form
ereditato, si aggiunge
una Windows Form che
eredita da una classe
Form precedentemente
creata. Vedremo nei
prossimi articoli il con-
cetto di ereditarietà di
classi, ci basti sapere
che la creazione di
nuove finestre median-
te l'eredità da form di
base è un modo sem-
plice per duplicare ciò
che è stato creato sen-
za ripetere ogni volta
le stesse operazioni.
nessuna barra del titolo e la finestra non può es-
sere spostata, ridimensionata o ridotta ad icona.
Nelle applicazioni reali questo tipo di form non è
d'uso comune.
FixedSingle - usato per creare una form a di-
mensione fissa, viene visualizzata una casella
con il menu di controllo, i pulsanti di riduci ad
icona, ingrandisci e chiudi e la barra del titolo. La
finestra non può essere ridimensionata (trasci-
nando il bordo o l'angolo) ma può essere sposta-
ta, massimizzata oppure ridotta ad icona.
Fixed3D - è uguale allo stile FixedSingle eccetto
che la finestra appare con un effetto tridimen-
sionale intorno ai bordi. La finestra non può es-
sere ridimensionata ma può essere spostata,
massimizzata oppure ridotta ad icona.
Sizable - è il valore di default. Vengono visualiz-
zati tutti i pulsanti: di riduzione ad icona d'in-
grandimento e di chiusura, nonché la barra del
titolo e la casella di controllo.
FixedDialog - usato anch'esso per creare una
form a dimensione fissa, vengono visualizzati i
pulsanti di riduci ad icona, ingrandisci e chiudi e
la barra del titolo. La finestra non può essere ridi-
mensionata ma può essere spostata, massimiz-
zata oppure ridotta ad icona.
FixedToolWindow - visualizza la barra del titolo
ed il pulsante di chiusura. La finestra può essere
spostata ma non può essere ridimensionata o
ridotta ad icona.
CREAZIONE DI UNA NUOVA FORM
Nella maggior parte delle applicazioni non sarà sufficiente la sola finestra creata in automatico
da VB Net ma ci occorreranno certamente altre form. Impariamo come aggiungere ulteriori
form al progetto
> AGGIUNGIAMO UN ELEMENTO
I Per creare una nuova form in fase di proget-
Itazione, selezionare dal menu Progetto la vo-
ce Aggiungi Windows Form. Sarà visualizzata la finestra
di dialogo Aggiungi nuovo elemento.
> DIAMOGLI UN NOME
ilementi del progetto locale
Windows Form Classe Modulo
I I i
Classe Controllo Creazione
Corinponent utente guidata fio,,.
DataSet File XML Sdiema XP _
- applicazioni Windows
| NuovaFinestra|
:
j
I Nel campo Nome dobbiamo digitare il nome
Ideila finestra (non è necessario digitare l'e-
stensione di file .VB poiché viene aggiunta da Visual
Basic). Potremo chiamarla NuovaForm.
> SCEGLIAMO IL TEMPLATE
ento - ÀpplicazioneDi Esempio
getto locale
Form Classe Modulo
Classe Controllo Creazione
Corinponent utente guidata fb...
DataSet
<;. Oj
Schema XML
I Nella finestra di dialogo Aggiungi nuovo ele-
I mento, fra i vari template esistenti selezio-
niamo il modello Windows Form (nella parte destra
della maschera).
> SIAMO PRONTI
I Clicchiamo sul pulsante Apri. In questo modo
Isi aprirà la finestra di progettazione con la
nuova form pronta per essere usata. Non ci resta
che aggiungere il codice che deve gestirla.
^ 90 /Luglio-Agosto 2005
Visual Basic .NET
T CORSI BASE
• SizableToolWindow - visualizza la barra del tito -
lo ed il pulsante di chiusura. La finestra può es-
sere spostata e ridimensionata.
Dopo aver definito il bordo della finestra possiamo
definire l'aspetto della barra del titolo (sempre nei
limiti imposti dalla proprietà FormBorderStyle) .
Utilizzando la proprietà Text possiamo indicare la
stringa che dovrà essere visualizzata nella barra del
titolo della form. Modificando la proprietà Control-
Box possiamo decidere se far comparire i pulsanti di
controllo della form. Si può inoltre scegliere quali
pulsanti di controllo devono essere disabilitati, con
la proprietà MaximizeBox per il pulsante d'ingrandi-
mento e MinimizeBox per il pulsante di riduzione ad
icona. Se ambedue le proprietà sono settate a False i
pulsanti non vengono neppure mostrati a video. Tra
le altre proprietà possiamo utilizzare:
• Icon - La proprietà Icon permette di selezionare
l'icona che dovrà essere visualizzata nell'angolo
superiore sinistro quando viene mostrata la
form, oppure quando viene ridotta ad icona in
fase di esecuzione. Per inserire un'icona, dobbia-
mo visualizzare la finestra delle proprietà e clic-
care con il mouse sul pulsante (con i tre puntini)
che appare quando si seleziona la proprietà
Icon, così facendo verrà visualizzata la finestra di
dialogo Apri in cui selezionare il file corrispon-
dente all'icona desiderata.
• WindowState - La proprietà WindowState ci per-
mette di definire lo stato di visualizzazione della
form (ingrandita, ridotta ad icona o con dimen-
sioni normali) e può assumere tre valori.
• Normalper - visualizzare una form con le di-
mensioni normali (è il valore di default).
• Minimized - per visualizzare una form ridotta ad
icona.
• Maximized - per visualizzare una form a tutto
schermo.
• StartPosition - La proprietà StartPosition per-
mette di specificare la posizione che dovrà occu-
pare la form sullo schermo. Può assumere quat-
tro valori.
• Manual - non viene specificata nessuna posizio-
ne per la finestra.
• CenterParent - la finestra viene visualizzata al
centro rispetto alla relativa form padre.
• CenterScreen - la finestra viene visualizzata al
centro dello schermo.
• WindowsDefaultLocation - la finestra viene vi-
sualizzata nella posizione predefinita di Win-
dows, con le dimensioni indicate.
• WindowsDefaultBounds - la form viene visua-
lizzata nella posizione predefinita di Windows,
con le dimensioni determinate dai limiti delle
impostazioni predefinite di Windows.
• AutoScroll - Se una form contiene un numero di
controlli tali, da non rientrare nei limiti imposti
dalla risoluzione video, è possibile adottare due
soluzioni: utilizzare strutture a schede oppure
form scorrevoli.
Le strutture a schede rappresentano, da molto tem-
po, la modalità standard per raccogliere un gran nu-
mero di controlli in un'area ridotta dello schermo.
Le form scorrevoli possono essere invece utilizzate
in molte situazioni, in modo particolare oggi che gli
utenti sono abituati a scorrere su internet le lunghe
pagine HTML. In VB.NET è molto semplice imple-
mentare una finestra scorrevole, è infatti sufficiente
impostare a True la proprietà AutoScroll. Ogni volta
che l'utente ridimensiona la finestra, in modo da
rendere uno dei controlli parzialmente invisibili,
verrà visualizzata una barra di scorrimento orizzon-
tale o verticale (o entrambe) a seconda del caso.
Possiamo prendere confidenza con le proprietà ap-
pena descritte, modificandole nella finestra della
proprietà, per avere subito un riscontro visivo sul-
l'effetto che hanno sulla form.
GLI EVENTI
Analizziamo ora gli eventi di una form introducen-
doli nell'ordine in cui vengono generati lungo il ciclo
di vita.
New - L'evento New è il primo evento generato nella
vita di ogni form e determina lo stato di Creato, ma
non caricato. L'evento viene generato nel momento
in cui viene utilizzata la parola chiave New nel codi-
ce prima dell'invocazione del metodo Show (come
vedremo nel proseguo dell'articolo). Il codice di
quest'evento normalmente non è visibile poiché è
racchiuso nella regione collassata "Codice generato
da Progettazione Windows Form". In questa parte di
codice si possono inserire le istruzioni necessarie
all'inizializzazione delle variabili della form.
FORM DI AVVIO
I TUOI APPUNTI
Utilizza questo spazio per
le tue annotazioni
Quando si disegna un'applicazione
con diverse finestre si pone il
problema di definire la finestra di
avvio del programma. Per default
la finestra di avvio è la prima che si
è creata, per modificare l'ordine di
esecuzione si devono seguire i
seguenti passi:
1 Nella finestra Esplora soluzioni
fare clic con il pulsante destro
del mouse sul progetto e scegliere
Proprietà.
2 Viene visualizzata la finestra di
dialogo Pagine delle Proprietà
NomeProgetto (dove NomeProget-
to è il nome del progetto in uso)
alla proprietà Generale
3 Cliccare sull'elenco a discesa
Oggetto di avvio per
visualizzare l'elenco di tutte le
finestre del progetto
4 Selezionare dall'elenco la form
che si vuole venga visualizzata
per primo e cliccare sul tasto OK
La form di avvio possiede una
peculiarità, se viene chiusa,
vengono automaticamente chiuse
anche tutte le altre finestre e
l'applicazione termina.
Luglio-Agosto 2005/ 91 *
CORSI BASE T
I
Visual Basic .NET
Public Sub New()
MyBase.NewQ
'Chiamata richiesta da Progettazione Windows Form.
InitializeComponentQ
'Aggiungere le eventuali istruzioni di
inizializzazione dopo la chiamata a
'InitializeComponentQ
End Sub
È sconsigliato scrivere codice in questo evento (a
meno di non esserne costretti), ed usare piuttosto
l'evento successivo, l'evento Load.
Load - Dopo l'evento New viene generato l'evento
Load, in questa fase i controlli della form sono stati
tutti creati e caricati anche se la finestra non viene
ancora visualizzata. Si possono modificare e leggere
le proprietà dei controlli, ma si devono evitare azio-
ni che non possono essere eseguite su controlli invi-
sibili. In genere questo evento contiene il codice in
cui si inizializzano controlli e variabili.
Private Sub Forml_l_oad(ByVal sender As
System. Object, ByVal e As System. EventArgs)
Handles MyBase.Load
TextBoxl.Text = ""
End Sub
Paint - L'evento Paint viene generato immediata-
mente prima che la form diventi visibile ed ogni
volta che la finestra viene disegnata, ad esempio
quando viene ingrandita, ridotta ad icona o ripristi-
nata. Nell'evento Paint vengono di norma eseguite
operazioni di spostamento o di ridimensionamento
dei controlli in una form di cui sono state modifica-
te le dimensioni. VB .NET mette, comunque, a di-
sposizione degli strumenti per il ridimensionamen-
to automatico dei controlli.
Activated e Deactivate - L'evento Activated viene
La routine Initialize-
Component viene uti-
lizzata dall'ambiente di
sviluppo per conserva-
re i valori delle pro-
prietà impostati nella
finestra Progettazione
Windows Form.
In Visual Basic 6 queste
informazioni non era-
no salvate come codi-
ce, ma come istruzioni
in formato testo all'ini-
zio del file .FRM e non
venivano mai mostrate
nella finestra del
codice.
SALVARE UNA FORM
Dopo aver creato una nuova form è necessario salvarla form sull'hard disk.
Per questo possiamo utilizzare quattro diversi metodi
> DAL MENU FILE
> DALLA BARRA DEGLI STRUMENTI
Ble
1 Modifica Visualizza Pr
:t": iee :- re:.
:■!;
Strumenti Finestra ?
File
Modifìce Vìsuelizze Progetto Gene
, Debug Deti
i è £ & «
un
\ j ► Debug - ntf massima . a fi" Pfl » « - .
'a
- o-JS-G»!
» massima . Q US" S *
K | è £ §t ^ | g m | % 1 ,
i%.
A«zr~ te
CTOL-fMAIUSC+A
MAIUSC+ALT+A
lì
'
3 m 3 j ra,
ì
'ÉPnfflF^aaaaaaaHm
3i n j ig
_.:■ ,;- ^-^
i B Fonavo"
m EJ FormEsistente vb
- jP ApplicaiioneDiEserapio
j.; 5 :™ro.vb
S Formi. vb
É- E FormEsiste-:e..:.
ffludTlZl
Q Salva NuovaForm.vb
CTOL45
Salva NuovaForm.vb con no
CTRL-H-1AIUSC+S
n
Controllo del codice sorgenti
*
Stampa..,
C^+P
■^>= l# ? J
«
Esci
Qe...|^ ® a: éJ:
Proprietà
¥ X
|:::::::::::::::
:::::::::::::::
*™-.»
d
¥\ ti @| lei
\5*Z i"*!
-
Output
» X
: -
'
Wmmentn n„ 5 „n,l
'
I Ciccando sulla voce Salva NuovaForm.vb,
Idove NuovaForm è il nome della form sele-
zionata (CTRL+S).
> TUTTE INSIEME
J Per accedere rapidamente cliccando sull'i-
Icona Salva NuovaForm.vb dalla barra degli stru-
menti.
>m
i%.
■1
— — -
3 il n j a
^
- _,J -:::-: =: Eie :
+ ^?e'e-e-:es
3 Assemblylnfo.vb
! S Forml.vb
♦ B FormErétentevb
a
*:;i;:i:l,.
1 s* a
Controllo del codice sorgente ►
Be...|^,-... »s... 0:
I
1
Proprietà
E
Operazione di gene Compilano,
Spazio dei nomi dell
Ctóout ^ X
-il
> TUTTE INSIEME DALLA BARRA
= Modifica Ssuatzza Progetta Genera Debug Dati
Sfrurnenfi Fi Q e Sfr a Z
l'g^Hl * % 6| "^ ra T ,p T [
l y Debug . * maxima - Q È"
ogi Salvatiti Q
. %
Pagina iniziale | Forml.vb [Progettazione] | Formi. vd NuovaForm.vb [Progettazione] | 4 S> X
U.'.rl..l,..,B.'..Vl
D fflH!.^^^^BIIÌT§|
■
E H|IU|i^|0
QJ Soluzione "Applicazioi
É |jp ApplicazioneDi
É- dì References
LI Assemblylnft
SI Forml.vb
É- ai FormEsistent
| :::::::;
Be...|^v....|#s
|| :::::::: ! ! ! ! Ili
Proprietà
[7^1
"11 ti \\m\ m
JSe il progetto è composto da più form, sele-
zionando dal menu File la voce Salva Tutto
(CTRL+MAIUSC +S)
□ L'accesso rapido per salvare tutte le form insieme
cliccando sull'icona Salva Tutto dalla barra de-
gli strumenti.
► 92 /Luglio-Agosto 2005
Visual Basic .NET
T CORSI BASE
generato nel momento in cui una form diventa la
form attiva, subito dopo l'evento Paint L'evento
Activated può essere utilizzato quando si vogliono
ripristinare situazioni che possono essere state mo-
dificate da un'altra form quando la form non era at-
tiva. L'evento Deactivate viene generato quando
viene attivato un'altra form dell'applicazione, per
poi generare un altro evento Activated nel momen-
to in cui lo stato attivo ritorna alla form di partenza.
Closing - L'evento Closing si verifica alla chiusura
della form. L'evento può essere annullato, impo-
stando la proprietà Cancel, passato al gestore even-
ti, su true, in questo caso la finestra rimane aperta.
Questo evento viene usato per chiedere all'utente se
si vogliono salvare i dati eventualmente modificati
(per intenderci è quello che ci chiede Word ogni
volta che stiamo per chiudere il programma). Al ter-
mine dell'evento Closing (sempre se non si è impo-
stato Cancel-Trué) la form viene scaricata.
Closed - L'evento Closed si verifica quando la fine-
stra è chiusa e non è più visibile. È possibile utilizza-
re questo evento per eseguire operazioni quali il sal-
vataggio di informazioni immesse nella form o la
liberazione di risorse utilizzate dalla form. Infine
quando la form viene distrutta viene generato l'e-
vento Dispose.
I METODI
Per rendere visibile una form, dobbiamo utilizzare il
metodo Show, dopo aver creato un'istanza della
form stessa, utilizzando la classica parola chiave
New della programmazione ad oggetti. Il codice ne-
cessario a mostrare una finestra è il seguente:
Dim frm As New Forml()
frm.Show()
È possibile visualizzare una form in due modalità
• Modal - sono finestre a scelta obbligatoria che in
genere impongono all'utente una risposta al lo-
ro quesito prima di restituire il controllo all'ap-
plicazione.
• Modeless - sono le finestre standard che non
impongono nessuna scelta obbligatoria.
ELIMINARE UNA FORM DAL PROGETTO
Per eliminare una form dal
progetto possiamo utilizzare due
metodi diversi:
METODO 1
Possiamo selezionare la form che si
vuole escludere dal progetto e, dal
menu Progetto selezionare la voce
Escludi dal progetto.
Esplora Soluzioni sul form che si
vuole eliminare e selezionare
Escludi dal progetto
m-
fe-oS BS
- - """'
, & m.ssim, . SJ
s|nm|ii%.
SormlvbProg^™]
E: : '- ----- -
J
°Ti5
-, 3 =c E!
a Nuovi
.._ -_ -..
|ifl Mostra S-iffls
™L
I
• maBB g mw ^ l , w ^ mw F^
£°
33B 3 J ©
\aZ!Z2*
4
— a 8 ,,.,- ..^.„ =M
METODO 2
Possiamo cliccare con il tasto
destro del mouse nella finestra
In tutti e due i modi, una form
salvata in precedenza non viene
cancellata fisicamente dall'Hard
Disk. Per eliminare ogni traccia
della form si devono usare gli
strumenti di Windows e cancellare
fisicamente il file con il nome della
form, oppure si può selezionare la
voce Elimina nella finestra Esplora
Soluzioni
Quando viene visualizzato una form a scelta obbli-
gatoria, il codice successivo alla chiamata del
metodo Showdialog non viene eseguito fino a
quando la finestra visualizzata non viene chiusa, e
l'input da tastiera o dal mouse è valido solo per gli
oggetti della form. Viceversa nel caso di finestre
modeless il codice successivo alla chiamata del
metodo show viene eseguito progressivamente, La
visualizzazione della nuova form non interrompe,
quindi, il flusso di esecuzione del codice e l'utente
può passare da questa a qualsiasi altra form del-
l'applicazione. Per nascondere temporaneamente
una form si può utilizzare il metodo Hide che na-
sconde la form impostandone la proprietà Visibile
a False
frm.HideQ
Nascondendo una form, però, la finestra rimane
caricata in memoria. Per cancellare definitivamen-
te dalla memoria una form dobbiamo utilizzare il
metodo Close
frm.Close()
Per creare una nuova
form in fase di proget-
tazione, si può anche:
cliccare sull'icona corri-
spondente nella barra
degli strumenti.
Cliccare con il tasto de-
stro del mouse sul pro-
getto, nella finestra
Esplora soluzioni, sele-
zionare la voce Aggiun-
gi e poi Aggiungi Win-
dows Form.
Per visualizzare una finestra non modale si deve
usare il metodo Show (questo metodo non accetta
argomenti).
frm.Show()
Per visualizzare una finestra a scelta obbligatoria si
deve usare il metodo Showdialog.
frm.Showdialog()
CONCLUSIONI
Tramite l'impostazione delle proprietà della form,
il disegno dei controlli e la scrittura di codice VB per
la risposta agli eventi, è possibile creare interfacce
sempre più attraenti. Nei prossimi numeri analiz-
zeremo i vari tipi di form provando a dare una
risposta sul tipo giusto da usare in ogni occasione.
Luigi Buono
Luglio-Agosto 2005/ 93 ►
CORSI BASE T
I
ASP.NET
I web controIs
di ASP.NET
Usare i web control, senza conoscerne da vicino le funzionalità, può
essere un'operazione non priva di difficoltà. Con una guida come
questa, districarsi tra tutte le possibilità diventa semplice
G CD Q WEB
aspdotnet6.zip
^
jn
REQUISITI
f/3Jl HTML, ASP.NET
Microsoft.NET
Framework 1.0 o
successivi, ASP.NET
Tempo di realizzazione
Districarsi tra un numero elevato di nuovi
web controIs, per chi è alle prime armi con
ASENET, non è una pratica banale. I pro-
blemi cominciano non appena si passa, dalle prime
applicazioni create per sperimentare, a quelle ben
più complesse destinate ai sistemi di produzione.
Scegliere tra un vasto insieme di controIs può diso-
rientare anche gli utenti più smaliziati, per tale mo-
tivo cercheremo di darne una classificazione ordi-
nata.
GLI HTMLCOMTROLS
La prima distinzione che si fa all'interno dei web
controIs prevede la suddivisione in due grandi fami-
glie, che prendono il nome dalla parte finale del
namespace nel quale sono collocati. Gli HtmlCon-
trols, sono posizionati all'interno del namespace Sy-
stem.Web.UI.HtmlControls, ed i WebControls, che
sono collocati nel namespace System.Web.UI.Web-
Controls. Al primo gruppo appartengono, molto
semplicemente, tutti i tag HTML a cui viene aggiun-
ta la proprietà runat- "server". Possiamo dire, banal-
mente, che è il sistema che ASENET li usa per map-
pare gli oggetti della pagina sulle istanze di classi che
poi andrà a creare, in fase di compilazione. Ovvia-
mente esistono diverse classi che prendono il nome
dalla tipologia di tag HTML a cui si riferiscono, tra
cui HtmlAnchor, che rappresenta il tag <a />, Html-
Table che rappresenta il tag <table /> e così via. Un
esempio classico di uso degli HtmlControls è il se-
guente:
</script>
<body>
<form runat="server">
<a ID="AnchorButton"
OnServerClick="AnchorBtn_Click"
runat="server">
Click here
</a>
<hl>
<span id = "Message" runat="server"/>
</hl>
</form>
</body>
</html>
Tutti i controlli marcati come Runat-Server sono
identificabili come HtmlControls. Gli HtmlControls
godono spesso di particolari proprietà e metodi uti-
lizzabili via codice. Per tutti gli altri tag, che non pre-
vedono funzioni particolari o non sono utilizzati di
frequente, ASENET prevede una classe HtmlGene-
ricControl che consente di accedere a questi ultimi
in maniera semplice, ma senza avere accesso alle ri-
spettive proprietà HTML, come invece si può fare
nel caso degli HtmlControls mappati direttamente.
Eccone un esempio di utilizzo:
<a id = "link" runat="server" />
Nel codice, per impostare le proprietà di questo ele-
mento, faremo riferimento, semplicemente, a quelle
che il tag HTML corrispondente offre abitualmente
nella creazione di pagine HTML, ad esempio:
<%@ Page Language="VB" AutoEventWireup="True" %> link. Href=" http: //www. aspitalia.com";
<html>
<script runat="server">
Sub AnchorBtn_Click(sender As Object, e As
EventArgs)
Message.InnerHtml = "Hello World!!"
End Sub
Nnk.InnerText = "ASPItalia.com";
Questo approccio consente di mantenere le cono-
scenze acquisite in anni di utilizzo di HTML, poten-
do al tempo stesso sfruttare le caratteristiche di ASP
.NET, che rendono possibile programmare lato ser-
^ 94 /Luglio-Agosto 2005
ASP.NET
T CORSI BASE
ver i controls presenti sulle pagine, trattandoli come
oggetti. È dunque spesso utilizzato da chi comincia
ad utilizzare ASRNET, perché è di sicuro più sempli-
ce da apprendere.
I WEBCONTROLS
A differenza degli HTMLControls i WebControls sono
controlli complessi che non fanno direttamente rife-
rimento ai tag html classici. Ad esempio:
< mytree : treeview runat= "server" >
<mytree:treenode Text= "Michigan" >
< mytree :treenode Text=" Detroit" />
< mytree :treenode Text="Farmington" />
</mytree:treenode>
< mytree :treenode Text="Washington" >
<mytree:treenode Text="Bellevue" />
<mytree:treenode Text="Redmond" />
</mytree:treenode>
</mytree:treeview>
Questo Controllo disegna un albero, o una treeview,
cosa che risulterebbe impossibile o molto comples-
sa utilizzando i soli HTMLControls. Inoltre i Web-
Controls essendo nati in modo specifico all'interno
di un ambiente ad oggetti, a differenza dei loro pre-
decessori, godono di alcune caratteristiche interes-
santi, come ad esempio: "la coerenza dei nomi dei
membri delle classi all'interno di un insieme omoge-
neo per funzionalità di controls". Ad esempio potre-
mo stare tranquilli che la proprietà Text all'interno
dei WebControls sarà sempre utilizzata per modifi-
care il contenuto del testo di un oggetto che ne fa
uso. E che questa proprietà si chiamerà sempre
"Text" non per esempio "Label", "Content", o altro. Il
primo vero obiettivo dei WebControls è dunque
quello di fornire, a chi li utilizza, un insieme di pro-
prietà simili su tutta la famiglia. Un esempio di
quanto detto è il seguente
<asp:label id
= "testo" runat= "server" />
<asp:button id = "pulsante" runat=
'server'
/>
testo. Text = '
Questo è il testo";
pulsante. Text
= "Questo è il testo"
;
Anche con controlli che offrono funzionalità dav-
vero differenti tra di loro, come in questo caso, il
modello ad oggetti condiviso permette di man-
tenere inalterato il modo di operare. Di particolare
interesse, perché consentono di racchiudere al pro-
prio interno altri controls, sono i controlli Panel e
Placeholder. Concettualmente sono identici, ma
Panel aggiunge un "blocco" interno ai tag, che nel
caso di browser uplevel viene convertito in un tag
<div />, negli altri casi una <table />, mentre
Placeholder si limita ad includere al proprio interno
i controls. Entrambi sono utili quando si vogliono
nascondere pezzi della pagina, sfruttando la pro-
prietà Visible, ad esempio nel caso in cui la pagina
sia composta da un wizard che consente di costrui-
re passo -passo il risultato finale.
RICH CONTROLS
E DATA CONTROLS
Del secondo gruppo abbiamo già parlato a suffi-
cienza nelle precedenti puntate di questa serie, trat-
tando l'accesso ai dati con ADO.NET. A questa fami-
glia appartengono i già noti DataGrid, DataList e
Repeater ed un'ulteriore sottofamiglia di controls, i
List Controls. Di questo gruppo fanno parte Drop-
DownList, ListBox, CheckBoxList e RadioButtonList.
Come il nome suggerisce, si tratta di controls che
permettono di implementare semplicemente liste di
opzioni, rispettivamente attraverso un elenco a scel-
ta, uno a scelta multipla, un elenco di checkbox ed
uno di radio button. Tutti questi controls possono ri-
cevere la lista dei propri valori da un datasource,
proprio come un Data Control, piuttosto che attra-
verso la definizione di sottocontrolli, chiamati List-
Item. Vediamo un esempio che ci permette di capire
quanto il modello unificato sia vantaggioso.
Creiamo un semplice elenco, che attraverso una
DropDownList mostri una serie di colori da poter
visualizzare, con il colore selezionato dall'utente
mostrato attraverso una Label:
File Modifica Visu sriti Strumenti ? Collegamenti ASPItalia.com
_,j " ff= : E?] | Indirizzo \g\ http://localhost/ioprogrammo/
Seleziona un valore: | Rosso ^J
Hai selezionai o: Pio ss e
Fig. 1: Cliccando sulla combobox viene modificato il
contenuto dell messaggio
<form runat="server">
<p>Seleziona un valore:
<asp:dropdownlist id = "data" runat="server"
autoPostBack="true">
<asp:listitem>Rosso</asp:listitem>
<asp:listitem>Nero</asp:listitem>
<asp:listitem>Verde</asp:listitem>
</asp:dropdownlist>
</p>
<p>Hai selezionato:
<asp: label id = "testo" runat="server" />
</p>
</form>
I TUOI APPUNTI
Utilizza questo spazio per
le tue annotazioni
<SCRIPT RUNAT="SERVER">
void Page_Load() {
Luglio-Agosto 2005/ 95 ►
CORSI BASE T
I
ASP.NET
if (Page.IsPostBack) {
testo.Text = data.SelectedValue;} }
</SCRIPT>
Il risultato è visibile nell'immagine sottostante e mo-
stra il nostro elenco di opzioni con la relativa sele-
zione da parte dell'utente. Se vogliamo cambiare il
List Control, ci basta sostuire le occorrenze di Drop-
DownList con il rispettivo control da utilizzare. Nel
nostro caso optiamo per un elenco di radio button,
attraverso RadioButtonList. L'effetto che si ottiene è
mostrato in Figura 2; quello che abbiamo fatto è
stato semplicemente modificare la pagina nella
definizione del solo controls. Né il codice, né tanto
meno l'elenco dei valori sono stati modificati. Ai rich
controls, infine, appartiene un insieme di oggetti
che, mediamente, aggiungono funzionalità com-
plesse. Il più famoso di tutti è sicuramente Calendar,
che aggiunge un calendario completo semplice-
mente inserendo il codice <asp:calendar runat-
11 server 11 /> nella pagina. Gli altri due che mancano
all'appello sono AdRotator, che permette di far ruo-
tare banner, e Xml, che invece consente di mostrare
facilmente nella pagina il frutto di trasformazioni
XSLT.
File Mo ilizza Preferiti Strumenti
*
Collegamenti
ie] A5PItalia, corri u
G-0-BIslf8l|P3lSI Indirizzc
la
http://localhost/ioprogrammo/06/tes
Seleziona un valore:
r Rosso
^Nero
C Verde
Hai selezionato: Nero
Fig. 2: Per ottenere un layout completamente diverso
è stato sufficiente sostituire II controls combobox
con uno di tipo RadioButtonList
I VALIDATOR CONTROLS
Uniti dal fatto di ereditare tutti dalla classe BaseVa-
lidator, i Validator Controls sono accomunati ov-
viamente per le funzionalità che espongono, ovvero
dalla possibilità di convalidare l'input dell'utente
all'interno di una web form sfruttando un modello
ad oggetti condiviso. Ogni validator espone comun-
que differenze dovute alla diversa tipologia di vali-
dazione che rende disponibile all'utente, con un
buon nucleo delle funzionalità esposte condivise
all'interno della famiglia:
• ControlToValidate: indica il nome del control
sul quale applicare la validazione;
• Display: indica la modalità in cui il messaggio di
errore deve essere visualizzato.
• None: non visualizza l' errore;
• Static: visualizza l'errore, occupando subito
lo spazio necessario nella pagina;
• Dynamic: visualizza l'errore, ma lo spazio
nella pagina viene occupato nel caso debba
essere mostrato;
• EnableClientScript: attiva il controllo lato client;
• Enabled: attiva la convalida;
• ErrorMessage: il messaggio da visualizzare in un
ValidationSummary;
• IsValid: contiene un boolean con il risultato
della validazione;
• Text: specifica il testo da visualizzare in caso di
errore.
Ovviamente ci sono diversi tipi di validazione e per
ciascun tipo è presente un control dedicato. Nei casi
in cui non sia possibile sfruttare i controlli già pre-
senti, se ne possono costruire di propri, sfruttando il
control CustomValidator. Da segnalare, infine, che a
tutti i validator manca la possibilità di controllare
che l'input sia presente, dunque se utilizzate, ad
esempio, un RangeValidator è necessario associare
al campo sul quale deve essere fatta la convalida an-
che un RequiredFieldValidator. Perché la convalida
possa funzionare, bisogna associare, al validator il
control su cui effetuare la validazione attraverso la
proprietà ControlToValidate. È da sottolineare il fat-
to che la convalida funziona sia lato client che lato
server, ma solo ed esclusivamente, nella versione
1.1, con Internet Explorer 6.0 o successivi. Questa
limitazione è dovuta al fatto che le definizioni dei
browser di ASRNET l.x non sono aggiornate e che,
in fase di progettazione, è stata fatta la scelta di uti-
lizzare codice Javascript non compatibile con il
DOM, ma sono con IE. Fortunatamente questo limi-
te non ci sarà più con la versione 2.0, in arrivo per
fine anno. In tutti gli altri casi (ad esempio usando
FireFox) la convalida avviene solo lato server, garan-
tendo che comunque l'input inserito dall'utente sia
conforme a quanto specificato in fase di creazione
dell'applicazione.
UN ESEMPIO PRATICO
Per apprezzare al meglio l'utilità dei Validator
Controls, costruiamo con pochi semplici passaggi
una form tipo per la registrazione di un utente nel
database:
1 Partiamo con la defizione di un campo nel quale
inserire il nome dell'utente, associando un
RequiredFieldValidator Qer essere sicuri che l'utente
non dimentichi di inserire il nome:
Inserisci il tuo nome: <asp:textbox id = "nome"
runat="server"/>
<asp: RequiredFieldValidator runat="server"
ControlToValidate="nome"
y 96 /Luglio-Agosto 2005
ASP.NET
T CORSI BASE
ErrorMessage="* devi specificare il tuo nome"
Display="static"/xbr />
2 Continuiamo con la definizione di due campi
per l'inserimento dell'indirizzo e-mail, in modo
da evitare che l'utente, per sbaglio, non ne specifichi
uno errato. In questo caso utilizziamo un
CompareValidator tra i due controlli, specificando
come valore dell'attributo Operator la stringa
"Equal":
Il tuo indirizzo e-mail: <asp:textbox id = "emaill"
runat="server"/>
<asp:RequiredFieldValidator runat= "server"
ControlToValidate= "email 1"
ErrorMessage="* devi specificare il tuo indirizzo"
Display="static"/><br />
Ripeti il tuo indirizzo email: <asp:textbox
id = "email2" runat="server"/xbr />
<asp: CompareValidator runat="server"
Controllo Validate= "email 1"
ControlToCompare="email2"
Operator="Equal"
ErrorMessage="* i valori inseriti devono combaciare"
Display="dynamic" /xbr />
3 Proseguiamo quindi con la definizione di un
campo nel quale inserire una data (ad esempio,
di nascita) e sfruttando il RangeValidator, andiamo a
verificare che sia superiore al primo gennaio 1900:
Inserisci una data successiva al 1900:
<asp:textbox id = "data" runat="server"/>
<asp: RangeValidator runat="server"
ControlToValidate="data"
MaximumValue=" 1/1/2900"
MinimumValue=" 1/1/1900"
Type="Date"
ErrorMessage="* La data deve essere successiva
al 01/01/1900" Display="static" /><br/>
4 Infine inseriamo un ultimo campo per la pas-
sword e sfruttando il RegularExpressionValida-
tor, permettiamo l'inserimento di caratteri alfanu-
merici (minimo 1, massimo 10):
Inserisci la password (solo caratteri alfanumerici,
max 10 caratteri):
<asp:textbox id = "password" runat="server"/>
<asp:RequiredFieldValidator runat="server"
ControlToValidate="password"
ErrorMessage="* devi inserire un valore"
Display="dynamic"/>
<asp:RegularExpressionValidator id="valtextbol_req"
runat="server"
ControlToValidate="password"
ValidationExpression = "\w{l,10>"
ErrorMessage="* massimo 10 caratteri alfanumerici"
display="dynamic" /xbr />
5 A questo punto aggiungiamo un pulsante sulla
pagina, alla cui pressione andiamo a verificare
che Page.IsValid sia vero.
Sub ValidaForm(sender As Object,
e As
System. EventArgs)
IbIText.Text = String.Empty
if Page.IsValid then
IbIText.Text = "Pagina valida!"
end if
End Sub
È sempre importante verificare questa proprietà
lato server, perché nel caso in cui la convalida lato
client fosse disattivata, è il vero segnale che il con-
trollo è andato a buon fine.
CONCLUSIONI
Scegliere nel modo più appropriato il control da uti-
lizzare è un punto molto importante nello sviluppo
di applicazioni web basate su ASRNET, perché una
scelta errata può portare a perdere molto più tempo
di quanto una scelta consapevole possa invece aiu-
tare. Mettendo in pratica le semplici istruzioni di
questo articolo scegliere il control più adatto alle
proprie esigenze diventa un compito più agevole da
sopportare. Aggiungere controlli di validazione:
passo-passo
Daniele Bochicchio
Daniele Bochicchio è il
content manager di
ASPItalia.com,
community che si
occupa di ASP.NET,
Classic ASP e Windows
Server System.
Il suo lavoro è
principalmente di
consulenza e
formazione, specie su
ASP.NET, e scrive per
diverse riviste e siti.
È Microsoft ASP.NET
MVP, un
riconoscimento per il
suo impegno a
supporto delle
community e per
l'esperienza maturata
negli anni.
Il suo blog è
all'indirizzo
http://blogs.aspitalia.com/
daniele/
Nome
Codice
Descrizione
Button
<asp: Button ID="bottonel" text="Cliccami" CommandName=
"Funzione" CommandArgument= "Argomenti" runat="server"/>
Aggiunge un pulsante (<input type="button">) alla cui
pressione viene invocato un evento server side.
CheckBox
<asp:CheckBox ID="chkl"text= "Selezionami" checked=
"true"runat= "server" />
Inserisce una checkbox.
Hyperlink
<asp:Hyperl_ink ID="linkl" Text ="Clicca su questo link"
NavigateUrl="http://www.aspitalia.com" runat="server" />
Inserisce un link con una descrizione presa dalla proprietà
Text, che punta all' URL specificato in NavigateUrl.
Image
<asp:Image ID="imgl"ImageUrl ="img.gif"ToolTip="Testo
del ralt"width="80"height="30"runat= "server" />
Aggiunge un'immagine alla pagina, con <img src="path" />
ImageButton
<asp:ImageButton ID="imgbtnl"ImageUrl ="img.gif"ToolTip=
"Testo dell'alt"runat="server" />
Aggiunge un pulsante con un'immagine cliccabile.
Produce <input type= "image" />
Label
<asp: Label ID="lbll"Text=" testo del controllo" CssClass=
"classe" runat= "server" />
Aggiunge alla pagina il testo specificatonella proprietà
Text.
^Tabella 1: Principali Webcontrol e la loro descrizione j
Luglio-Agosto 2005/ 97 ►
CORSI BASE T
I
Javascript
Usare gli Array
alle basi del codice!
Diremo qualcosa su una delle strutture dati che costituisce le
fondamenta di ogni linguaggio di programmazione, impareremo come
utilizzarla all'interno di JavaScript e vedremo cosa il linguaggio ci offre
\~rn\ Basi di Javascript
EsìE^a.
Tempo di realizzazione
La logica che sta alla base degli array è
molto semplice. Si tratta di collezioni di
variabili.
L'immagine classica di un Array è quella di un
treno, dove tutte le carrozze sono numerate. Il
contenuto di ciascuna carrozza è accessibile
con una sintassi del tipo treno [1], treno [2] etc...
È anche vero che riferendoci all'insieme dei
treni, potremmo scrivere qualcosa del tipo
treno [1][1], treno [1] [2], treno[2][l],treno[2][2],
per riferirci rispettivamente alla prima e secon-
da carrozza del treno uno e la prima e la secon-
da carrozza del treno due. In sostanza un array è
una struttura formata da un insieme di elemen-
ti, reperibili attraverso uno o più indici. Il
numero di indici permette di definire la dimen-
sione dell' array.
Il numero di elementi dell' array permette di de-
finire la lunghezza dello stesso. In Javascript un
array viene generato creando una istanza della
classe ArrayO nel seguente modo:
COME INIZIARE
Per provare gli script proposti nel-
l'articolo non avete bisogno di
molti strumenti. Prima di tutto vi
occorre un editor di testo, il note-
pad andrà benissimo. Se volete
avere invece qualche comodità
potete usare un editor evoluto,
come ad esempio DreamWeaver, o
simili. Chiaramente i file html che
andremo a generare devono esse-
re salvati in una directory sotto un
web server per potere essere ese-
guiti. Potere installare Apache in
locale prendendolo dal ed allegato
alla rivista, oppure andrà benissi-
mo MS se siete in ambiente
Windows, oppure potete provare
gli script presso il vostro provider
di hosting. Di seguito riportiamo
lo scheletro di uno script
JavaScript, potete tranquillamente
utilizzarlo come base per il testing
delle vostre pagine HTML, dovete
semplicemente creare un file di
testo con queste istruzioni dentro
e salvarlo come nomefile.html in
una directory del vostro Web
Server
<hea@d>
<script type="text/javascript">
</script>
</head>
<body>
</body>
</html>
</come iniziare>
<script type
= "text/javascr
pt">
var Frutta =
new Array(3);
Frutta[0] =
'Mela";
Fruttati] =
'Pera";
Frutta[2] =
'Banana"
</script>
Si noti che gli indici degli array iniziano la
numerazione a partire da zero. Inoltre, nella
dichiarazione della dimensione di un array si
usano le parentesi tonde, mentre invece nella
valorizzazione si usano le parentesi quadre.
Occhio alle parentesi, è un errore comune
scambiarle !
Per un Array, è definita la sola proprietà: length
che restituisce il numero di elementi di cui è
composto l'Array. Un esempio d'utilizzo della
proprietà è il seguente:
f o r( i = ; i < = Frutta, length- l;i++)
{
document.write("Fruttat" + i +"] =
' + Fruttati] +
"<br>");
}
L'output dello script è la stampa di tutti gli ele-
menti dell' array. Si noti che nel ciclo, si è consi-
derato l'indice i che parte da zero, quindi per
poter essere sicuri di ciclare su tutti gli elemen-
ti dell' array occorre proseguire sino ad Frutta
.length- 1, e non sino a Frutta.length. Anche que-
sto è un errore comune... La classe Array in Ja-
vascript mette a disposizione alcuni metodi per
la loro gestione, in particolare quelli elencati in
Tabella 1.
Tanto per chiarire, facciamo qualche esempio
d'uso dei vari metodi:
var Frutta = new Array("Mela", "Pera", "Limone");
^ 98 /Luglio-Agosto 2005
Javascript
T CORSI BASE
document.write("Frutta. toStringQ
+ Frutta. toStringQ);
// Restituisce la stringa: Mela, Pera, Limone
Nella dichiarazione dell' array, si è utilizzato un
metodo alternativo che permette sia la dichiara-
zione che la valorizzazione dell 'array in un
passo unico. Il metodo toStringQ restituisce
semplicemente gli elementi dell' array concate-
nati dalla virgola.
document.write("Frutta.concat(\" Un po'di frutta
\",\"Italiana!\") = " + Frutta. concat(" Un po'di
frutta", "Italiana!"));
//Restituisce la stringa: Mela, Pera, Limone,
Un po' di frutta, Italiana!
Il metodo concat(xl,x2,..) concatena agli ele-
menti dell' array gli elementi che vengono pas-
sati come input al metodo. Tale metodo è utile
nel caso di due array. Ad esempio:
var Frutta = new Array("Mela", "Pera", "Limone");
var FruttaTropicale
new Array("Jambulo", "Cocco",
"Mango");
TuttaFrutta = Frutta. concat(FruttaTropicale);
document.write("TuttaFrutta.toString() = "
+ TuttaFrutta.toStringQ);
// Restituisce la stringa: Mela, Pera, Limone, Jambulo,
Cocco, Mango.
Nella terza riga del codice, usando coricato con
due array, si è creato un nuovo array TuttaFrut-
ta() composto dagli elementi dell' array Fruttai)
e dell' array FruttaTropicale (). Si noti che ne l' ar-
ray Fruttai) ne l' array FruttaTropicale () sono
stati modificati.
var Frutta = new Array("Mela", '
Pera", "Limone");
strFrutta_01 = Frutta.join(" ***
")
document.write("strFrutta = " +
strFrutta);
// Restituisce Mela *** Pera ***
Limone
strFrutta_02 = Frutta.join()
document.write("strFrutta = " +
strFrutta);
// Restituisce Mela, Pera, Limone
Il metodo joinO viene utilizzato per riunire in
una stringa gli elementi di un array. Crea una
stringa di caratteri composta dagli elementi del-
l' array separati dal carattere che è passato in in-
put al metodo.
Nel caso non si passino caratteri, per default è
utilizzato il carattere virgola (e funziona come
toStringO). È l'opposto del metodo splitQ che
analizzeremo parlando della classe Stringo.
var Frutta = new Array("Mela", "Pera", "Limone");
document.write("Frutta.pop() = " + Frutta. pop());
(#
Metodo
Descrizione
1
toStringQ
Restituisce una stringa composta dagli elementi dell' array
2
concat(xl,x2,..)
Concatena gli elementi xl, x2,.. con l' array cui è applicato il
metodo. Tipicamente xl,x2,.. sono array
3
join (chrsepamtore)
Converte in stringa gli elementi dell' array e li separa con il
carattere chrseparatore, che è facoltativo.
4
popO
Rimuove l'ultimo elemento dell'array e lo restituisce come
output del metodo
5
push(xl,x2,.J
Aggiunge xl, x2,.. in coda all' array nell'ordine in cui essi
compaiono
6
reverseQ
Ribalta l'ordine con cui sono sistemati gli elementi dell'array
7
shiftO
È la funzione duale di popQ. Rimuove il primo elemento
dell'array e lo restituisce come output del metodo.
8
unshift(xl,x2,..)
E la funzione duale di pushQ. Aggiunge xl, x2,.. in testa
all' array nell'ordine in cui essi compaiono
9
slice (start,end)
Restituisce un array con gli elementi compresi tra start
(compreso) ed end (quest'ultimo non incluso)
10
sort(comparefn)
Esegue l'ordinamento degli elementi dell'array utilizzando
come criterio di confronto la funzione comparefn in input.
11
splice(start,
deleteCount,xl,x2,..)
Rimuove tanti elementi quanti specificati dal parametro
deleteCount a partire dall'elemento start compreso, e li
sostituisce con gli elementi xl,x2,.. se presenti, nell'ordine in
cui compaiono.
12
valueOfO
Ritorna il valore primitivo dell'oggetto Array
^ Tabella h Un elenco del metodi per la gestione degli array A
Il Restitituisce: Limone
for(i=0;i< = Frutta. Iength-l;i++)
{
document.write("Frutta[" + i +"] =
" + Frutta [i] +
"<br>");
}
// Stampa solo i due elementi "Mela"
2 "Pera".
Il metodo pop() elimina l'ultimo elemento dal-
l' array e lo restituisce come output. Il risultato è
quindi che l' array FruttaO, dopo che ad esso è
stato applicato il metodo popO, si trova senza
l'elemento "Limone".
var Frutta = new Array("Mela", "Pera", "Limone");
Frutta. push("Fragola", "Albicocca");
for(i=0;i< = Frutta. Iength-l;i++)
{
document.write("Frutta[" + i +"] = " + Frutta[i] +
"<br>");
}
Il metodo pushQ aggiunge in coda all' array le
stringhe che riceve in input. In pratica, adesso
l' array FruttaO contiene due elementi in più che
sono "Fragola" ed "Albicocca". Restituisce la lun-
ghezza dell'array nel nostro caso Frutta.pushC
"Fragola", "Albicocca") restituisce 5. Attenzione a
non utilizzare pushQ per concatenare due array;
non funziona. Un codice del tipo:
var Frutta = new Array("Mela", "Pera",
"Li
mone");
var FruttaTropicale = new Array("Jambulo
', "Cocco",
"Mango");
Frutta. push(FruttaTropicale);
for(i=0;i< = Frutta. Iength-l;i++)
{
document.write("Frutta[" + i +"] =
' +
Fruttati] +
Luglio-Agosto 2005/ 99
CORSI BASE T
I
Javascript
"<br>");
I TUOI APPUNTI
Utilizza questo spazio per
le tue annotazioni
Restituisce quanto segue:
Frutta[0] = Mela
Fruttati] = Pera
Fruttat2] = Limone
Fruttat3] = Jambulo, Cocco, Mango;
In pratica, Javascript applica per default il meto-
do toStringO all'array che viene passato come
argomento a pushQ. Quindi il codice Frutta
.push (FruttaTropicale) equivale a Frutta.pushi
FruttaTropicale. toStringO);
var Frutta = new Array("Mela", "Pera", "Limone");
Frutta. reverse();
for(i=0;i< = Frutta. Iength-l;i++) {
document.write("Fruttat" + i +"]
" + Fruttati] +
"<br>");
}// Ritorna un array i cui elementi sono nell'ordine:
Limone, Pera, Mela
Il metodo reverseQ non fa altro che ribaltare l'or-
dine degli elementi dell 'array. Il primo elemen-
to diventa l'ultimo, l'ultimo il primo e via di
seguito con quelli intermedi.
var Frutta = new Array("Mela", "Pera
", "Limone");
var strShift = Frutta. shift();
document.write("Frutta.shift() = " +
strShift);
//Ritorna "Mela"
for(i=0;i< = Frutta. Iength-l;i++)
{
document.write("Fruttat" + i +"] =
: " + Fruttati] +
"<br>");
}// Ritorna un array i cui elementi sono nell'ordine:
Pera , Limone.
Il metodo shiftO elimina il primo elemento del-
l' array e lo restituisce come output. È il duale di
popi).
var Frutta = new Array("Mela", "Pera", "Limone");
Frutta. unshift("Mora", "Albicocca");
for(i=0;i< = Frutta. Iength-l;i++)
{
document.write("Fruttat" + i +"] = " +
Fruttati] + "<br>");
}// Ritorna un array i cui elementi sono nell'ordine:
Mora, Albicocca, Mela, Pera, Limone
var Frutta =
= new Array("Mela", "Pera",
"Limone", "Mora", "Albicocca");
var FruttaSlice = Frutta. slice(l, 3);
// Stampa
l'array FruttaSlice
for(i=0;i< =
= FruttaSlice.length-l;i++)
{
document.write("FruttaSlicet" + i +"] = " +
FruttaSliceti] + "<br>");
}
// Stampa
l'array FruttaSlice, nel quale è stato
omesso il valore "end"
var FruttaSlice = Frutta. slice(3);
for(i=0;i< =
= FruttaSlice.length-l;i++)
{
document.write("FruttaSlicet" + i +"] = " +
FruttaSliceti] + "<br>");
}
L'espresione Frutta.slice(l,3) restituisce un array
i cui elementi sono gli elementi 1 e 2 dell' array
Fruttai), ossia "Fera" e "Limone". Quindi l'ele-
mento corrispondente al valore start è compre-
so, mentre quello corrispondente all'indice end
è escluso. L'array originario Fruttai) rimane
inalterato.
var Frutta = new Array("Mela", "Pera",
"Limone", "Mora
"/'Albicocca");
// Stampo l'array Frutta() originale
for(i=0;i< = Frutta.length-l;i++)
{
document.write("Fruttat" + i +"] = "
+ Fruttati] +
"<br>");
}
// Ordino l'array Frutta() e lo stampo.
Frutta. sort();
for(i=0;i< = Frutta.length-l;i++)
{
document.write("Fruttat" + i +"] = "
+ Fruttati] +
"<br>");
}
Il codice sopra riportato rappresenta il primo
esempio di utilizzo del metodo sorti), senza pa-
rametri in input. Il metodo provvede ad ordina-
re gli elementi dellT array Fruttai), che di conse-
guenza viene modificato in un nuovo array con-
tenente gli stessi elementi dell' array originale,
ma con un ordinamento differente. Si noti che è
possibile utilizzare direttamente il metodo,
senza assegnarlo ad una variabile. In altri termi-
ni, entrambi le espressioni sono corrette:
Il metodo unshift(xl,x2,..) aggiunge in coda al-
l'array le stringhe che vengono passate in input.
È il metodo duale di push(xl,x2,..). Il metodo
unshift(xl,x2,..), come push(xl,x2,..) non è utiliz-
zabile per concatenare array.
Frutta. sort();
var FruttaSort = Frutta, sorto ;
La prima espressione ordina l'array Fruttai), la
seconda espressione, oltre a ordinare l'array
► 100 /Luglio-Agosto 2005
Javascript
i
T CORSI BASE
FruttaQ, crea il nuovo array FruttaSorti), dupli-
cato di Fruttai), con gli elementi in ordine alfa-
betico. Che cosa succede se applichiamo il
metodo sorti) ad un array di numeri ?
var Numeri = new Array(45,73,500,740,23);
// Stampo l'array Numeri() originale
for(i=0;i< = Numeri.length-l;i++)
i
document.write("Numeri[" + i +"] = "
+ Numeri[i] + "<br>");
_}
// Ordino l'array Numeri() e lo stampo.
Numeri. sort();
for(i=0;i< = Numeri.length-l;i++)
i
document.write("Numeri[" + i +"] = " + Numeri[i]
+ "<br>");
L'array di numeri "ordinato" stampa in questo
caso la sequenza di numeri: (23,45,500 ,73,740);
è immediato verificare tale sequenza non è ordi-
nata secondo l'ordine numerico. Questo perché
l'ordinamento utilizzato dal metodo sorti) è
sempre e solo alfabetico, e con questo criterio,
500 è "minore" di 73. Per fare in modo di otte-
nere un sorti) corretto è il seguente:
var Numeri = new Array(45,73,500,740,23);
// Ordino l'array Numeri() e lo stampo.
Numeri, sort(ordina);
for(i=0;i< = Numeri.length-l;i++)
{
document.write("Numeri[" + i +"] = " + Numeri[i]
+ "<br>");
_}
function ordina(xl,x2)
{
return (xl-x2);
}
// Applico il metodo splice()
Frutta. splice(2,2,"Cane", "Gatto", "Topo");
// Stampo l'array Frutta modificato.
for(i=0;i< = Frutta. Iength-l;i++)
{
document.write("Frutta[" + i +"] = " +
Fruttati] +
"<br>");
}
Il metodo Frutta.splice(2,2, "Cane", "Gatto", "To-
po") nell'esempio sopra riportato, elimina due
elementi a partire dal secondo incluso, ossia "Li-
mone" e "Mora", e li sostituisce con "Cane", "Gat-
to", "Topo". L'array che ne deriva è:
Fruttato] = Mela
Fruttati] = Pera
Frutta[2] = Cane
Frutta[3] = Gatto
Frutta [4] = Topo
Frutta[5] = Albicocca
Un po' strana come frutta !
Veniamo ora la metodo valueOfi). La classe Ar-
ray i) non ha un implementato un metodo speci-
fico per valueOfi), ma utilizza quello della classe
padre Objecti). Tale metodo si comporta, dal
lato pratico, come toStringi) ed è poco utile con
la classe Arrayi). Vedremo come meglio utiliz-
zarlo con la definizione di funzione, in articoli
successivi.
Un cenno agli array a più dimensioni. La dichia-
razione:
var
Frutta = new Array(3);
var
Fruttato]
= new Array(4);
var
Fruttati]
= new Array(4);
var
Frutta[2]
= new Array(4);
Permette la costruzione di un array Frutta a due
indici con i seguenti elementi:
In questo caso, la funzione ordina(xl,x2) viene
utilizzata come criterio ordinamento per i nu-
meri xl ed x2 che viene passata in input, ordi-
nando in senso crescente la sequenza. Scam-
biando xl ed x2, si ottiene invece un ordina-
mento decrescente. Altri approfondimenti si
trovano nel box a lato pagina.
Frutta[0][0], Frutta[0][l], Frutta[0][2] # Frutta [0] [3]
Frutta[l][0], Frutta[l][l], Frutta [1][2] # Frutta [1][3] #
Frutta[2][0], Frutta [2] [1], Frutta [2] [2], Frutta [2] [3]
Che possono essere gestiti come elementi di un
array normale.
var Frutta
= new Array("Mela", "Pera",
"Limone", "Mora",
"Albicocca");
// Stampo
l'array Frutta() originale
for(i=0;i< =
= Frutta.length-l;i++)
{
document.write("Fruttat" + i +"] = " +
Fruttati] +
"<br>");
}
CONCLUSIONI
Gli array rappresentano uno dei pilastri della
programmazione. Comprendere i metodi messi
a disposizione dalla classe JavaScript che li
gestice è fondamentale per scrivere applicazio-
ni di qualunque tipo.
Danilo Berta
Luglio-Agosto 2005/ 101
CORSI AVANZATI T
I
Mobile
BlueTooth il re
della comunicazione
Sfruttiamo il Nokia SDK per creare applicazioni che dialogano fra loro
Un'introduzione ai servizi disponibili per altri telefonini e come
cercare e connettersi a cellulari vicini
■WAI.WJ.IJ-JW.H-.ua
\rn\ Basi di C++, basi di
^y Symbian C++
'\ Visual C++ 6 o sup.
oU ActivePerl, Nokia SDK
1.2 o sup.
_^i^j_j_j
Tempo di realizzazione
(0(0(0«ì(0(0
Il sempre crescente bisogno di comunica-
re ha portato, negli ultimi anni, alla diffu-
sione di apparecchi telefonici portatili.
Ciò che prima sembrava un privilegio di pochi
è adesso accessibile a tutti.
Quotidianamente, infatti, premiamo freneti-
camente le dita su tastierini numerici di di-
verse forme e dimensioni. Se da una parte il
mercato vede l'utente finale come un "utiliz-
zatore", l'altra faccia della medaglia è occupa-
ta dagli sviluppatori di software, un laborioso
sciame di api che impiega il suo tempo per
creare prodotti di un certo livello. Sebbene
Java, con i suoi difetti e le sue limitazioni, ha
facilitato lo sviluppo di applicazioni, Symbian
OS ci offre la possibilità di programmare un
dispositivo mobile usando un linguaggio di
più basso livello come il C. Nei precedenti ar-
ticoli non sono state analizzate solo le tecni-
che di base, ma anche alcuni punti chiave del
sistema come l'interfaccia grafica e la ge-
stione dei contatti. Per completare il quadro
non si può non rivolgere lo sguardo a come le
informazioni entrano ed escono da un dispo-
sitivo. Sebbene esistano diversi standard, co-
me il cavo seriale o i segnali a infrarossi, pun-
teremo su quella strana parolina, a volte pro-
nunciata male, che sentiamo dire tutti i gior-
ni: bluetooth.
IL BLUETOOTH
Quando parliamo di bluetooth non dobbiamo
pensare subito ad un qualcosa di fisico, bensì
a "come" le informazioni vengono trasferite
attraverso una rete senza fili. Il mezzo di co-
municazione è essenzialmente lo stesso del
wi-fi, ovvero le onde radio. Ciò che varia sono
le frequenze e le regole per la trasmissione,
che consentono di ottenere delle prestazioni
migliori e delle velocità più elevate, limitando
però le distanze. La comunicazione tra dispo-
sitivi bluetooth di classe A permette una co-
municazione nel raggio di cento metri, mas-
sima distanza usufruibile da questa tecnolo-
gia. Questo è il motivo principale per cui blue-
tooth è particolarmente adatto solo per al-
cune situazioni, come il trasferimento di dati
e il gioco in multiplayer tra dispositivi portati-
li. Inoltre l'adozione del bluetooth su dispo-
sitivi fissi come il personal computer, la tv e
persino l'automobile, ha creato nuove oppor-
tunità per i più svariati usi.
UHI INSIEME
DI RUOTE DENTATE
Lo stack bluetooth, ovvero l'insieme di com-
ponenti di cui è composta l'architettura, è
schematizzato in Figura 1.
I termini qui presenti saranno molto utili per
la comprensione del codice. Il blocco inferiore
riguarda la parte hardware e non verrà tratta-
ta, anche in virtù del fatto che le applicazioni
non hanno accesso diretto a questo livello.
Sarà proprio il sistema operativo Symbian che,
grazie ad un ricco set di API, permetterà di
usufruire dei benefici della tecnologia blue-
tooth. I singoli blocchi hanno ovviamente ruo-
li e caratteristiche diverse tra loro. Il protocol-
lo RFCOMM, posizionato in cima al blocco
superiore, consente di trattare la comunica-
zione bluetooth come se fosse una comunica-
zione seriale. Ciò ritorna utile in presenza di
applicazioni legacy, ovvero datate. SDP (Ser-
vice Discovery Protocol) riveste un ruolo fon-
damentale nella gestione dei servizi presenti
sulla rete. Ogni dispositivo bluetooth possiede
il proprio SDP. Esso avrà il compito di rendere
disponibili, a chi lo richiede, tutte le in-
formazioni utili per l'accesso ad uno specifico
servizio, come l'indirizzo e il numero di porta.
► 102 /Luglio-Agosto 2005
Mobile
▼ CORSI AVANZATI
Stack Bluetooth
Software
SDP RF COMM protocol
L2CAP protocol
HCI Driver
Hardware
HCI Firmware
Link Manager Protocol (LMP)
Baseband Link Controller (LO
Bluetooth Radio
Fig. 1: Uno schema della composizione dello stack
bluetooth
Terminata la fase iniziale, SDP lascia subentra-
re L2CAP (Logicai Link Controller and
Adaptation Protocol). È questo il momento in
cui le informazioni vengono pacchettizzate e
ne viene effettuato il multiplexing sul canale.
Tale livello è completamente trasparente al
programmatore, in quanto implementato dal
sistema operativo stesso. Per finire la parte più
bassa dello stack software è occupata dal dri-
ver HCI (Host Controller Interface) che si
preoccupa del dialogo con la parte hardware
dello stack.
if (sessione. Open(database) != KErrNone) {
User::Panic(_L("ConnessioneBluetooth"),-l);
}
Una volta aperta la sessione con il database
SDP si può aggiungere un servizio. Ricordia-
moci di effettuare una chiamata close() su en-
trambi gli oggetti al termine dell'operazione,
consentendo al sistema operativo di liberare
le risorse occupate. Un servizio è rappresenta-
to da un oggetto della classe TSdpServRecord-
Handle e la sua aggiunta al database SDP è ef-
fettuata richiamando la funzione CreateServi-
ceRecordL sulla sessione ottenuta. Ecco il co-
dice:
TSdpServRecordHandle record;
sessione. CreateServiceRecordl_(KSerialClassID, record);
KSerialClassID è una costante che identifica il
tipo di servizio. Volendo utilizzare una con-
nessione seriale tramite il protocollo RF-
COMM, semplice per la trattazione, il suo va-
lore sarà 0x1101. Un elenco completo è con-
sultabile al seguente uri: https://www.blue-
tooth.org/foundry/assignnumb/document/ser-
vicejdiscovery. Affinché il record sia attivo
dobbiamo dichiararne esplicitamente alcuni
attributi. Ciò viene fatto tramite la classe
CSdpAttrValueDES, vediamo come:
CSdpAttrValueDES* protocolDescriptorList =
SdpAttrValueDES::NewDESL(NULL);
CleanupStack::PushL(protocolDescriptorl_ist);
COME INIZIARE
La comunicazione
seriale è quel sistema
che viene usato ancora
oggi per il
trasferimento di dati
tramite un cavo ed una
porta logica, che forse
conosciamo come
porta COM. I driver
bluetooth che
installiamo sui nostri
pc utilizzano il
protocollo seriale per
interfacciare i vari
software coi i
dispositivi connessi
alla rete.
PUBBLICAZIONE
DI UN SERVIZIO
Come appena accennato, il gestore SDP di
ogni dispositivo deve permettere la registra-
zione di tutti i servizi presenti sul dispositivo
stesso, affinché possano essere visti dall'inte-
ra rete. Symbian OS implementa quindi il da-
tabase SDP come un server, in grado di dialo-
gare con più processi contemporaneamente.
La prima cosa da fare è ottenere un accesso a
tale database. Le classi interessate sono due:
RSdp e RSdpDatabase. Entrambe sono defini-
te in btsdp.h.
RSdp database;
RSdpDatabase sessione;
if (database. Connect() != KErrNone) {
User::Panic(_L("ConnessioneBluetooth"),-l);
1 Installiamo gli strumenti che ci
servono: Nokia SDK 1.2, Micro-
soft Visual C++ 6.0 e ActivePerl.
Durante l'installazione di Visual
C++ ricordiamoci di settare le varia-
bili d'ambiente. Il Nokia SDK può
essere scaricato all'indirizzo
http://www.forum.nokia.com/main
/o..034-4.00.html .
2 Aggiungiamo a Visual C++ il
template Nokia per la creazione
assistita di un'applicazione base.
Copiamo avkonappwiz.awx e avko-
nappwìz.hlp da Symbìan\6.1\Serìes-
60\Series60Tools\applicationwizard
a Microsoft Visual Studio \Common
\MSDev98\Template\
3 Apriamo Visual C++ e clicchiamo
su File, poi su New. Dalla tab
projects selezioniamo Series 60
AppWizard, digitiamo il nome del
progetto, settiamo la directory e
premiamo OK. Nel nuovo dialog in-
seriamo il nome dell'applicazione e
clicchiamo su Finish.
4 Scriviamo il nostro codice ag-
giungendo funzioni alla struttu-
ra standard o creiamo nuove classi
adatte alle nostre esigenze.
5 Apriamo una console dei co-
mandi e posizioniamoci nella di-
rectory group appartenente al no-
stro progetto. Digitiamo bldmake
bldfiles e subito dopo abld build
thumb urei. Il programma è compi-
lato.
6 Per creare il file d'installazione
portiamoci invece nella directo-
ry instali e digitiamo makesis Mia-
Applicazione .pkg.
Trasferiamo MiaApplicazione.sis sul
telefonino, installiamolo e lancia-
molo.
Luglio-Agosto 2005/ 103 ►
CORSI AVANZATI T
I
Mobile
protocolDescriptorList
->StartListL()
->BuildDESL()
->StartListL()
->BuildUUIDL(KL2CAP)
->EndListL()
->BuildDESL()
->StartListL()
->BuildUUIDL(KRFCOMM)
->BuildUintL(TBuf8<l>(KChannel))
->EndListL()
->Endl_istl_();
CleanupStack::PopAndDestroy(protocolDescriptorList);
Dispositivi trovati:
% pc cpsp ••!
Ricerca di O
dispositivi in
corso
Annulla
Seleziona Annulla
Fig. 2: Il dispositivo ricerca I dispositivi e tenta di
connettersi a quello selezionato
Il funzionamento
server di una classe
consente, grazie
all'aiuto dei thread,
l'accesso
contemporaneo di
diversi processi.
Questo risulta utile
quando le azioni che
vogliamo
implementare sono
abbastanza comuni e
possono essere
eseguite nello stesso
momento.
Ogni funzione della classe CSdpAttrValueDES
restituisce l'oggetto attivo dopo aver apporta-
to le modifiche. È possibile quindi effettuare
una serie di chiamate a cascata. StartListL se-
gna l'inizio di una lista di parametri ed End-
ListL ne segna la fine. Tramite BuildxxxxL so-
no stati aggiunti i due protocolli L2CAP e RF-
COMM, discussi in precedenza. Entrambi so-
no delle costanti definite in bt_sock.h. La se-
guente riga di codice aggiorna il record all'in-
terno del database:
sessione. UpdateAttributel_(record,
KSdpAttrld ProtocolDescriptorList,
*protocolDescriptorl_ist);
Per finire, possiamo aggiornare alcuni singoli
attributi del record. Tutte le costanti sono de-
finite in btsdp. h:
sessione. UpdateAttributel_(iRecord,
KSdpAttrldServicelD, KUidBTAdvertiserAppValue);
sessione. UpdateAttributel_(iRecord,
KsdpAttrldBasePrimaryLanguage
+ KSdpAttrldOffsetServiceName, _L("Mercurio"));
sessione. UpdateAttributel_(iRecord,
KsdpAttrldBasePrimaryLanguage +
KSdpAttrldOffsetServiceDescription,
_L("Questa è la descrizione del servizio"));
Un servizio di nome Mercurio è stato aggiunto
con successo al database SDP Abbiamo inoltre
specificato il suo nome, Mercurio, ed una sua
descrizione. Aprendo un ServerSocket in locale
si può adesso sfruttare tale servizio per far
conoscere a tutti i dispositivi collegati in rete i
parametri necessari per la connessione.
RICERCARE UN SERVIZIO
Come forse molti di noi avranno notato, man-
dando una foto o un contatto tramite blue-
tooth, compaiono sul display del nostro cellu-
lare le finestre riportate in Figura 2. Questo
passaggio è preliminare nel trasferimento
delle informazioni in quanto "presenta" due
dispositivi, permettendo loro di conoscersi e
di dialogare. In questa sezione parleremo di
come è possibile rintracciare un servizio. La
procedura può essere schematizzata in tre
passi:
1. Ricerca e selezione di un dispositivo pre-
sente nel raggio d'azione
2. Ricerca dei servizi presenti sul dispositivo,
offerti tramite server DSP
3. Selezione di un particolare servizio
RNotifier è la classe chiave per eseguire il pri-
mo punto. Dal momento che il suo funziona-
mento è di tipo server, deve prima essere ef-
fettuata una connessione.
RNotifier notifier;
User::LeaveIfError(notifier.Connect());
Tramite la funzione StartNotifierAndGetRe-
sponseO è quindi possibile avviare la ricerca
dei dispositivi, facendo comparire a schermo
una GUI standard che elenca quelli trovati.
Notiamo che tale GUI viene visualizzata an-
che quando il programma non possiede un
ambiente grafico. Inoltre, visto che si tratta di
una chiamata asincrona , lo stato del procedi-
mento sarà contenuto in un oggetto di tipo
TRequestStatus e sarà nostra premura atten-
dere su questo oggetto prima di proseguire. Il
riferimento al dispositivo selezionato dall'u-
tente verrà memorizzato in un oggetto di clas-
se TBTDeviceResponseParamsPckg, che come
si vede dalla documentazione Symbian è un
typedefper il template TPckgBufo, un conte-
nitore di oggetti di tipo TBTDeviceResponse-
Params. Ecco il codice:
TBTDeviceSelectionParamsPckg filtro;
TBTDeviceResponseParamsPckg aResponse;
► 104 /Luglio-Agosto 2005
Mobile
i
T CORSI AVANZATI
TRequestStatus status;
notifier.Startl\lotifierAndGetResponse( status,
KDeviceSelectionNotifierllid, filtro, aResponse );
User: :WaitForRequest(status);
if (status. Int() == KErrNone)
User::Panic(_L("RicercaDispositivi"),-l);
notifier.CancelNotifier(KDeviceSelectionNotifierUid);
notifier.Close();
È adesso possibile ottenere tutti i servizi di-
sponibili relativi all'indirizzo del device sele-
zionato in precedenza dall'utente. Il sistema
operativo ci mette a disposizione un agente
tramite la classe CSdpAgent. I parametri di in-
gresso nella costruzione sono due: un oggetto
che implementa l'interfaccia MSdpAgentNoti-
fier ed un indirizzo.
CSdpAgent* agente
CSdpAgent: : NewL(*this,
aResponse. BDAddrQ);
agente->NextRecordRequestl_();
NextRecordRequestLQ fa partire la scansione.
Ogni qual volta viene individuato un servizio,
l'agente richiama NextRecordRequestComple-
te() sull'oggetto passato in fase di costruzione.
Nel nostro caso è this. Ecco la segnatura della
funzione:
void NextRecordRequestComplete(TInt aError,
TSdpServRecordHandle aHandle,
TInt aTotaIRecordsCount)
All'interno di tale funzione dobbiamo scrivere
il codice per verificare se si tratta del servizio
che stiamo cercando. Tutte le informazioni sui
servizi sono ovviamente contenute nel para-
metro aHandle.
te sono RSocketServ e RSocket. Esse contengo-
no le classiche funzioni attuabili sui socket,
come openQ, bindQ, etc. Basta dare uno
sguardo alla documentazione Symbian per
creare in pochi passi un applicativo point to
point.
Un'ultima constatazione deve riguardare la
relazione tra i socket e i servizi bluetooth.
I servizi servono proprio per far circolare sulla
rete le informazioni
riguardanti il collega-
mento tra due socket,
primi fra tutti l'indi-
rizzo di rete e la porta
di connessione.
In più, dato che si trat-
ta di una tecnologia
molto versatile, le
informazioni traspor-
tabili col servizio stes-
so possono variare in
numero e dimensione.
COS'È UNA CHIAMATA
ASINCRONA
Una chiamata asin-
crona viene eseguita
in maniera parallela
al normale flusso di
codice. A seconda
dei casi, è necessa-
rio aspettare espli-
citamente che essa
termini, prima di
poter effettuare
altre operazioni.
Una chiamata
sincrona è anche
definita bloccante in
quanto il program-
ma non continua la
sua esecuzione
fintantoché la
funzione sta lavo-
rando.
Fig. 3: Sul sito di Nokia www, forum, nokia. com/main
/0. . 034-4. 00. html è possibile trovare un elevato
numero di informazioni sulla programmazione Symbian
COLLEGAMENTO
POINT TO POINT
Avendo visto come pubblicare un servizio e
come cercarne di nuovi, possiamo addentrar-
ci in un caso pratico. L'SDK della Nokia con-
tiene, tra gli applicativi d'esempio, proprio
quello che fa al caso nostro. Il ricevente crea
un ServerSocket su una data porta. I parame-
tri di connessione vengono poi incapsulati in
un servizio che viene aggiunto al database
SDP. Il dispositivo mandante effettua quindi
la ricerca di tale servizio tramite la procedura
riportata nella sezione precedente. Una volta
stabilito il collegamento, i due processi, resi-
denti su due diversi dispositivi, possono tra-
sferirsi informazioni. Senza scendere troppo
nello specifico diciamo che le classi interessa-
CONCLUSIONI
Siamo adesso in grado di aggiungere un sep-
pure minimo supporto bluetooth all'interno
delle nostre applicazioni. Il discorso sarebbe
molto più vasto.
Si potrebbe parlare di OBEX, un protocollo
per il trasferimento di file in una rete senza
fili. Oppure analizzare la sicurezza in una rete
bluetooth. Symbian stesso dispone di un cer-
to numero di API per l'autenticazione e per la
crittazione delle informazioni. Come sempre,
una volta fatte le fondamenta, la costruzione
è molto più facile. Infine, a noi piace molto
quella meravigliosa sfida che è la programma-
zione.
Antonio Trapani
Luglio-Agosto 2005/ 105 ►
TIPS & TRICKS T I Una raccolta di trucchi da tenere a portata di... mouse
I trucchi del mestiere
Hps & Tricks
Questa rubrica raccoglie trucchi e piccoli pezzi di codice, frutto dell'esperienza di chi programma, che solitamente non
trovano posto nei manuali. Alcuni di essi sono proposti dalla redazione, altri provengono da una ricerca su Internet, altri
ancora ci giungono dai lettori. Chi volesse contribuire, potrà inviare i suoi Tips&Tricks preferiti. Una volta selezionati,
saranno pubblicati nella rubrica. Il codice completo dei tips è presente nel CD allegato nella directory \tips\o sul Web
all'indirizzo: cdrom.ioprogrammo.it.
IM9
VISUAL
BASIC
INTERCETTARE GLI EVENTI
DEL CD-ROM
In taluni casi può risultare utile intercettare l'inserimento e l'e-
strazione di un cd-rom o di un altro media rimovibile nel com-
puter, per proseguire con ulteriori indagini sul volume inserito o
semplicemente per aggiornare alcuni elementi dell'interfaccia
grafica. In Windows è possibile ricevere le notifiche di tali eventi
semplicemente gestendo il messaggio WM_DEVICE_CHANGE.
Questo tip illustra come fare a ridefinire il metodo WndProc di
un'applicazione Windows form per gestire tale messaggio.
'Costanti usate dal gestore dei messaggi:
Private Const WM_DEVICECHANGE As Integer = 537
Private Const DBT_DEVICEREMOVECOMPLETE As Integer = 32772
Private Const DBT_DEVICEARRIVAL As Integer = 32768
Protected Overrides Sub WndProc(ByRef m As
System.Windows.Forms.Message)
Select Case m.Msg
Case WM_DEVICECHANGE
If (m.WParam.ToInt32() = DBT_DEVICEREMOVECOMPLETE) Then
MessageBox.Show("CD espulso!")
Elself (m.WParam.ToInt32() = DBT_DEVICEARRIVAL) Then
MessageBox.Show("Nuovo CD inserito!")
End If
Case Else
'Chiama l'implementazione base di WndProc per gli altri
messaggi:
MyBase.WndProc(m)
End Select
End Sub
JAVA
SCRIPT
AGGIORNARE SENZA REFRESH
DEL BROWSER
Con questo semplice esempio mostriamo come sia possibile, uti-
lizzando l'oggetto XMLHttpRequest, realizzare delle pagine che
aggiornano la loro interfaccia senza richiedere il refresh del brow-
ser. Lo script che segue rende possibile realizzare una pagina che
mostra una serie di slide, prelevate su richiesta dal server, e le
visualizza senza ricaricare la pagina. Per provare l'esempio è ne-
cessario pubblicare su un server web lo script, la pagina di prova
e le slides.
/**
* Funzione che istanzia un oggetto XMLHttpRequest usando un
meccanismo cross browser.
*
* ©return restituisce un'istanza di XMLHttpRequest oppure il
valore false in caso
* di errori.
V_
function getXMLHttpRequestlnstanceQ
{
var xmlhttp;
// Prova il metodo Microsoft usando la versione più recente:
try
xmlhttp = new ActiveX0bject("Msxml2.XMLHTTP");
} catch (e)
{
try
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (E)
{
xmlhttp = false; } }
// Se non è stato possibile istanziare l'oggetto forse siamo
// su Mozilla/FireFox o su un altro browser compatibile:
if (ixmlhttp && typeof XMLHttpRequest != 'undefined')
A
try
xmlhttp = new XMLHttpRequestQ;
} catch (e)
{
xmlhttp = false; } }
// Restituisce infine l'oggetto:
return xmlhttp; }
► 106 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Una raccolta di trucchi da tenere a portata di... mouse H T TIPS & TRICKS
* Funzione che sostituisce il contenuto HTML di un nodo della pagina.
* @param nodeld ID del nodo
* @param html codice HTML da sostituire a quello del nodo
j7
function updateContent(node!d, html)
{
var node = document.getElementByld(nodeld);
if(null == node)
{
alert("[ERRORE] L'elemento " + nodeld + " non esiste");
return; }
node.innerHTML = html;
node.style.visibility = "visible";}
/**
* Richiede al web server il contenuto di una slide (testo o HTML) in
maniera asincrona.
* @param nodeld ID dell'elemento della pagina che conterrà la slide
* @param uri URL della slide (deve essere sullo stesso server per
motivi di sicurezza)
j7
function showSlide(nodeId, uri)
{
var xmlhttp = getXMLHttpRequestInstance();
if(lxmlhttp)
{
alert("II browser non supporta
l'oggetto XMLHttpRequest");
return false;}
xmlhttp. open("GET", url,true);
xmlhttp. onreadystatechange=function()
{
if (xmlhttp. readyState==4)
{
if (xmlhttp. status= = 200)
{
updateContent(nodeId,
xmlhttp. responseText);
} else if (xmlhttp. status= =
=404) {
alert("[ERRORE] l'URL '
+url+"non esiste!");
} else {
alert("[ERRORE] errore
non gestito (" + xmlhttp. status
+ ")");}
}}
xmlhttp. send(null);
}
(Mirai^ il TIP DEL
Ctt
AGG lUniG ERE EFFETTI SPECIALI ALLE FINESTRE
Tip fornito da Domenico Testa
Diamo un pò di brio alle nostre applicazioni Windows Forms
aggiungendo qualche gradevole effetto speciale. Possiamo uti-
lizzare infatti, tramite i servizi di interoperabilità messi a dispo-
sizione dal .NET framework, la funzione AnimateWindow,
esportata dalla libreria di sistema user32.dll.
Con questa funzione è possibile, specificato Fhandle di una
finestra, aggiungere e combinare tra loro una serie di effetti spe-
ciali quali dissolvenza, scorrimento orizzontale e verticale, sia al
caricamento della finestra che alla sua chiusura, semplicemen-
te passando gli opportuni flag.
using System;
using System. Windows. Forms;
class AnimatedForm : Form
{ /// <summary>
/// Funzione per animare una finestra, esportata dalla libreria
user32.dll
/// </summary>
[System.Runtime.InteropServices.DIIImport("user32.dN")]
static extern bool AnimateWindow(IntPtr hwnd, int time,
AnimateWindowFlags flags);
/// <summary>
/// Flag utilizzabili nella funziona AnimateWindow.
/// Questi flag possono essere combinati.
/// </summary>
enum AnimateWindowFlags
{ HorPositive= 0x00000001, // Anima la finestra da sx a dx (con
AnimationSlide)
HorNegative = 0x00000002, // Anima la finestra da dx a sx
(con AnimationSlide)
VerPositive = 0x00000004, // Anima la finestra dall'alto verso
il basso
VerNegative = 0x00000008, // Anima la finestra dal basso
vero l'alto
Center = 0x00000010, // Centra la finestra
Hide = 0x00010000, // Nasconde la finestra
Activate = 0x00020000, // Attiva la finestra
Slide = 0x00040000, // Usa l'animazione slide
Blend = 0x00080000 // Attiva un effetto fading }
Animated Form()
{ // Qualche effetto speciale d'esempio:
AnimateWindow(Handle, 500, AnimateWindowFlags. Activate |
AnimateWindowFlags. Blend);
AnimateWindow(Handle, 700, AnimateWindowFlags. Hide |
AnimateWindowFlags. Slide | AnimateWindowFlags. Center);
AnimateWindow(Handle, 500, AnimateWindowFlags. Slide |
AnimateWindowFlags. HorPositive);
AnimateWindow(Handle, 500, AnimateWindowFlags. Hide |
AnimateWindowFlags. Slide | AnimateWindowFlags. HorNegative
| AnimateWindowFlags. VerPositive);
AnimateWindow(Handle, 500, AnimateWindowFlags. Activate |
AnimateWindowFlags. Slide | AnimateWindowFlags. HorPositive |
AnimateWindowFlags. VerNegative); }
static void Main()
{ Application. Run(new AnimatedForm()); }}
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 107 ►
EXPRESS T ■ Access
Input guidato in dB Access
Spesso la fase di inseri-
mento dei dati può pro-
durre errori. I principi sul-
l'usabilità del software indi-
cano chiaramente la strada
che il programmatore deve
seguire. L'input dei dati deve
essere semplice e deve ridur-
re al minimo gli errori. Esi-
stono molti accorgimenti che
vanno in questa direzione.
Se il valore da inserire è con-
tenuto in un insieme circo-
scritto, ossia un gruppo di
elementi non molto numero-
so; allora si può pensare di
proporre all'utente la scelta
tra una lista. Digitare il valo-
re da immettere potrebbe
provocare un errore di batti-
tura. La scelta tra una lista
di valori minimizza la possi-
bilità di sbaglio. Nell'esem-
pio si fa riferimento ad un
database di nome software
costruito con MS Access.
Esiste un'unica tabella di
nome software che ha lo
scopo di mantenere informa-
zioni circa software commer-
ciali, una sorta di biblioteca
di programmi. Se volete una
" softwa reteca". Per ogni
record si memorizza un IDS
- identificativo del software
che è anche chiave. Gli altri
attributi sono: nome, casap,
versione, tipo e licenza.
I primi tre indicano rispetti-
vamente la denominazione,
la casa produttrice e la relea-
se del software. Il loro input
avviene in modo tradizionale,
per completa digitazione. I
successivi due sono il tipo
(di base, applicativo, e altri)
e la licenza ( freeware, sha-
revare e altro). Come suggeri-
scono le parentesi, per que-
sti, si possono costruire
insiemi ridotti di elementi.
Fabio Grimaldi
<1> GENERA LA MASCHERA
IN MODALITÀ STRUTTURA
Uri* Maschera
■■■
llMaschera
d
Formato Dati | Evento | Altro |
Tutte |
Oriaine record JSIluMKma
-I-I
Filtro
Ordina per
Consenti filtri 5Ì
Consenti modifiche Sì
Consenti eliminazioni Si
Consenti aggiunte Si
Immissione dati No
Tipo Recordset Dynaset
Blocco record Nessun blocco
Recupera i redef Si
Dopo aver eseguito le operazioni di routine: apertura am-
biente Access; creazione di un database vuoto; salvatag-
gio con nome software; creazione di una tabella con i cam-
pi sopra descritti, si genera una maschera in modalità
struttura. Si associa alla tabella software. Apparirà la ta-
bella, sottoforma dei sui campi di fianco alla maschera.
4> IL CAMPO TIPO E A DUE
COLONNE
Creazione guidata Casella combinata
Quali valori d> e inclusi nella casella
inserire nell'elencOj quindi immettere i valori pe
Per regolare la larghezza di una colonnaj trast
doppio clic sullo stesso bordo per ottenere un
Numero di colonne: |~2~~
Coli
Col2
Di base
Sistema Operativo
Utility
Antivirus
me rete
Applicativo
Office Automation
Programmazione
J
™
DataBase
1
Per rendere l'input più professionale per il campo tipo
associamo due colonne. Per potere distinguere i due gran-
di gruppi di software di base e applicativo. Si digita 2a\
numero di colonne. Si tratta però di specificare che sarà
la seconda colonna ad andare nel campo tipo. Questo lo
si decide al passo successivo.
t2> FASE DI COSTRUZIONE
DELLA MASCHERA
IH Mascherai : Maschera
00©
| ■ l ■ 1 ■ l ■ 2 ■ I ■ 3 ■ I ■ 4 ■ I ■ 5 ■ I ■ 6 ■ I ■ 7 ■ I ■ 8 ■ I ■ 9 ■ I ■ 10 ■ I ' 11 ' I ' 1_±.
| -9 Corpo
Ah abl Q
p! » F
m m _i
SII
mMm
: ~T=
input So
ftw
ire
■
: ^
|Norne
-!„„, -f^-
■|Ver
IDS
1 1
. || Nonne
V
" Versione
4 1 CasaP
Il Tipo
<£ Licen
Trascinando i campi di cui si vuole digitare l'input in au-
tomatico si creano delle caselle di testo. Utilizzando la
casella degli strumenti si aggiungono due caselle com-
binate. Associate rispettivamente ai due campi tipo e
licenza. Si personalizza la form con un titolo ed eventua-
li immagini. L'aspetto ottenuto è quello in figura.
<5> ASSOCIAZIONE AL GIUSTO
CAMPO
f* Memorizza il valore per uso
successivo
Ì* Memorizza il valore in questo
campo:
^J
ID5
Nome
Versione
CasaP
Licenza
Nella finestra successiva si associa il risultato al
campo tipo. Si ripete dal passo due la stessa sequenza
di operazioni per il campo licenza. Questa volta è suffi-
ciente una sola colonna. Adesso l'intera maschera è
completa. Usciamo e diamo un nome alla form. Input
software è un etichetta appropriata.
Non resta che vedere il risultato.
t3> IMMISSIONE PERSONALIZ-
ZATA DI UNA USTA DI VALORI
Creazione guidata Casella combinata
= 3
Questa : recedui-a guidata consente dì « reare una casella
ombinata co ì l'elenco dei valori : le :: possibile selezionare,
Indicare la modalità che dovrà essere utilizzata da parte della
casella combinata per caricare i valori.
■ Ricerca valori ir una tabella :■ query da patte della casella
combinata.
,: * tffflnis: one personalizzata
u valore selezionato
J
_L
H
Se la casella degli strumenti ha attivato il generatore
automatico di eventi, viene avviato un processo per
l'immissione dei valori. A tale scopo il bottone con la
bacchetta magica deve essere in stato di premuto. Noi
optiamo per l'immissione personalizzata. Per poter ag-
giungere una lista di valori da cui l'utente potrà attin-
gere. Questo ci darà maggiore flessibilità.
<G> OBIETTIVO RAGGIUNTO.
ECCO LA MASCHERA IN FUNZIONE
EU Input Software : Maschera
- n(x)
input : Software
Software: [Access
ine: [2002
e. asaPr "duttnce; [Microsoft Lorporat o ;
Record: H | i | [
An;': :ativc
Sistema Operativo
Utility
Alitivi us
Gestione rete
Office Automation
Programmazione
Per vedere il risultato basta cliccare sulla maschera
appena creata. Si nota come i primi tre input debba-
no essere digitati, mentre i secondi due possano es-
sere effettuati mediante la scelta di un valore su una
casella combinata.
La chiave IDS essendo contatore viene assegnata in
automatico mediante incremento.
► 108 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Access ■ T EXPRESS
Query a campi incrociati
Estrarre informazioni dai
dati opportunamente
organizzati in un database
è uno dei compiti di un
avanzato DBMS. In MS
Access, tra gli altri stru-
menti, sono previste le
query a campi incrociati. Si
tratta di un'utile mezzo che
permette di riorganizzare e
computare i dati per sem-
plificarne l'analisi. Le query
a campi incrociati vengono
utilizzate per eseguire una
somma, una media, un
conteggio o qualsiasi altro
tipo di totale sui dati rag-
gruppati in base a due tipi
di informazioni. Tali infor-
mazioni saranno disposti su
una tabella a doppia entra-
ta, ovvero, una tabella in
cui le intestazioni di riga e
di colonna sono associate
alle due informazioni.
L'incrocio tra riga e colonna
è l'elemento che attraverso
le nostre query intendiamo
riorganizzare. Nell'esempio
si intende applicare il
metodo ad una tabella otte-
nuta come il risultato di
una query. Essa conteneva i
dati delle spese in relazione
a dei progetti ed in partico-
lare alle voci di tali proget-
ti. Le voci sono le stesse
per differenti progetti.
La classica visualizzazione
della tabella, produceva in-
nanzitutto ridondanza poi-
ché le stesse voci venivano
ripetute per diversi progetti,
così come si duplicava il
nome del progetto; inoltre,
la lettura era poco chiara.
Applicando il metodo citato
elimineremo la ridondanza
e otterremo una tabella leg-
gibile e significativa.
Vediamo come.
Fabio Grimaldi
<1> SITUAZIONE DI PARTENZA
PRODUCIAMO UNA QUERY
H PROGETTI : Tabella
Progetto
Voce
Spesa
Editoria elettronica
Progettazione
2300
Editoria elettronica
Materiale
5100
Editoria elettronica
Consulenza
10800
L'azienda nel web
Progettazione
1800
L'azienda nel web
Materiale
3100
T
L'azienda nel web
Consulenza
12500
Inizialmente si ha una tabella in cui sono presenti tre
attributi. I primi due identificano rispettivamente il
progetto e la voce di spesa. Nell'ultimo attributo è ri-
portata la spesa vera e propria. Si può notare presen-
za di ridondanza e la difficoltà nel leggere i dati, che
in una sola parola sono disorganizzati. Il nostro scopo
è ottenere una tabella che riassuma le voci di spesa
per ogni tipologia di dato; ad esempio ci piacerebbe
sapere quanto si spende globalmente.
4> STRUTTURAZIONE
DELLA QUERY
Campo:
Tabella:
Formula:
Campi incrociati:
Ordinamento:
Criteri:
Oppure:
Progetto
Voce
Spesa
PROGETTI
PROGETTI
PROGETTI
Raggruppamento
Raggruppamento
Sommai
zione riga
Intestai, colonna
Valore
< | mi |
I due campi progetto e voce saranno rispettivamente
le intestazioni della riga e della colonna.
Per farlo si deve porre a raggruppamento il valore
corrispondente alla riga formula, e intestazione di
riga (di colonna per l'attributo voce) in corrisponden-
za di campi incrociati.
Per spese i due campi varranno somma e valore.
Anche se si tratta di riorganizzare i dati e non di sele-
zionarli, ci serviamo ugualmente di una query. È que-
sta l'occasione per ampliare il significato di questo
utile strumento. Per farlo scegliamo dal raccoglitore
apposito, che compare all'avvio di Access, lo stru-
mento query che applicheremo alla tabella progetti
già presente.
<5> METTERE APPUNTO
E SALVARE LA QUERY
ÉP Queryl Query a campi incrociati
Progetto
Voce
Salva con n
SS
\Jorne ■' uery:
Spese X fjìogetbj e ':', vi
Nella modalità struttura i valori previsti dalla query a
campi incrociati possono essere selezionati da appo-
site liste. Appare un piccolo menu accessibile attra-
verso la conosciuta freccetta verso il basso. Terminato
la definizione della struttura della query, chiudendola
ci viene chiesto il nome con cui salvarla.
Scegliamo "Spese X progetto e X voce".
<3 OPTIAMO PER LA QUERY
A CAMPI INCROCIATI
Query Strumenti Finestra ?
Esegui
Ini Nostra tabella..,
Rimuovi tabella
[ip Query di selezione
IH Query a campi incrociati
Eaf ! Query di creazione tabella. ,
\$l Query di aggiornamento
Q ! Query di accodamento . . .
jK! Query di eliminazione
5QL specifico
Parametri...
Una volta avviata la procedura di strutturazione della
query si procede con la scelta del tipo. Dalla barra dei
menu si può operare tale scelta. Nel caso specifico
optiamo per la query a campi incrociati selezionando
dal sotto menu query la voce apposita. Al termine del-
l'operazione apparirà la ben conosciuta finestra di
produzione query.
VISUALIZZAZIONE DELLA
QUERY A CAMPO INCROCIATO
ÉP Spese X progetto e X voce : Query a campi incrociati
Progetto | Consulenza! Materiale
Progettazione
►
Editoria elettronica 10300 5100
2300
L'azienda nel web 12500 3100
1300
Il risultato è davvero apprezzabile. I valori di spesa
sono incasellati con un tabella che ricorda un po' le
griglie di Excel. Del resto anche access deve prevede-
re il trattamento, se pur in forma più elementare di
dati numerici. Si può notare la maggiore chiarezza e
l'eliminazione della ridondanza rispetto alla situazio-
ne di partenza di Figura 1.
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 109 ►
EXPRESS T
Realizzare una classe
per la gestione delle matrici in c++
Spesso capita di dover
lavorare con dati ordinati
come tabelle oppure con le
matrici. In questi casi ci tro-
viamo di fronte a due proble-
matiche: la gestione dinami-
ca della memoria e l'acces-
so rapido alla struttura dati.
La maggior parte dei casi
permette l'utilizzo delle
matrici statiche. Ovvero ven-
gono fissati il numero di
righe ed il numero di colon-
ne al momento della creazio-
ne e non abbiamo più la pos-
sibilità di modificarle.
Ma non di rado incontriamo
il bisogno di dover cambiare
la dimensione ed allora
incontriamo dei problemi.
Quindi ora andiamo a vedere
come realizzare una classe
template Matrice veloce e
semplice da usare. Ciò di cui
abbiamo bisogno è un com-
pilatore c++, un editor di
testo e un po' di dimesti-
chezza con c++.
Per realizzare il seguente
esempio ho utilizzato Dev-
C++ un ambiente di sviluppo
c++ freeware.
Dev-C++ è scaricabile gratui-
tamente dal sito web:
www. bloodshed. net
Stefano Vena
<1 ? LE PRIME RIGHE DI CODICE <2> I COSTRUTTORI
ttinclude <stdlib.h>
template<class T>
class Matrice
{
private:
T* vettore;
int size;
int m_cols:
int m_rows:
La classe viene realizzata come template in modo
tale da rendere semplice l'utilizzo della stessa per
qualsiasi tipo di dato. La "specializzazione" per un
tipo di dato verrà fatta dal compilatore durante la
compilazione. I membri della classe vengono defini-
ti con visibilità private. Da notare la variabile che
conterrà la matrice è un semplice vettore!
ACCESSO ALLE CELLE
const T& getAtflnt rowjnt col) const {
return vettore[row*cols+col];}
void setAt(int row,int col,
const T& valueK
vettore[row*cols+col] = value;}
const T* operator[](int row) const{
return vettore+row*m_cols;}
T* operator[](int row){
return vettore+row*m_cols:
}
Il metodo di memorizzazione delle celle è molto sem-
plice ed efficiente.Le righe vengono salvate di segui-
to una all'altra. Per ottenere il valore alla cella di
coordinate x,y "saltiamo" le prime x righe e resti-
tuiamo il valore alla colonna y. I metodi getAt e setAt
sono molto intuitivi e non necessitano di altri com-
menti. L'operatore [] è realizzato in modo tale da
simulare il comportameto dell'operatore [][]. Infatti
utilizzando l'operatore [] esso restituisce un sotto
array che punta alla prima cella della riga scelta.
Accederemo alle celle successive attraverso l'utilizzo
della doppia parentesi quadra(fi) ancora una volta.
public:
MatrixO
J
vettore = NULL;
size = 0:
m_cols = m_rows = 0: }
Matrix(int rows, int cols)
{
vettore = NULL:
resize(rows,cols):
resetO: }
Matrix(const Matrix<T>& m)
{
vettore = NULL:
resize(m.m_rows,m.m_cols);
memcpy(vettore,m.vettore,size):}
-MatrixO
J
if(vettore!=NULL)free(vettore):}
Trattandosi di una classe template dobbiamo imple-
mentare i metodi in modalità inline poiché non tutti
i compilatori supportano le classi template nel for-
mato "Intestazione - Sorgente". Il codice inserito in
questo passo non necessita altre spiegazioni.
éi
ALTRE OPERAZIONI
int cols Qconst {return m_cols:}
int rows Qconst {return m_rows;}
void resetO
{
if( vettore == NULL ) return:
memset(vettore,0,size):
}
};
A questo punto aggiungiamo la possibilità di otte-
nere il numero di righe e colonne della matrice.
Se vogliamo impostare tutte le celle della matrice
sul valore zero possiamo usare il metodo reset
Grazie alla funzione memset il metodo reset azzera
la matrice.
3> ALLOCAZIONE
DELLA MEMORIA
void resize (int rows, int cols) {
size = sizeof(T) * rows * cols:
if(vettore == NULL)
vettore = (T*) malloc ( size):
else
vettore = (T*) realloc (vettore, size):
if(vettore == 0)
throw "Memoria insufficiente":
m cols = cols:
m rows = rows:
}
La funzione resize è il cuore della classe. Attraverso
questo metodo ridimensioniamo la matrice.
La variabile vettore è un puntatore, quindi se esso
punta al valore NULL allochiamo lo spazio necessa-
rio per la prima volta con malloc. Se abbiamo già
allocato della memoria per il puntatore vettore ne
riserviamo altra, secondo le nostre esigenze tramite
la funzione realloc. Se la memoria virtuale non è
sufficiente a soddisfare la richiesta, la nostra varia-
bile punterà al valore NULL. In questa sfortunata
situazione lanciamo un eccezione. Sempre in questa
fase salviamo le informazioni su righe e colonne
della matrice.
<6 UTILIZZIAMO LA CLASSE
Matrice<int> Matricejnt (,5):
Matrice<double> Matrice_Dbl;
Matrice_Dbl.resize(10,5):
Matrice_Dbl[0][0]=1.0;
Matrice_Dbl.setAt(1,1 ,
Matrice_Dbl.getAt(0,0) );
cout « MatriceJnt.colsQ « endl:
cout « MatriceJnt.rowsO « endl:
MatriceJnt.resizeO:
Questa è una carrellata delle poche ma utili funzio-
nalità della classe appena creata.
► 110 /Luglio-Agosto 2005
http://www.ioprogrammo.it
T EXPRESS
Realizzare una classe per la gestione
"Intelligente" dei puntatori
I puntatori rappresentano
gioie e dolori dei program-
matori C++. Essi permettono
di compiere tutte quelle
operazioni che fanno parte
della programmazione "spor-
ca" ma proprio per questo
motivo sono spesso la causa
di errori. Capita spesso di
utilizzare puntatori che non
fanno più riferimento agli
oggetti aspettati, oppure
tentiamo di liberare la
memoria di puntatori vuoti o
peggio ancora terminiamo il
programma senza rilasciare
le risorse. In questi casi
avremmo bisogno del garba-
ge collector! In mancanza
del garbage collector possia-
mo realizzare e, quindi, uti-
lizzare una classe in grado
di maneggiare i puntatori al
posto nostro. Attenzione
però! Gli oggetti della nostra
classe andranno istanziati
tutti sullo stack e non nella
memoria virtuale! Quindi ora
andiamo a vedere come rea-
lizzare un gestore di punta-
tori attraverso una classe
template veloce e semplice
da usare. Ciò di cui abbiamo
bisogno come al solito è un
compilatore c++, un editor
di testo e un po' di dimesti-
chezza con il linguaggio. Per
realizzare il seguente esem-
pio potremmo utilizzare
l'ambiente di sviluppo (free-
ware). Dev-C++, scaricabile
gratuitamente dal sito web :
www. bloodshed. net
Stefano Vena
<1 : LE PRIME RIGHE DI CODICE <2> I COSTRUTTORI
ttifndef PUNTATORI_INT_H
ttdefine PUNTATORI INT H
ttinclude <map>
extern
std::map<void * r int> lista_puntatori;
template <class T>
class Puntatore
{ protected:
T *obj;
Per prima cosa creiamo due file il primo di nome "pun-
tatore.!)"^ un secondo di nome "puntatore .cpp" nel
file con estensione "//"andiamo a scrivere il codice del
primo passo. Gioca un ruolo importante la mappa 7/-
sta_puntatori" in essa vengono salvati gli indirizzi di
tutti i puntatori utilizzati. Essa viene dichiarata all'e-
sterno della classe in modo tale da essere condivisa da
tutti gli oggetti di tipo "Puntatore". La classe in que-
stione è un template e contiene un solo membro interno
che rappresenta il puntatore da gestire.
<4 OPERATORI
T *qet(){ return obj; }
T &operator*()
{ if (!obj) Asseqna( new TQ);
return *get(); >
T *operator->()
{ if (!obj) Asseqna( new TQ );
return get(); }
const Puntatore Soperator =(T *_obj)
{ return Assegna(_obj); }
operator T*()
{ return get(); } };
ttendif
L'overloading di questi operatori ci permette di uti-
lizzare le istanze degli oggetti di tipo Puntatore
come dei comuni puntatori agli oggetti con la diffe-
renza che quando tutti i riferimenti al puntatore ge-
stito verranno meno questo verrà eliminato.
public:
PuntatoreQ
{
obj = NULL;
}
Puntatore(T * _obj)
{
obj = NULL;
Assegna(_obj);
}
-PuntatoreQ
{
Assegna(NULL);
}
Trattandosi di una classe template dobbiamo imple-
mentare i metodi in modalità inline poiché non tutti
i compilatori supportano le classi template nel for-
mato "Intestazione - Sorgente".
I costruttori sono solo due uno è quello di default e
fa puntare il puntatore gestito a NULL
L'altro prende come parametro un puntatore ad un
oggetto di tipo Te lo assegna al gestore.
II codice inserito in questo passo non necessita altre
spiegazioni.
»> ILFILEPUNTATORE.CPP
ttinclude "puntatore.h"
ttinclude <stdio.h>
std::map<void *,int> lista_puntatori;
A questo punto andiamo ad aggiungere le ultime
righe di codice al file "puntatore.cpp ".
Avendo già implementato i metodi della classe
all'interno dell'intestazione ora non ci resta che di-
chiarare la mappa.
Fatto questo siamo pronti per utilizzare i nostri pun-
tatori intelligenti.
< 3 ASSEGNA E RILASCIA
const Puntatore &Assegna(T *_obj)
{ T *oldobj=Rilascia();
if (oldobj)
{ if (oldobj!=_obj) delete oldobj; }
else
{ obj = _obj; }
if (obj)
lista_puntatori[(void *)obj]+
return *this; }
T *Rilascia()
{ T *oldobj = obj;
if (obj)
{ if((lista_puntatori[(void *)obj]--)<=0)
{ lista_puntatori.erase((void *)obj); } }
obj = NULL;
return oldobj; }
Le funzioni Assegna e Rilascia rappresentano il nucleo
della classe. Attraverso la funzione "Assegna" possia-
mo creare un nuovo oggetto oppure determinare un nuo-
vo utilizzo. Ogni qual volta invochiamo il metodo Asse-
gna otteniamo il puntatore all'oggetto corrente e se
l'oggetto è nuovo cancelliamo il vecchio. Dopo questa
operazione incrementiamo il contatore Su gli accessi
all'oggetto corrente. Attraverso la funzione "Rilascia"
possiamo rilasciare il puntatore correntemente gestito e
decrementare il contatore sugli accessi, quindi se il va-
lore del contatore scende sotto lo zero lo eliminiamo dal-
la mappa.
<6> UTILIZZIAMO LA CLASSE
Puntatore<Obj> obj = new Objfl);
Puntatore<Obj> objl = obj;
cout « obj->Metodo() « endl;
cout « obj1->Metodo() « endl;
cout « (*obj).Metodo() « endl;
cout « ( (Obj*) obj )->Metodo() « endl;
Questa è una carrellata delle potenti e utili funzio-
nalità della classe appena creata.
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 111 ►
EXPRESS T
Esportazione di i
di progetto C++ "
modello
HTML
Siamo abituati a trattare
con linguaggi e program-
mi che siano dotati di esten-
sioni in grado di farli comuni-
care con altri linguaggi e pro-
grammi. Dev C++ è uno di
essi. Il C++ free di blood-
shed, infatti, è in grado di
esportare le proprie produzio-
ni in diversi formati in modo
potente e naturale. I due for-
mati previsti sono i più utili e
comuni: il famoso Rieti text
Format -RTF- come format-
tatore di testi e Hyper text
mark up language - HTML
- il noto linguaggio di marca-
tura per il web. Nell'esempio
attuale siamo interessati a
questo secondo formato. Ve-
dremo come con pochi colpi
di click sia facilmente otteni-
bile una pagina web, appunto
in formato HTML, che contie-
ne le informazioni riassuntive
sul codice sviluppato. Un uti-
lissimo strumento per chi,
oltre a produrre codice, ha la
necessità di pubblicare co-
stantemente in rete il proprio
lavoro; o comunque di man-
tenere ordinato il proprio ar-
chivio di codice sviluppato
organizzandolo come un iper-
testo. In particolare, vedremo
come sia possibile tradurre in
formato HTML un progetto
C++. Per farlo apriremo un
semplice progetto reperibile
negli esempi di cui è corre-
dato Dev C++. Il semplice
programma che visualizza a
video una stringa. Lanciamo
quindi l'applicazione e dall'a-
michevole ambiente di svi-
luppo selezioniamo dal menu
file apri progetto.
Fabio Grimaldi
<1> PER COMINCIARE APRIAMO
IL PROGETTO
H Dev-O* 4.9.9.0 - [ WinT
pile Modifica Cerca Visualizza Pr
J © si in il ei
J ::■■:: •
? ©
Progetto | Classi | Debug |
E-IJjp WinTest
] Test.c
Il progetto di esempio a cui facciamo riferimento è
Wintest, che contiene il file test.c. Esso è reperibile
nella directory di esempi allegata all'applicativo Dev-
C++. Ovviamente, per aprirlo basterà selezionare dal
menu file la voce apri progetto e individuare la car-
tella di esempio, quindi il progetto nominato.
4> SALVATAGGIO
DEL PROGETTO IN HTML
Esporta in
Salva in:
Q Documenti
Ir^UChessBase
n Immagini
Documenti
l^I Musica
[
recenti
desktop
fr^ Nuova cartella
£j)htmi
La tipica finestra di dialogo Windows ci consente di
scegliere percorso e nome del file HTML.
Diamo alla pagina web in fase di creazione il nome
progW file HTML verrà salvato nella derectory selezio-
nata.
Basterà essere muniti di un qualsiasi browser per
poter richiamare e visualizzare il risultato ottenuto.
<2> COMPILAZIONE,
ESECUZIONE E TEST
Vediamo di cosa si tratta. È il più semplice dei pro-
grammi che si possa costruire. Hello word ciò che si
tenta di visualizzare la prima volta che si esplora un
programma. Cliccando sull'apposito bottone, o sele-
zionando l'analoga voce dalla barra dei menu si com-
pila ed esegue l'intero progetto. Successivamente, si
può verificare il l'applicazione in funzione.
VISIONE DEL RISULTATO.
ECCO LA PAGINA HTML
|_| HotMail gratuita |_| Personalizzazione co... |_j Windows
|_i WindowsMedia
Project:
Index of fìles
WinTest
Filename
Teste
Lo^tion
C:\Dev-Cpp\Examples\WinTest\Test. e
Size
5 Kb
Eqxmted'by ::= V4.9.9.0
Da sistema operativo, accedendo al percorso del file e
cliccando su di esso, si può verificare il risultato otte-
nuto. Come si può notare, si tratta di una semplice
quanto utile pagina che riassume le informazioni
salienti del progetto.
Sono presenti, inoltre, due link: al software che ha
prodotto il codice e al codice stesso.
t3> ESPORTAZIONE
DELL'INTERO PROGETTO
Importa
►
|5 Esporta
in HTML
in RTF
£ Stampa
Impostazioni Stampa
Ctrl+P
Progetto in HTML
*Esci
Dal menu file optiamo per la voce esporta, e dal sotto
menu che compare scegliamo progetto in HTML.
È questa la funzione che in automatico svolge la fun-
zione che abbiamo descritto.
Adesso si tratta soltanto di specificare dove e con
quale nome salvare il progetto esportato in formato
HTML.
<6 DIAMO UN'OCCHIAIA
AL CODICE DI ESEMPIO
# include <uindous . h>
#include <stuing.h>
#include <iostream>
Thj.s is the ctlon fcr the maln wlndoi
dispatched usino- BispatchMessag-e (or sent with
■ g-ets called with the contents of t
*/
LRESULT CÀLLBACK
MainWndPuoc ( HTJOTJD hwiid,
UINT nHsg, UPARAH wPauam,
Per completare il percorso esplorativo verso questa
funzionalità di DEV-C++, clicchiamo sul nome del
file e visioniamo il codice.
Anch'esso è riformattato in HTML. Pronto a essere
copiato e incollato da altri utenti che lo consultano ad
esempio su internet. Una riprova della completezza
informativa che tale esportazione fornisce.
Si tratta di una soluzione utile per creare ad esemipo
documentazione relmativa a un progetto a cui si sta
lavorando o per rendere disponibile sul web o su un
cdrom il listato dei nostri progetti.
Anche in questo DevC++ si dimostra un editor solido
completo di tutte le funzionalità anche importanti che
spesso si ritrovano in IDE commerciali.
► 112 /Luglio-Agosto 2005
http://www.ioprogrammo.it
SPECIAL T ■ Chris Atwood
Sun Java Studio
Enterprise
Abbiamo intervistato Chris Atwood il team
manager di Sun Java Studio, a proposito
delle novità del nuovo ambiente e più in
generale su come vede lo sviluppo di Java
Ecco cosa ci ha detto
Prima di tutto com-
plimenti per l'ottimo lavoro che Sun
sta svolgendo. I progressi di Java e
dei tools che gli ruotano intorno
sono davvero notevoli. Tanto per
rompere il ghiaccio, ci parli un po'
di Java Studio Enterprise. Che cosa
è? A chi è diretto?
is Atwood: L'obiettivo di Sun Java
Studio Enterprise è di aumentare la
produttività dei
progettisti e dei
^ team di sviluppo.
Il miglioramento
della produtti-
vità risulta evi-
I dente nel ciclo di
vita proposto
dall'applicativo,
che include la
documentazione
e modellazione
del progetto, la collaborazione fra
team dislocati in diversi Paesi, lo svi-
luppo e il debugging rapido dei servi-
zi di sistema Java e infine un miglior
controllo delle prestazioni una volta
effettuato il deploy.
>: Il cuore dell'intero Java Studio
Enterprise sembra essere il "Code
Aware Collaboration", cioè il siste-
ma che consente a team distribuiti
di lavorare in collaborazione: è
così?
C.A.: La collaborazione istantanea fra
programmatori è una direzione alla
Chris Atwood
quale credo che lo sviluppo debba
tendere. Bisogna orientarsi verso
progetti distribuiti, all'interno dei
quali gli analisti lavorano a stretto
contatto con i clienti e direzionano i
loro sforzi concordandoli con il team
degli sviluppatori. La presence-awa-
reness (la possibilità di sapere se i
nostri interlocutori sono online) im-
plementata in Java Studio rende più
rapidi tali sforzi di revisione di codice
e di specifica, specialmente se unita
al servizio di collaborazione Java Sy-
stem ora disponibile su java .net.
Oltre a queste nuove funzionalità
orientate al lavoro in team, Java Stu-
dio Enterprise include strumenti
quali refactoring, portlet builder, app
server, database, portai server, access
management, e directory server.
Infine senza nessun costo aggiuntivo
è possibile accedere al supporto per
gli sviluppatori tramite i forum e le
email gestite dal personale di Sun.
Abbiamo trovato molto inte-
ressante l'integrazione con UML,
anche se UML non è ancora molto
diffuso fra gli sviluppatori meno
esperti.
Pensa che Java Studio possa colma-
re questo gap?
C.A.: Abbiamo creato Java Studio per
fornire agli analisti professionisti uno
strumento in grado di velocizzare le
proprie esigenze di design e di imple-
mentazione. E comunque forniamo
traduzione a cura di Milena Ianigro
come parte integrante di Java Studio
tutorial online, video e webchat che
possono essere utilizzati dai pro-
grammatori per migliorare la propria
conoscenza di UML. Inoltre, sia i pro-
grammatori esperti che quelli meno
esperti dovrebbero informarsi sui
Sun Developer Tech Days durante i
quali teniamo corsi di formazione e
conferenze nelle varie città.
ioP: A suo tempo Microsoft fu accu-
sata di aver plagiato Java con C# e
la piattaforma .NET. Ora invece
sembra che sia Sun a rincorrere la
facilità d'uso e la semplicità tipica
di Microsoft. Qual è la sua opinio-
ne?
C.A.: Innanzitutto devo dire che que-
sto è un ottimo momento per noi
programmatori, tutti si danno un
gran da fare per avere la nostra atten-
zione! E questa gara all'accaparra-
mento delle menti ha portato molti
sviluppatori a scegliere di stare in
mezzo: 4.5 milioni di sviluppatori (e
la cifra è in crescita) si sono orientati
verso la compatibilità e la sicurezza
offerti da Java, caratteristiche che nei
10 anni passati sono state arricchite
con un notevole insieme di API.
Grazie a questa vasta base di svilup-
patori Java già installata, un gran nu-
mero di partner e di community
stanno collaborando a strumenti che
semplificano in modo considerevole
la creazione di nuovi servizi pur pre-
servando la compatibilità dello stan-
dard Java. Un esempio di questa sem-
plificazione in evoluzione è l'engi-
neering bidirezionale che è presente
sia in Java Studio Enterprise sia in
Creator. In Creator l'applicazione è
disegnata su un canvas visivo e il co-
dice standard di Java è generato a
partire da questo modello. Allo stesso
modo, quando il codice viene modifi-
► 114/ Luglio-Agosto 2005
http://www.ioprogrammo.it
Chris Atwood ■ T SPECIAL
cato anche la parte visuale viene mo-
dificata di conseguenza. Non potreb-
be essere più semplice. Lo stesso di-
casi per i progettisti che usano Java
Studio Enterprise: il diagramma delle
classi UML è generato a partire dal
codice Java e quando il modello
viene modificato si genera anche il
nuovo codice. Ancora una volta i van-
taggi della piattaforma Java sono da
ricercare nella creazione di servizi
fruibili dai clienti indipendentemen-
te dalla piattaforma che utilizzano. Al
di là della facilità e della semplicità
d'uso degli strumenti, Java possiede
una semantica più ampia, che ha
consentito agli sviluppatori di accre-
scerne la ricchezza della piattaforma.
Uno dei cardini di questo successo è
la facilità di accesso al codice sorgen-
te, utile per velocizzare la compren-
sione delle funzionalità e sviluppar-
ne le estensioni. In qualità di princi-
pale promotore di Java, Sun incarna
una "filosofia aperta" dello sviluppo,
attraverso il Java standards process
(si veda JCP.org) e l'implementazione
- infatti è possibile già adesso reperi-
re i sorgenti di 1700 progetti su
Java.net e NetBeans.org. Credo che
Sun sia unica nella capacità e volontà
con cui incoraggia i suoi sviluppatori
a utilizzare sorgenti aperte.
Quali sono le tre migliori argo-
mentazioni che utilizzerebbe per
convincere uno sviluppatore Java a
scegliere Java Studio?
C.A.:
1 Se ha bisogno di apprendere velo-
cemente un codice, può utilizzare
in tempo reale l'engineering bidi-
rezionale UML
2 Se vuole rivedere qualche parte
del codice con un collega, può
usare le caratteristiche di collabo-
razione istantanea
3 Se vuole sviluppare del codice ra-
pidamente, può utilizzare le ca-
ratteristiche di generazione e pro-
filing interne al prodotto.
E può iniziare immediatamente,
rilassandosi mentre guarda il vi-
deo sulle caratteristiche generali,
per poi scaricare il tool gratuita-
mente ed effettuare il test
...e cosa direbbe per convincere
uno sviluppatore C# a tornare a
Java?
C.A.: Ho già detto qualcosa sulla po-
tenza degli strumenti e sulla commu-
nity che fa capo a Java. Tuttavia vedo
che molti sviluppatori sono accomu-
nati dalla voglia di creare modelli di
applicazioni ad alte prestazioni in
grado di integrarsi solidamente in
ambienti aziendali. In pratica tutti
vogliono disporre di strumenti facili
da usare e facili da apprendere e che
in seguito questi stessi strumenti sia-
no di aiuto per migliorarne la cono-
scenza e le capacità.
Poiché diventare produttivi usando
un nuovo tool richiede un grande in-
vestimento in termini di tempo, i
programmatori vogliono uno stru-
mento che sia disponibile su più si-
stemi operativi e che abbia una com-
munity forte alle spalle, che supporti
una piattaforma dinamica e che di-
sponga di una documentazione ade-
guata con un numero sufficiente di
esempi.
legati al progetto e non con quelli
derivati dall'ambiente. Con Java Stu-
dio Enterprise, i vostri strumenti e il
vostro progetto in esecuzione sono
tutti testati insieme; abbiamo incluso
anche un supporto nel caso abbiate
bisogno di assistenza professionale.
Inoltre, poiché sia Java Studio che
NetBeans si basano sulle componen-
ti standard Swing, vi renderete conto
che i vostri strumenti funzioneranno
perfettamente con il vostro sistema
operativo preferito.
Cosa vede nel futuro dello svi-
luppo di Java?
C.A.: La principale caratteristica di
Java, dovuta alla community che ne è
la base, è che la piattaforma si evolve
nelle direzioni che gli sviluppatori ri-
tengono più opportune. Per uno svi-
luppatore è di grande conforto sape-
re che la piattaforma è costantemen-
te alla ricerca di soluzioni per proble-
mi che vanno dalla computazione in
tempo reale all'integrazione con gli
ambienti business: abbiamo a dispo-
àé Ancora una volta i vantaggi
09 della piattaforma Java sono
da ricercare nella creazione
di servizi fruibili dai clienti
indipendentemente dalla piattaforma
che utilizzano ìì
In poche parole gli sviluppatori vo-
gliono un tool che li assista nello svi-
luppo del codice e che li aiuti a risol-
vere i problemi rapidamente, senza
doversi preoccupare di dove questo
codice dovrà girare e con una marca-
ta propensione per la condivisione
delle informazioni.
Potrebbe indicarci le maggiori
differ Io credo che il vero punto di
forza consista nelLaver fornito un
ricco insieme di funzionalità già
testate per lavorare insieme - mentre
stiamo progettando vogliamo dover-
ci confrontare con i soli problemi
sizione un punto di partenza sicuro
per ogni nostra necessità. Tuttavia,
vedo che i team di sviluppo distribui-
to stanno aumentando sempre di
più, nella misura in cui gli applicativi
aumentano le loro capacità. In que-
sto scenario, i progettisti model-
leranno i processi di client business,
per poi scambiare questi modelli con
il loro corrispondenti esperti di java
implementando così il servizio. Il
ripetersi di engineering bidirezionale
tra questi team specialistici miglio-
rerà la qualità dei componenti dei
servizi di applicazione creati e ri-
lasciati nel network.
http://www.ioprogrammo.it
Luglio-Agosto 2005/115 ►
SOFTWARE SUL CD T ■ Tool di sviluppo
ÌWI
SUL CD
w.ioprogrammo.it
LROGRAMMO n 93
jssnssnEnsMnEsniisnn
V8.NET
ASP.NET
FLASH
JAVA
JAVA TOP TOOLS
■ ECLIPSE 3.0.2
JDK 1.5.0.03
■ CASTOR 0.9.6
■ EHCACHE-1.1
■ PIRCBOT- 1.4.4
POCKETBUILDER
APACHE
1.3.33/2.0.54
Il Web Server più usato al mondo
Apache è certamente il web server
che conta il maggior numero di in-
stallazioni al mondo. Se un tempo la
sua fama era dovuta ad affidabilità ed
ottime performance, allo stato attua-
le pur mantenendo queste caratteri-
stiche può vantare di essere parte di
un progetto più grande portato avan-
ti dalla Apache Software foundation
e che vede l'integrazione forte di più
strumenti sotto un'unica logica. Così
ad apache web server si affiancano
moltissimo moduli che ne estendono
e ne completano le funzionalità an-
dando a contrastare quell'omogenei-
tà che da sempre è la forza degli stru-
menti analoghi portati avanti da Mi-
crosoft. Se avete bisogno di un Web
Server perfettamente integrato con
PHP e Mysql, Apache è quello che fa
per voi.
Directory: /Apache
CASTOR 0.9.6
Un tool di persistenza dei dati
leggero
Di Castor parliamo abbondantemen-
te nel bell'articolo di Massimiliano
Bigatti pubblicato in questo stesso
numero. Si tratta di un tool che con-
sente di mappare righe, colonne e
campi di database SQL in corrispon-
denti classi ed oggetti java. Una vali-
da alternativa ad Hibernate quando
si vuole usare un tool senza troppe
complicazioni e senza la complessità
di Hibernate. Certamente utile per
accellerare lo sviluppo di applicazio-
ni che fanno largo uso di DB, altret-
tanto certamente meno complesso e
per certi versi meno potente del fra-
tello maggiore: Hybernate.
Directory: /Castor
COMMONS
COLLECTIOHI 3.1
Aumenta la velocità di sviluppo
delle applicazioni Java
Le Common Collections sono un set
di API che fornisce largo supporto ai
problemi della programmazione
giornaliera, il loro scopo infatti è for-
nire un set di classi che consentono
di astrarre la programmazione ad un
livello superiore mettendo a disposi-
zione del programmatore alcuni
strumenti che sono stati in prece-
denza ricavati dal linguaggio base.
Ad esempio le Common Collection
mettono a disposizione le interfacce
Set e SortedSet che espongono meto-
di utili per lavorare con le collezioni.
Directory: /CommonCollections
COMMONS
HTTPCLIENT 3.0
Accedere al web via Java
Il protocollo HTTP è probabilmente
il protocollo più significativo usato
su Internet attualmente. Se una volta
si poteva considerare come oggetto
della programmazione il solo svilup-
po di applicazioni Standalone, at-
tualmente la tendenza è quella di svi-
luppare programmi in grado di forni-
re un'interfaccia significativa verso il
web.
Non solo ma il protocollo http è uti-
lizzato come mezzo di trasporto per
una gran mole di dati, vedi ad esem-
pio i Web Services. Il package Java
.NET fornisce funzionalità di base
per accedere a questo genere di ap-
plicazioni ma non offre una suffi-
ciente flessibilità.
Le API Jackarta Commons HttpClient
offrono un superset di clasi e metodi
che aggirano le limitazioni di Java
.NET operando in maniera tale da
rendere tutte le caratteristiche del
protocollo HTTP completamente
fruibili dal linguaggio di Sun.
Directory: /CommonHttpCIient
COMMONS
NET 1.4.0
Internet lato client sotto controllo
Le librerie Jakarta Commons Net im-
plementano il lato client di moltissi-
mi protocollo di base. Lo scopo delle
librerie è fornire un livello di astra-
zione unificato per l'accesso a qua-
lunque tipo di protocollo. L'idea è
che il programmatore debba cono-
scere un solo insieme di classi e me-
todi attraverso i quali può gestire
qualunque tipo di connessione lato
client senza per questo doverne co-
noscere i dettagli di fondo.
Directory: /CommonNet
COMMONS
ORO 2.0.8
Espressioni regolari anche in Java
Le regular expression sono una risor-
sa importante, che può risolvere in
pochissimo tempo una quantità
enorme di problemi di programma-
zione legati alla gestione del testo. Il
linguaggio principe per l'uso delle
Reegular Expression è Perl ma pro-
prio grazie all'uso delle Common-
Oro, la stessa potenza si riesce ad
ottenere con Java. Si tratta infatti di
librerie che consentono di manipola-
re il testo ad alto livello fornendo
funzioni che garantiscono una note-
vole flessibilità. In particolar modo il
supporto alle Regular Expression è
piuttosto potente.
Directory: /Corti monsOro
COMMONS VFS
Supporto universale a qualunque
tipo di File System
Ne parliamo in questo stesso numero
► 116 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Tool di sviluppo ■ T SOFTWARE SUL CD
DEVPHP
Un editor PHP che ci semplifica la vita grazie alle sofisticate funzioni che espone
Tutti sanno che è possibile
creare una pagina PHP uti-
lizzando semplicemente il no-
tepad. È altrettanto vero che
utilizzare uno strumento evo-
luto come DEVPhp aiuta mol-
tissimo nella stesura del co-
dice. Ovviamente la parte da
leone la fa la syntax hihligh-
ting ma anche il debugger, co-
me le altre facilities messe a di-
sposizione dei programmato-
ri aiutano non poco a sviluppare
pagine sintatticamente cor-
rette.
Inoltre c'è da dire che DEVPhp
è gratuito, particolare non tra-
scurabile se si pensa che per
questo linguaggio di pro-
grammazione gli editor gra-
tuiti di buon livello qualitati-
vo si contano sulle dita di una
sola mano.
Directory: /DevPHP
> CREIAMO UNO SCRIPT
> SINTASSI E FORMA
> CONTROLLO DEI RISULTATI
File Edit 5earch View Run Options Tools Window Help
New ►!
^\ Text File Ctrl+N
view | Extern
: iJjH Open Ctrl+0
[^ Reopen ►
|J§] Open Project
C^ Reopen Project File ►
Reload current File
HTML Document
PHP Script
1
(2j Save Ctrl+S
File Format ►
jyf 5ave Project
[g] SaveAs...
Gj2j Save Ali
£H rln^e TM+F4
'■■.' Puri ■jp.fiorii rocik- ■.Viridi:".-' Heln
L:M :-i -,r;,n:ìr'i »e> C f:<r.;,i =\ ; ,o rv»;nr>>-;r>t jlmn '.«:' "n Pi
:5nippet:
v-PHP Code Completion
function aco? (fio
- , > |i| t i-|
Jiìctoti addcslashes (sì ing ìstr. string tcharlist]
mi il U 1 'ì | 'il ii i h
(uncìion apai;he_cNld_temiinale (void)
function apacheji.itikup_i.jri (stfing S'ilen-arnei
fi. ic ici apache_note (stnng In-» :_r.;in: | strini
l'iicuii 3i>acht_ sleu I mhj snabl? hn»_;
function arrav ([rniKed $...])
function srrai. change key ca?e tara;,' tinput [.
li ! I»» -i 3"-_ l.i mi lai.-.» (it ii 1 B i i • I bi
□ -Q
>Kl h^tBsBBBa^a BlQlINSIBaaa^BBJi
«
©©SU toiho * fo ^
^
_j total.»*
C5S.ini
devphp.ini
J5.in,
SS, 6 " ™" 80 *'
2S=^SSSS? _gg^
Iniziamo creando un nuovo script
Idal menu "file/new/php script".
Verrà creato un file "Untitledl.php" che
successivamente potremo salvare in un
punto qualunque dell'hard disk e rinomi-
narlo.
I Nella creazione dello script si noti la
Isintax highliting del codice e la co-
de complexion attivabile premendo con-
temporaneamente i tasti etri e barra spa-
ziatrice, queste due funzioni aiutano enor-
memente il programmatore.
J Abbiamo scelto di visualizzare la
Ipreview del risultato direttamente
nel browser digitando l'indirizzo nella
barra e utilizzando un nostro web server.
In alternativa è possibile usare una preview
interna.
nel bell'articolo di Federico Paparo-
ni. Si tratta di librerie che astraggono
il programmatore dal dover conosce-
re i dettagli di implementazione di
un File System, in particolare con-
sentono di trattare file system remoti
come se fossero locali alla nostra
macchina.
Nell'articolo di Federico Paparoni si
spiega come sfruttare queste caratte-
ristiche delle librerie Common VFS
per utilizzare internet come un gran-
de Hard Disk virtuale da sincronizza-
re con le risorse locali al nostro com-
puter.
Directory: /CommonsVFS
DBAMGR 2K
Il gestore di database MSDE
Completamente italiano questo pro-
getto e precisamente di Andrea Mon-
tanari, ma molto apprezzato anche
all'estero si tratta di una console am-
ministrativa alternativa per Micro-
soft MSDE 1.0 e MSDE 2000 scritta in
Microsoft Visual Basic 6.0.
Si tratta di uno dei pochi strumenti
disponibili per la gestione visuale dei
fratelli minori di SQL Server che per
loro natura non sono dotati di una
console amministrativa.
jjj
Offre funzionalità del tipo: gestione
della sicurezza a livello di database,
tabelle e vista.
La gestione delle tabelle supporta
l'inserimento di nuovi campi e molto
altro ancora.
Certo non aspettatevi di potere usare
i diagrammi, ma sicuramente si trat-
ta di uno strumento molto interes-
sante se intendete avvalervi di MSDE
piuttosto che di MS SQL Server.
Directory: /DBAmgr2K
DEV C++ 4.9.9.2
Il più amato dai
programmatori C++
Uno degli IDE più utilizzato da chi
programma in C++. In questo nume-
ro lo abbiamo usato più di una volta,
sia per realizzare alcuni degli express
sia nell'articolo relativo alle WX
Widgets se pure in una forma estesa
grazie all'uso del package per la pro-
grammazione delle WX.
gg □ □ gg ^ ?i)° Ru " g- L.H*»
È dotato di Syntax Highlighting, code
completion, in alcuni casi è possibile
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 117 ►
SOFTWARE SUL CD T ■ Tool di sviluppo
ECPLIPSE SDK 3.0.2
L'IDE di programmazione universale
Eclipse è diventato in breve
tempo uno dei prodotti più
usati dai programmatori di tut-
to il mondo. Si tratta ovviamente
di un ambiente di programma-
zione. La prima caratteristica da
mettere in evidenza è che l'intero
eclipse è scritto in Java e que-
sto lo rende disponibile con fun-
zionalità omogenee sia su Linux
che su Windows. La seconda ca-
ratteristica è relativa alla sua
architettura a plugin. Se da un la-
to l'ambiente si presenta di de-
fault come destinato alla pro-
grammazione Java, con sempli-
ci plugin diventa un IDE per PHP,
per C++ o per praticamente qua-
lunque altro linguaggio dispo-
nibile. Inoltre sempre grazie al-
la sua architettura a plugin, è
possibile aggiungere funzio-
nalità aggiuntive che lo rendo-
no ad esempio un ambiente RAD
e Visual nel caso si installi il plu-
gin Visual Eclipse. Un must as-
solutamente da provare.
Directory: /Eclipse
> UN NUOVO PROGETTO
> DIAMOGLI UN NOME
> LE LIBRERIE
* ^.^ a™ ~~.<*OB1 uaiii * -»«.»» o
-'' 9 ìjtj>( »- O- *■ w » ©• » • 55 «'i™
'# * 9'TTT^
T*?™^
*— !
.SpU,
B
RnwmpmOKTCiamBfl .™„»,„»
-n
•^■«tt- ^.*;IH*
4» w u™,. 1S.!!^I
8 Un proi.fi foMw « resi far seurt»!
'***-"
.™ SiM .™^.4i^i - v -.fiB#
«e «,»„.
3SS
:
-
liZSSZXJZZÌZZ
«iW.
-a
* » *• ■:■ ■;-
* ■
Iniziamo dal menu file, selezionando
I File/New/Project apparirà il wizard
per la creazione di una nuova applicazio-
ne Java. Selezioniamo "Java Project" e an-
diamo avanti con Next.
J Diamo un nome al progetto e sce-
gliamo inoltre se desideriamo che
venga creato all'interno dello spazio di de-
fault usato da Eclipse, oppure in una di-
rectory di nostra scelta.
I Dalla Tabsheet "Libraries" sceglia-
Imo le eventuali librerie da aggiun-
gere all'applicazione. È possibile aggiun-
gere dei Jar esterni come delle librerie in-
cluse nel J2SE
addirittura utilizzarlo come stru-
mento di programmazione RAD e Vi-
suale.
Directory /DevCPP
ENCACHE 1.1
Una cache per le applicazioni Java
Di Encache parliamo approfondita-
mente in questo stesso numero nel-
l'articolo del nostro Daniele de Mi-
chelis. Encache è una libreria java
che consente di inserire funzionalità
di caching all'interno di un'applica-
zione. Il meccanismo come potrete
intuire è piuttosto interessante, per-
ché consente di velocizzare in modo
notevole i vostri programmi. Imma-
ginate per esempio tutte le applica-
zioni che accedono in una qualche
misura a dati su ub DB. Un meccani-
smo di cache consente di evitare l'ac-
cesso continuo al database magari
remoto per spostarlo invece su una
cache locale, ovviamente questo
comporta dei problemi nella diffi-
coltà di implementazione del mecca-
nismo di cache e nelle scelte proget-
tuali da compiere.
Ogni problema viene risolto da enca-
che che consente di occuparsi della
parte più strettamente programmati-
va senza doversi dedicare alla gestio-
ne della cache.
Directory: /encache
IRRLICHT 0.10
La libreria per lo sviluppo rapido
di VideoGames
Ad irrlicht dedichiamo costantemen-
te spazio sulla nostra testata.
Si tratta di una libreria veramente
molto efficace per lo sviluppo di vi-
deogiochi.
Consente di accedere facilmente a
funzionalità per la gestione del 3D,
per l'implementazione dei parametri
d'animazione, per l'applicazione
delle texture, per la generazione di
effetti complessi.
Si tratta insomma di una libreria de-
cisamente utile per chi vuole iniziare
a programmare da zero i propri vi-
deogiochi come per chi invece ha
bisogno di funzionalità particolar-
mente avanzate.
Quella che vi presentiamo è la versio-
ne 0.10, rilasciata da appena un mese
e già ai vertici per quanto riguarda la
diffusione
Directory /irrlicht
LAZARUS 0.9.6
Quasi come Delphi ma Gratis
L'OpenSource è probabilmente la
più grande rivoluzione dei nostri
tempi. Non c'è programma che non
abbia un suo equivalente di tipo
OpenSource, così Lazarus è il clone
OpenSource di Delphi.
Gira tranquillamente sia sotto Win-
dows che sotto Linux ed assomiglia
in tutto e per tutto alle versioni di
► 118 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Librerie e Tool di sviluppo ■ T SOFTWARE SUL CD
Delphi fino alla serie 3. Certo non ha
la complessità del Delphi 2005 enter-
prise edition ma non ne ha neanche
la pesantezza.
Consente di sviluppare applicazioni
standalone complete in piena filoso-
fia Visuale e Rad com'è tipico di Del-
phi che è stato il primo ambiente a
proporsi come IDE completamente
Visuale e RAD e ancora oggi offre
molto più di altri ambienti di pro-
grammazione in quanto a sviluppo
rapido di applicazioni
Directory: /Lazarus
MYSQL 4.11/5.0.4
Il principe dei database
È innegabile, la coppia MySQL+PHP è
la spina dorsale della maggior parte
delle Web Application oggi disponibi-
le sul web. MySQL in questa coppia
svolge il ruolo di database e lo fa nella
maniera migliore possibile.
La sua caratteristica principale è
quella di essere molto veloce, le sue
altre caratteristiche sono quelle di
supportare le transazioni, la ricerca
full text, la sicurezza fino al livello di
singola cella. Offre inoltre costrutti
SQL estremamente funzionali che in
qualche caso superano di gran lunga
quelli offerti dal concorrente Micro-
soft Sql Server. Date un'occhiata al-
l'articolo di Gianluca Negrelli ad
esempio sulla paginazione su milioni
di record e scoprirete come la presen-
za del costrutto "LIMIT" in MySQL ri-
solve brillantemente l'intero proble-
ma che invece è così pressante in MS
Sql Server.
Directory: /MySQL
J2SE 1.5.0 03
L'sdk essenziale per programmare
in Java
Se siete dei programmatori Java, sicu-
ramente non potete farne a meno.
L'SDK contiene la base per la pro-
grammazione nel linguaggio di Sun.
Questo Upgrade 0.3 contiene alcune
importanti modifiche al compilatore
che risolvono diversi Bug della ver-
sione precedente. In particolare evi-
tano il crash dell'applicazione sotto
particolari condizioni, altre impor-
tanti modifiche sono state apportate
ad AWT e a Swing.
Directory: /J2SE
JAKARTA SLIDE
WEBDAVCLIENT
Le librerie Java per il supporto
a WebDav
Il concetto dietro cui si basano queste
librerie è particolarmente astratto,
ma molto utile. In sostanza si tratta di
un framework che consente un orga-
nizzazione gerarchica dei file che
compongono un'applicazione, intesi
come file che possono essere distri-
buiti su File System eterogenei. Oltre
a questo tipo di caratteristiche la
libreria integra anche funzioni per la
sicurezza il versioning e altri servizi
utili
Directory: /jakartaslide
JCIFS
Samba e CIFS in un un'unica
libreria
Samba è ormai un protocollo diffuso
e affidabile. Se prima era solo Linux
ad utilizzarlo per la condivisione del-
e risorse locali, adesso anche Mac
OSX utilizza samba come principale
protocollo per accedere a cartelle in
rete locale e questo ne ha aumentato
esponenzialmente la diffusione.
Questa libreria implementa il proto-
collo Samba al proprio interno e con-
sente di scrivere applicazioni java che
utilizzino questo ormai diffuso meto-
do per l'accesso a file condivisi in rete
Directory: /Jcifs
WX DEV C++
Usiamo una particolare versione di Dev C++ per programmare interfacce in modo visuale e Rad
Directory: /wx-devcpp
> UN NUOVO PROGETTO
> SCEGLIAMO IL MODELLO > DISEGNIAMO
J Clicchiamo su file/nuovo/progetto
per iniziare una nuova applicazione.
Il winzard successivo ci consentirà di sce-
gliere un modello da cui iniziare.
Fra i tanti disponibili scegliamo di
Icreare un'applicazione Windows usan-
do C#. Lo scheletro che ci verrà proposto cor-
risponderà alla base di una form vuota.
i a & h u x^cxi'oeM/'/Bir^ ^ *■- a m ► si^uo
MainForm.«|
aumentatone C#
KfHB^H 4,,1 1
#develop
XSL-T
Generale
Appunti
Windows Forrns
Data
Components
Custom Componente
* Pointer
Dalla tabsheet in basso scegliamo
"Disegna". Ci apparirà la form su cui
trascinare i controlli che riempiono la bar-
ra sinistra del progetto.
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 119 ►
SOFTWARE SUL CD T
Librerie e Tool di sviluppo
PHP 4.3.11 - 5.0.4
Il linguaggio di internet
Se seguite ioProgrammo o più sem-
plicemente siete dei programmatori
Web, o ancora molto più semplice-
mente navigate su Internet, non po-
tete non sapere che cosa è PHP. Si
tratta del linguaggio con il quale
sono sviluppate la maggior parte
delle applicazioni internet esistenti.
Quasi tutto il software per il web si
regge su PHP.
La curva di apprendimento è bassis-
sima, le funzionalità esposte eleva-
tissime, certamente se avete inten-
zione di sviluppare per il web non
potrete fare a meno di provare anche
questo linguaggio come base per le
vostre applicazione
Directory: /PHP
ULTIMATE ++
L'IDE più innovativo per C++
Se DEV C++ rappresenta ormai uno
standard per i programmatori C++,
Ultimate ++ si sta dimostrando una
ottima alternativa. Leggero, veloce,
dotato di alcune estensioni che lo
rendono in parte RAD, viene distri-
buito con il compilatore MinGW.
mare le proprie applicazioni utiliz-
zando C#, SharpDevelop sembra es-
sere Tunica alternativa.
È dotato naturalmente di tutte le
caratteristiche di un buon IDE, tutta-
via dispone di un'organizzazione che
lascia rapidamente intendere che da
questo ambiente dovremo attenderci
delle grosse novità nel futuro, sia in
termini di prestazioni che di comple-
tezza.
Directory: /Ultimatepp
SHARPDEVELOP
1.0.3
L'ambiente alternativo
per la programmazione C#
Per chi non vuole affrontare i costi di
Visual Studio.NET, per chi comunque
vuole disporre di un ambiente abba-
stanza leggero per potere program-
Da poco è possibile utilizzarlo anche
per programmare inVB.NET, tuttavia
la sua caratterizzazione è rivolta pre-
valentemente a C#. Si tratta di un
prodotto ormai consolidato, rapido,
visuale, economico e semplice da
usare
Directory: /SharpDevelop
PYTHON 2.4.1.
Il linguaggio emergente
Un linguaggio di scripting, multipiat-
taforma, orientato agli oggetti. Tre
caratteristiche che rendono questo
linguaggio piuttosto appetibile. Non
per niente tutte le classifiche mon-
diali sulla diffusione dei linguaggi di
programmazione lo individuano co-
me quello maggiormente in ascesa.
Python è elegante, estensibile, con
una curva di apprendimento non
elevatissima.
Viene utilizzato per programmare
moltissimi tool di gestione dei siste-
mi Unix, ma sta trovando una rapida
applicazione anche sui sistemi Win-
dows e nello sviluppo di applicazioni
crossplatform.
Directory: /Python
THIHIG 0.1
L'editor di Thinlet
Thinlet è una libreria java che con-
sente di creare interfacce grafiche
utilizzando XML. Le interfacce così
create vengono parserizzate dalla
libreria e producono in output l'ap-
plicazione corretta.
Thing è un editor XML per lo svilup-
po rapido è visuale di interfacce
compatibili con Thinlet. Si tratta di
un editor scritto in java, perciò per-
fettamente funzionante sia con Li-
nux che con Windows.
Ha dalla sua una certa pesantezza
nell'esecuzione ma una volta presa
confidenza con la logica di funziona-
mento l'accoppiata Thing +Thinlet si
mostra decisamente potente e garan-
tisce un abbattimento notevole dei
tempi di sviluppo.
Directory: /Thing
TOMCAT 5.5.9
L'application server per JSP
Se siete dei programmatori JSP avete
senza dubbio bisogno di Tomcat per
testare e utilizzare le vostre applica-
zioni. È senza dubbio un server web
ovvero può funzionare da server web
ma è soprattutto un Application Ser-
ver che vi consente di utilizzare la
tecnica delle servlet o delle JSP per
creare applicazioni Web con una
logica business e con il linguaggio
Java a fare da asse portante.
Directory: /Tomcat
PIRCBOT
L'ire programmabile in Java
Ne parliamo nell'articolo di Federico
Paparoni. Si tratta di un framework
per la programmazione di ire bot.
Chi ha dimestichezza con la rete ire
sa già cosa sono gli eggdrop e come
funzionano. Piccoli software in grado
di reagire ad eventi che si svolgono
all'interno di una chat e di "governa-
re" un canale sulla base della volontà
del suo moderatore.
Mentre Eggdrop è un bot già pro-
grammato, sviluppato in ogni sua
parte anche se estendibile in TCL,
Pircbot viceversa è un'insieme di
classi Java che consentono di realiz-
zare facilmente e in autonomia un
proprio bot dotato di comandi, com-
portamenti e funzioni completa-
mente aderenti alla volontà di chi lo
realizza.
Per chi ama la rete IRC si tratta vera-
► 120 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Librerie e Tool di sviluppo ■ T SOFTWARE SUL CD
mente di una "perla" da provare asso-
lutamente.
Directory: /Pircbot
JAKARTA
COMMONS LOGGIIUG
Le api essenziali per il Debugging
I log svolgono un'attività essenziale
nella manutenzione di un'applica-
zione sia in fase di test che in fase di
rilascio. Solo attraverso ai log si rie-
sce ad ottenere un quadro preciso di
quali eventi hanno portato a un pro-
blema nell'utilizzo di un determinato
software. La Jakarta Commons Log-
gin offre funzionalità di alto livello
per la gestione dei log di un'applica-
zione, consente di creare nuovi for-
mati di Log come di riutilizzare quel-
li già esposti in altri componenti co-
me HttpClient o DBCP.
Directory: /logging
SHIARLI
La libreria Java per lo sviluppo di
reti neurali
Sogno da sempre di ogni appassiona-
to di programmazione e di tecnolo-
gia in generale è quello della creazio-
ne di una generazione di robot pen-
santi. Sogno ambizioso e non privo
di preoccupazioni e rischi, ma pur
sempre affascinante. In questo nu-
mero di ioProgrammo, ci conduce
nell'affascinante mondo della co-
struzione di reti neurali.
Una rete neurale è una simulazione
dell'intricata rete di relazioni che
compone il cervello umano e che tra-
mite l'applicazione si stimoli senso-
riali produce il comportamento in-
telligente. Strumento pratico per im-
plementare quanto descritto da An-
drea Galeazzi è Snarli: una libreria Ja-
va sviluppata da Simon D. Levy del
dipartimento di informatica dell'uni-
versità di Washinghton e Lee. Snarli
mette a disposizione una serie di
metodi che aiutano il programmato-
re a creare e addestrare reti neurali
simulate.
Directory: /Snarli
XERCES 1.4.4
II parser XML per Java e C+
Xerces è una libreria Java che forni-
sce una serie di classi per il parsing e
la generazione di file XML. Il parser è
disponibile sia per Java che per c++,
implementa gli standard del W3C
Xml e i livelli uno e 2 del DOM, inol-
tre supporta SAX nella sua versione
2.Xerces può essere utilizzato con dei
wrapper per funzionare ad esempio
anche in peri e infine esiste un wrap-
per che rende Xerces compatibile
con il parser MSXML
Directory: /Xerces
XALAN
Il processore XSLT per Java e C++
Alcuni di voi avranno sentito parlare
di XSLT che consente di trasformare
un documento in XML in un interfac-
cia Html, oppure di XPath il linguag-
gio che consente di ricercare dati
all'interno di un documento XML.
L'unione delle due tecniche conduce
spesso ad effetti sorprendenti. Xalan
è un'implementazione java di un
parser XSLT. Implementa gli stan-
dard W3C XSLT e usa il Bean Scrip-
ting Framework per fornire estensio-
ni Java ed Sql.
Directory: /xalan
OPENRPT 1.1.1
BETA WIN32
Report per tutti i gusti
OpenReport è un sistema di reporti-
stica completa ed OpenSource dotato
di interessanti caratteristiche.
Linux, Mac OS X, Xbsd, Solaris, Aix,
HPUX.
i; Rie Sdii Insw B«iiinei» Wntfcws Help
J.
l&O 3S Pt « tt # ' r T / HI □ fe
Rcporl Hcdcr
Pack mg List
CUStHTK
5ch~
BiUTo:
Attentici
: Fonnsffi |jef9
1 <* 1
«jbarc
Cjuety Soukk | head j_
Cancel |
GalumK |cohfiad_£iratpofimh
Maximum Lengih pf Vfllue
, i-ptonameJiead lìeld
inead.shiptoaddressLhead Eìetd
ihead_shiptoa<3diesi2:h£ad fìeld
Jhead_shiptùaddresi3:head fleld
iiptorih -,t,-.ti-?ip;head.field . .
rtiead. shipTOphonerhead field
Aliyninen!
* Left
r Center
rRight
dei ili Del ali
St|ÌF WfcMi (i^
Y-. |L4 Heighf |(VE
«d : a**» AtMinhj!:: : B**td :
L__
Ittmjjm:
ipl: cani New
Desideriamo presentarvelo nono-
stante si tratti ancora di una beta pro-
prio perché le sue potenzialità sono
davvero interessanti. Si tratta di un
sistema WYSIWYG che salva la defini-
zione del report in formato XML Già
da sole queste due caratteristiche ba-
sterebbero da sole a rendere il siste-
ma particolarmente interessante, a
tutto questo c'è da aggiungere che
OpenRpt gestisce molti tipi di codice
a Barre ed è multipiattaforma, fun-
ziona tranquillamente su Windows,
D r, K! Jwr: ajomrt ■....r*,. ! h* Q!
!'-. ti U
s sub: #i« :%* t sma%)
a
. Custoi
... Si
Bill T(
SIO » coh'esd_nùr ' w«d.™-i»j»
cohefld_9hi|3ici":o "■'.:! -fai'', ' ■:■ d
cchead_sliiptoaddress1.lìeaiJ (iald
: :■ > : 5' :■ i i:t: .1 j : n&ss: 3 ì-t-.i j fi? e
ìhrptDcityslateiif>:head 1ietd
cohead_3hipì-=p:-or? r-.-K\ l.sr:
a-,»™ M ^| M |
(fehu.l^hrfVttj.
, ,D »
: Attènl
OCm»
-—
".SENESI
«nMd .. EMw ****** P»w
it«iTL_dwriBijiiMiifl»si: . : : . : . .
rt«njd«*ri|s!.!lMasl fieli
hm.Di.i4n>.« *.D»uutu4
Unico neo per questo sistema è che il
supporto nativo ai Db è presente solo
per postregreSQL tuttavia con poche
modifiche si può rendere compatibile
con DB2, Oracle, SQL Server, MySQL.
Directory: /openrpt
DEV C++ PACK
Tre package per estendere
Dev C++
Dev C++, il noto ambiente che pre-
sentiamo spessissimo su ioProgram-
mo è estensibile tramite plugin o
package, ve ne presentiamo tre, due
dei quali fanno diventare Dev C++
RAD e visuale tale che possa essere
usati con la libreria WxWidgets che
presentiamo in questo stesso nume-
ro. Il terzo ancora da utilizzare con le
wxWidgets fornisce alcune caratteri-
stiche per la gestione delle immagini.
Nel complesso tre strumenti fonda-
mentali da usare per programmare in
C++ usando le librerie universali
wxWidgets.
Directory: /devpack
WXDEVCPP
un Dev C++ potenziato
Un'installazione semplificata che vi
consenta di usare da subito Dev C++
con le estensioni dedicate alle wx-
Widgets tali che lo rendono un am-
biente rad e pronto per essere usato
per la produzione di applicazioni
multipiattaforma. Ovviamente que-
sta installazione funziona in ambien-
te Windows. Ma Dev C++ così confi-
gurato rappresenta una straordinaria
alternativa a sistemi da costi com-
merciali molto più elevati e non per
questo dotati di caratteristiche molto
più evolute.
Directory: /wxWidgets
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 121 ►
PRODOTTI T ■ Pocket Builder
Pocket Builder
Arriva da Sybase una soluzione per lo sviluppo RAD di applicazioni
dedicate al mondo Mobile. La programmazione per Pocket PC diventa
rapida e immediata, le applicazioni Wireless a portata di mano
Pocket Builder è l'ambiente di svi-
luppo RAD proposto da Sybase per
la programmazione di sistemi mobili
Basati su Microsoft Pocket PC 2002 e
Windows Mobile 2003. Le caratteristi-
che sono piuttosto interessanti. Intanto
si tratta di un ambiente di programma-
zione RAD, perciò l'intento è quello di
consentire a chiunque non abbia un
livello alto di esperienza sulla program-
mazione di dispositivi mobili, comun-
que, di creare una propria applicazione
in tempi ragionevolmente brevi. L'idea
è quella di usare una programmazione
basata sul Drag & Drop degli oggetti
piuttosto che sulla reale implementa-
zione del codice, tecnica che nel tempo
si è dimostrata particolarmente valida.
Il secondo punto a favore di Pocket
Builder deriva dall'innata caratteristica
di Sybase di creare software orientato
alla gestione dei dati. Pocket Builder
non sfugge a questa regola, e dispone di
estensioni tali che lo rendono partico-
larmente indicato per sviluppare appli-
cazioni per dispositivi mobili che fac-
ciano uso di database. In particolare è
disponibile nella stessa suite il prodotto
SQL AnyWhere Studio che è sicuramen-
te un supporto di grande validità allo
sviluppo di software DB Oriented. Esi-
stono diversi Wizard che consentono di
sviluppare velocemente applicazioni di
database, in particolare il MobiLink
consente con pochi passi di ottenere
applicazioni completamente funzio-
nanti.
APPLICAZIONI
PRONTE
PER LA VOCE
Da un tool nato per creare software
destinato al mobile non ci si poteva non
aspettare tutta una serie di estensioni
dedicate all'uso della voce o comunque
a modi di comunicare nuovi quali ad
esempio gli SMS .Questo tipo di servizi
sono facilmente implementabili trami-
te Pocket Builder rendendolo partico-
larmente adatto alla creazione di pro-
dotti che integrano in un'unica soluzio-
ne tutte le estensioni multimediali pos-
sibili. A questa logica non sfuggono l'in-
tegrazione con le camere digitali che da
qualche tempo sono accessorio indi-
spensabile per ogni smartphone che si
rispetti. Infine sono previste estensioni
per la gestione del GPS e dello Scanner.
ALTRE
CARATTERISTICHE
Pocket builder supporta direttamente
la stampa, e integra la possibilità di
reportistica accurata partendo dalla
vostra applicazione. Si integra facil-
mente con le applicazioni già esistenti
come il Calendario, o la gestione degli
appuntamenti e dei task. Infine è pron-
to per la connettitività wireless. La sicu-
rezza viene gestita tramite la possibilità
di firmare il propiro codice con un cer-
tificato digitale.
CONCLUSIONI
Pocket Builder rientra in quella fascia di
IDE RAD dedicati alla programmazione
di nuova generazione che vede non più
le applicazioni standalone come target
dello sviluppo, supera abbondante-
mente la programmazione web e si
dedica direttamente a quel settore che
da molti analisti è definito come la nuo-
va frontiera dello sviluppo, ovvero il
mobile. I suoi punti di forza sono prima
di tutto, relativi alla sua natura di am-
biente RAD e in secondo luogo all'alta
integrazione con la gestione dei dati
derivante direttamente da un marchio
che in questo campo offre delle garan-
zie certe: Sybase. Se siete interessati a
programmare applicazioni per il mobi-
le dovete almeno provarlo.
PocketBuilder
Produttore: Sybase
Sul web: www.sybase.com
/developmentintegration/pocketbuilder
ta
4
► 122 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Fox Micro Linux H T PRODOTTI
Fox Micro Linux System
In 66x72mm un condensato ad alta tecnologia. Connessione
ethernet, sistema operativo linux, due porte USB f seriali, Scsi e 19
ingressi/uscite general purpose. Gli manca solo la parola... forse
1 sistemi embedded si stanno sempre
più diffondendo. Si tratta di piccole
macchine per lo più dedicate a compiti
specifici utili ad esempio in sistemi di
automazione industriale. Fino a qual-
che tempo fa il leader in questo settore
era il mitico microcontrollore Picl6f84,
da qualche tempo alla famiglia dei Pie
si è affiancata una nutrita schiera di
device, dei veri e propri PC dotati di
sistema operativo e di tutte le caratteri-
stiche di un computer vero e proprio
ma con dimensioni tali da renderli
appetibili come sistema embedded.
LA DOTAZIONE
Il cuore della scheda è un processore
Axis ETRAX 100LX 32 bit, RISC, 100
MHz, la dotazione di memoria è di 16
Mb ram con 4mb flash. L'accessoristica
è di tutto rispetto, due porte USB 1.1,
una scheda ethernet 10/100, il resto è
lasciato alla vostra fantasia. I due slot da
40 piedini contengono il necessario per
implementare 3 seriali, 2 Bus IDE, 2 Bus
Scsi, un'interfaccia parallela e un nutri-
to numero di entrate uscite general pur-
pose. Que-
sta dotazione
rende la Fox Micro
linux Systems partico-
larmente appetitosa come
soluzione dedicata alla costru-
zione di sistemi embedded. Dal pun-
to di vista software, il sistema operativo
è pilotato dalla versione 2.6 del kernel
di Linux. Nella busybox sono presente
tutte le componenti essenziali per fun-
zionare immediatamente. È già confi-
gurato un server Web un server Ftp, il
servizio telnet, ssh.
COME FUNZIONA?
È sufficiente alimentare il sistema e at-
taccarlo alla propria Lan per vederlo
2 x USB 1.1
POWER SU PPLV{+5tfCCJ
RESET JUMPER
LAWBÙÙTJlJMPÉR
CONSOLE PORT [3.3v le veli
USER DEFI NED SWITCH
POWER OSI! GREEN)
LAN ACTlViTY (VÉLLOW)
STATUS (RED)
Fig. 1: Uno schema generico delle possibili connessioni offerte dalla piedinatura del FOX
funzionare. Ovvia-
mente è necessario
connettersi con uno dei
sistemi sopraindicati per
poter effettuare configurazioni
personalizzate come ad esempio il
cambio dell'indirizzo IP. Inizialmente il
sistema è configurato sull'indirizzo
192.168.0.90. Accedendo via interfaccia
web è possibile variare i parametri
direttamente dal browser. La program-
mazione avviene per mezzo di un SDK
disponibile sul sito http:/ /www .acmesy-
stems.it. È possibile programmare diret-
tamente in C e poi effettuare il deploy
dell'applicazione verso la scheda.
Una prima occhiata agli esempi di codi-
ce allegati all'SDK mostra come pro-
grammare questo genere di apparecchi
sia incredibilmente più semplice che
imparare l'assembler del PIC.
CONCLUSIONI
Si tratta di una soluzione estremamente
efficace in tutte quelle condizioni dove
programmare un PIC risulta scomodo e
difficoltoso. Il costo dell'apparecchio si
aggira intorno ai 138 euro, ma non ci
sono costi software da sostenere, visto
che l'sdk è gratuito e la programmazio-
ne può avvenire direttamente da un si-
stema Linux.
Infine poter programmare partendo da
un linguaggio ad alto livello come il C
sicuramente offre dei vantaggi non in-
differenti in termini di costo dello svi-
luppo.
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 123 ►
SOLUZIONI T ■ Processi concorrenti
Processi concorrenti
con i semafori
Che cosa succede quando due applicazioni tentano di accedere
alla medesima risorsa? I semafori introdotti da Dijkstra forniscono una
soluzione e rappresentano un ottimo punto di partenza per il problema
Wj- REQUISITI
Pfjl Basidi
L-y programmazione
^^^a_j _j
Tempo di realizzazione
Al metodico cammino che ci sta portando al-
la comprensione della programmazione
concorrente si aggiunge un nuovo passo: i
semafori. Ricordo che perché due o più processi
cooperino, ossia possano essere eseguiti in modo
concorrente è necessario che venga correttamente
gestita una porzione di codice condiviso. In tale re-
gione chiamata sezione critica ogni processo condi-
vide delle variabili. Quando un processo elabora ed
utilizza tale regione non bisogna permettere l'ac-
cesso agli altri processi. Si parla di mutua esclusio-
ne. I metodi di gestione della mutua esclusione visti
nello scorso appuntamento risultano macchinosi e
poco funzionali nel caso di problemi complessi.
Una svolta alla risoluzione di tale problema è stata
apportata da Dijkstra che ha introdotto nuovi stru-
menti di sincronizzazione tra processi: i semafori.
SEMAFORI
Un semaforo è una variabile S che eccetto la fase di
inizializzazione è manipolata da due sole operazio-
ni atomiche: P e V. Esaminiamo le definizioni delle
due funzioni.
P(S):
while (S<=0) do;
S:=S+1;
V(S):
S:=S+1
Cosa si intende per atomiche? Che le operazioni
svolte nelle due funzioni presentate non possono
essere interrotte e devono essere eseguite in modo
indivisibile. Nel caso di P non si possono interrom-
pere le due istruzioni con altre. Se un processo
modifica il valore del semaforo, in quel intervallo di
tempo nessun altro processo può modificare lo
stesso semaforo. Osserviamo adesso come questo
nuovo costrutto sia usato nella gestione delle sezio-
ni critiche. Poi esamineremo nei particolari l'imple-
mentazione. Si suppone che n processi con-
dividano un semaforo di nome mutex, che ha valo-
re iniziale pari a 1. Ogni processo gestisce la sezione
critica attuando il ciclo infinito
proposto
di
seguito:
repeat
P(mutex)
<regione critica>
V(mutex)
<codice rimanente>
until false
Fig. 1: Grafo di precedenze per l'esecuzione di sette
istruzioni
Si tratta di una sequenza conosciuta. Questa volta la
sezione critica è protetta dalla presenza di un
semaforo che impedisce al processo di entrare se
mutex è occupato. Una volta terminato lo sfrutta-
mento della regione la si libera con V. Esaminiamo
altri esempi per i quali i semafori sono utilizzati con
profitto. Consideriamo il caso in cui si abbiano due
processi PI e P2 eseguono rispettivamente due
istruzioni SI e S2. Supponiamo di avere come vin-
colo che l'esecuzione di S2 debba avvenire soltanto
dopo che SI sia completamente terminata. Il pro-
cesso può essere sincronizzato mediante l'uso di un
semaforo. Sia sincr il semaforo citato con valore ini-
ziale 0, la soluzione si otterrà facilmente modifi-
cando di poco i due processi PI e P2.
(* Pi *)
► 126 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Processi concorrenti ■ T SOLUZIONI
SI;
V(sincr);
(* P2 *)
P(sincr);
S2;
Poiché sincr è inizializzato a 0, P2 eseguirà il suo sta-
tement S2 soltanto dopo che verrà superata la fun-
zione P, ossia quando PI invocherà V(sincr), quindi
dopo SI. Ricordo che la V incrementa la variabile S,
cosicché per P, la condizione S<-0 risulterà falsa.
Si tratta di un caso più complicato ma che possiamo
opportunamente risolvere adottando i costrutti
cobegin e coend. Ecco come:
var sema,semb, seme, semd,seme, semf,
semg: semaforo;
< Inizializzazione dei semafori>
begin
cobegin
begin SI; V(sema); V(semb) end;
begin P(sema); S2; V(semc); V(semd); end;
begin P(semb); S3; S4; V(seme) end;
begin P(semc) S5; V(semf) end;
begin P(semd) S6; V(semg) end;
begin P(semf); P(semg); P(seme); S7end;
coend;
end;
Ogni qual volta vi sono due istruzioni eseguibili
concorrentemente si lanciano due V, che in due di-
stinte istruzioni vengono raccolte dalle funzioni P
La realizzazione del grafo con questo nuovo meto-
do dimostra tra l'altro che non è indispensabile il
costrutto fork ejoin per la risoluzione di tale tipolo-
gia di problemi.
IMPLEMENTAZIONE
Alcune questioni sono rimaste in sospeso, come ad
esempio la modalità di inizializzare un semaforo.
Inoltre, bisogna studiare il modo di implementare i
semafori. Dall'analisi della situazione emerge come
maggiore svantaggio dell'approccio alla mutua
esclusione l'attesa nelle situazioni di occupato. Se
un processo è nella sezione critica, un altro proces-
so che tenta di entrare nella stessa sezione è costret-
to ad un ciclo a vuoto. Un loop che non consente al
processo di fare altro. Nella programmazione reale
ciò definisce un grande limite. Per superare il pro-
blema bisogna modificare l'implementazione delle
due funzioni P e V, rispetto alla definizione base
proposta. Trovare dei meccanismi che eliminino o
quanto meno minimizzino tale problema cono-
sciuto come: dell'attesa limitata. La fase di attesa
deve essere sostituita dalla possibilità per il proces-
so di riprendere. Si tratta di far passare il processo
che in questa situazione si trova allo stato di blocca-
to a quello di pronto. L'implementazione di tale
evento avviene, innanzitutto, modificando il tipo
semaforo che sarà adesso associato ad una variabi-
le strutturata non omogenea. Ecco come può essere
dichiarato il record.
type semaforo=
record
valore
:integer;
1: <lista di processi>;
end;
Quindi un semaforo sarà caratterizzato da un valo-
re, che ha il significato che conosciamo, e da una
lista di altri processi. Questa ultima non è stata di
proposito descritta nei dettagli, ad ogni modo si
tratta di una lista a puntatori. Una lista di PCB (pro-
cess control block) descrive al meglio il secondo
campo. Insomma una lista in cui ogni nodo presen-
ti descrittori di processi. Quando un processo deve
attendere ad un semaforo esso viene aggiunto alla
lista di processi e ovviamente si incrementa il valo-
re. La funzione V decrementerà il valore e rimuo-
verà il processo liberato. Alla luce di questa nuova
struttura dati l'implementazione delle due funzioni
si trasforma come segue:
P(S):
S. valore: =S.valore-l;
if S.valore<0
then begin
oggiungi il processo a S.L>
block
end;
V(S):
S. valore :=S.valore+l;
if S.valore<=0
then begin
< rimuovi il processo da S.L>
resta rt(P)
end;
L'operazione block sospende il processo che lo
invoca. L'operazione restart riavvia l'esecuzione del
processo bloccato da P Con questa definizione il
valore di S potrà essere negativo a differenza della
definizione classica quando ciò non era possibile.
Il valore negativo indica il numero di processi in at-
tesa al semaforo. Ciò è la conseguenza del decre-
mento introdotto nella funzione P L'aggiornamento
della lista avviene seguendo una filosofìa FIFO (First
in first out) tipica delle code, ossia il primo ad entra-
re e il primo ad uscire. Ad ogni modo non è un ele-
mento rilevante la strategia usata per la produzione
e la gestione della lista dei processi. Per cui è possi-
bile trovare in alcune implementazioni la gestione
della lista con code a priorità e in rari casi con pile.
L'elemento centrale nella realizzazione dei semafo-
ri è il modo in cui si garantisce l'atomicità di esecu-
zione. Bisogna assicurare che non ci siamo mai due
I TUOI APPUNTI
Utilizza questo spazio per
le tue annotazioni
ESECUZIONE
CONCORRENTE
L'esecuzione concorren-
te tra processi è garan-
tita quando:
• Si ha mutua esclusio-
ne: se un processo è
nella regione critica al-
lora nessun altro pro-
cesso ci si può trovare;
• Non ci sono processi
in esecuzione nella re-
gione critica e altri pro-
cessi ne fanno esplicita
richiesta, allora uno di
essi deve entrare. La
decisione non può spet-
tare ai processi che si
trovano nella parte ri-
manente. Inoltre, non
si può posticipare inde-
finitamente nel tempo
tale decisione.
• Vi è attesa limitata:
deve esistere un limite
superiore di numeri di
processi che entrino
nella regione critica pri-
ma di un generico pro-
cesso.
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 127 ►
SOLUZIONI T ■ Processi concorrenti
CURIOSITÀ
Le due funzioni PeV
sono anche conosciute
come wait e signal.
Anzi la loro prima
definizione aveva
questo nome. La P
(wait) indica che
bisogna aspettare così
come fa un
automobile davanti ad
un semaforo rosso.
Mentre la V (signal)
indica il lancio di un
segnale. Nell'analogia
stradale significa che
una direzione
dell'incrocio è stata
servita e il semaforo
può diventare verde
per altri automobilisti.
o più processi che eseguano operazioni di P e V
dello stesso semaforo allo stesso tempo. Questo è
un problema di sezione critica e può essere risolto
in due modi. Per processori singoli si può semplice-
mente inibire gli interrupt durante il tempo delle
operazioni di P e VCè da dire che alcune operazio-
ni di sistema non sono in grado di sopportare lun-
ghe sospensioni della CPU, come ad esempio la let-
tura dei dati da un disco; altrimenti i dati si perdo-
no. Si ovvia nel caso specifico al problema con la
presenza di buffer. Comunque in definitiva non si
tratta di una reale difficoltà considerato che le istru-
zioni di P sono pochissime, e si realizzano quindi in
tempo esiguo. In un sistema a multiprocessore (con
più CPU) tale metodo è impraticabile poiché le
istruzioni di diversi processi, nella fase di running,
possono essere manipolate su diversi processori in
modo arbitrario. In tal caso è necessario adottare
uno dei conosciuti metodi per la gestione critica
con flag (algoritmi 4, 5 e 6 presentati nello scorso
numero). Con ciò bisogna sinceramente ammette-
re che l'effetto indesiderato attesa limitata non è del
tutto eliminato. Si è spostata la protezione delle
sezioni critiche sulle sole operazioni di P e V, che è
meglio delle precedenti soluzioni che si applicava-
no a tutta la fase di entrata. Le operazioni suPeV
sono più corte con conseguente diminuzione di
tempi di attesa. Inoltre, è scomparsa l'attesa limita-
ta dall'ingresso alle sezioni critiche. Per approfondi-
re analizziamo un paio di esempi che fanno uso dei
semafori.
PRODUTTORE
CONSUMATORE
Il problema che già conosciamo prevede la presen-
za di due entità. Un produttore che genera dati ed
un consumatore che assorbe le produzioni del pro-
duttore. Considerando come più flessibile un siste-
ma di scambio asincrono risulta indispensabile la
presenza di un buffer. Supponiamo che il buffer sia
costituito da n dati. Bisogna prevedere un semaforo
che controlli il buffer. A tale semaforo devono rife-
rirsi i due processi i quali non possono per ovvie
ragioni accedere in contemporanea al buffer.
Le due strutture queue (coda) e
stack (pila) seguono due diversi
metodi di accesso ai dati.
Entrambe si possono realizzare con
array o liste a puntatori. Nel caso
della coda si segue la FIFO (first in
first out) che vuol dire che il primo
elemento ad entrare nella
struttura dati è anche il primo ad
uscirne. Per intenderci è il caso di
una normale coda di persone ad
uno sportello, dove il primo a
entrare è il primo ad essere
servito. Nel caso della pila si segue
il metodo LIFO (last in first out)
che indica che l'ultimo ad entrare è
il primo a uscire. Il tal caso le due
operazioni di inserimento (push) e
prelevamento (pop) avvengono
dallo stesso lato.
Produttore
-> | birC£er~| -
Consumatore
Fig. 2: Schema del problema Produttore - Consumatore
Inoltre, il produttore avrà un proprio semaforo che
impedirà di continuare le operazioni quando il buf-
fer è pieno, mentre il consumatore analogamente si
fermerà quando il buffer è vuoto. Ecco il codice nel-
la sua completezza. Si tralascia la sola dichiarazione
del tipo dato che dipende dal caso specifico di uti-
lizzo. Il buffer sarà un array o una lista a puntatori di
elementi di tipo dato.
var pieno, vuoto, mutex: semaforo;
datop, datoc: dato ;
begin
pieno:=0;
vuoto: = n;
mutex: = 1;
cobegin
produttore:
repeat
<produce il dato datop>
P(vuoto);
P(mutex);
oggiunge il dato datop al buffer>
V(mutex);
V(pieno);
until false;
consumatore:
repeat
P(pieno);
P(mutex);
<rimuove il dato datoc dal buffer>
V(mutex);
V(pieno);
<consuma il dato datoo
until false;
coend
end.
Quando il produttore incontra P(mutex) si blocche-
rà se il semaforo è occupato il che vuol dire che il
consumatore sta avendo accesso al buffer.
Ragionamento speculare per il consumatore. Il pro-
duttore prima di aggiungere un nuovo dato nel buf-
fer verifica se i due semafori gli danno via libera. Il
primo è libero se non occupato dal consumatore. Il
secondo è libero purché ci sia qualche posto libero
nel buffer. Vuoto è inizializzato a n; ogni volta che il
produttore lancia una P(vuoto) sottrae un elemento
vuoto, quindi ne aggiunge uno occupato al buffer e
procede. Si ferma nel caso vuoto sia 0, ossia non ci
► 128 /Luglio-Agosto 2005
http://www.ioprogrammo.it
Processi concorrenti H T SOLUZIONI
sono posti liberi. Poi ogni qual volta inserisce un
nuovo elemento nel buffer manda un segnale al
semaforo pieno (una V) che vuol dire che sta ag-
giungendo dati. Se, infatti, quando pieno è ancora 0,
il processo consumatore provasse a effettuare una
operazione si troverebbe a dover attendere sul se-
maforo pieno. Basta almeno un solo inserimento
del produttore e tale semaforo segna verde. Si ri-
scontra una precisa simmetria tra il codice associa-
to al produttore e quello associato al consumatore.
Si può dire che il produttore ha il compito di gene-
rare buffer pieni per il consumatore; e che il consu-
matore ha come compito quello di produrre buffer
vuoti per il produttore. Un esempio esplicativo sul-
l'uso dei semafori. Ma analizziamone un altro anco-
ra più intrigante.
I FILOSOFI AFFAMATI
Si tratta di un curioso e importante problema la cui
soluzione in ambito concorrente segna un percorso
da seguire per una ricca gamma i problemi della
stessa tipologia. Anzi per molti programmatori si
tratta del paradigma per i problemi sulla sincroniz-
zazione. Descriviamo la scena e gli attori. Si tratta di
un banchetto di n filosofi che svolgono le due sole
attività di pensare e mangiare. Attingono ad un vas-
soio di riso e poiché sono cinesi usano le bacchette,
due per la precisione. Anche le bacchette come i fi-
losofi e i vassoi sono n, per cui un filosofo può man-
giare solo se trova libera una bacchetta a destra e
una sinistra. È facile l'analogia con il mondo della
programmazione. Le bacchette sono risorse e i filo-
sofi processi che consumano risorse.
var bacchette : array[0..4] of semaforo;
Fig. 3: La scena di riferimento per il problema dei
filosofi affamati
L'idea alla base della soluzione è associare un sema-
foro ad ogni bacchetta. Si tratterà quindi di definire
un array di n semafori. Facendo riferimento al caso
in cui il colto banchetto comprenda 5 saggi, si può
implementare la soluzione, per il generico filosofo,
come segue. Si suppone che tutte le bacchette siano
inizializzate a 1.
(* azione del filosofo i *)
repeat
P(bacchette[i]);
P(bacchette[i+1]);
<mangia>
V(bacchette[i]);
V(bacchette[i+1]);
<pensa>
until false;
Il generico filosofo deve attendere che siano libere
le due bacchette di sua competenza, di indice i e
i+1. Una volta liberate può mangiare e successiva-
mente riposare le bacchette sul tavolo, azione che
corrisponde al richiamo delle due funzioni V. Quin-
di può ritornare nelle sue lunghe e profonde analisi
(sic!). A volere essere pignoli i pensatori potrebbero
trovarsi nella situazione di non mangiare e non
pensare, brutta condizione che dobbiamo evitare ai
nostri saggi. Se ogni filosofo prende la bacchetta alla
propria destra, ovvero supera P (bacchettali]), e si
blocca sulla successiva, quella a sinistra P(bacchet-
ta[i+l]) si presenta uno stallo, un deadlock. Si ha
attesa circolare. Ogni filosofo attende senza poter
far nulla. Si può risolvere mettendo in campo alcu-
ne idee; esaminiamole:
• Si invita un filosofo in meno;
• Analogamente alla prima idea si aggiunge una
bacchetta. Tradotto nella realtà informatica si
aggiunge una risorsa (purché non si tratti di uno
spreco può essere attuata);
• Un filosofo prima di prendere la bacchetta deve
sincerarsi che anche l'altra sia disponibile.
• Soluzione asimmetrica. I filosofi pari prendono
prima la bacchetta alla propria destra e poi
quella alla propria sinistra. I filosofi dispari fan-
no il contrario. Potenza dell'ingegnosità.
Le ultime due soluzioni non prevedono modifiche
della situazione iniziale.
CONCLUSIONI
Abbiamo visto come i problemi di sincronizzazione
sono ben gestiti mediante i semafori. Il percorso
anche se comincia a presentare le prime salite ci
mostra orizzonti sempre più interessanti e merite-
voli di essere osservati e approfonditi. Il sentiero
ancora non è alla fine. Vi aspetto per altre piacevoli
esplorazioni, tutto nel fantastico mondo della pro-
grammazione concorrente e dei sistemi operativi.
Alla prossima!!
Fabio Grimaldi
DEADLOCK
Letteralmente punto
morto o stallo. Nella
programmazione
concorrente un
insieme di processi è in
deadlock quando
ciascun processo
dell'insieme è in attesa
di un evento che può
essere causato
soltanto da un altro
processo dell'insieme.
Verosimilmente gli
eventi sono
l'acquisizione e il
rilascio delle di risorse.
http://www.ioprogrammo.it
Luglio-Agosto 2005/ 129 ►
BIBLIOTECA T
I migliori testi scelti dalla redazione
ON LINE
JAVA ITALIAN
PORTAL
Nato dalla passione di un gruppo di
ragazzi per l'informatica, JavaPor-
tal si è trasformato nel tempo in un ric-
co contenitore di articoli tecnici, noti-
zie, link utili riguardo al mondo del lin-
guaggio del chicco di caffè.
http://www.javaportal.it
|^H ■
làÉÉ
j»
%0^
He 1 --
S§F
ie ~~ *'
™""™'""
■ "**"**" ""' \\~:~"~z:r.' — -
W-
x ■ zp.?!ai,
£T ~i-— - ||i...-.-;rz-...
OPENSKILLS
sperimento Aperto di Knolewdge
condiviso - portale per Sysadmin
Linux - Questo è quanto si legge co-
me descrizione del sito OpenSkills. In ef-
fetti mai nessuna descrizione fu più az-
zeccata di questa. OpenSkills è un ric-
co contenitore di informazioni dedica-
ta agli amministratori di un sistema
Unix.
http://openskills.info/index-it.php
DEVELOPERFUSION
i lai bisogno di un pezzo di codice
con un esempio rapido che risol-
va un tuo problema? Ti serve un truc-
co veloce da applicare? Developer-
Fusion è quello che fa per tè. Il sito
contiene centinaia di piccoli snippet
divisi per linguaggio e argomento.
http://www.developerfusion.com
Biblioteca
BLACK HAT
CRIMINI MISFATTI
E TRUFFE SUL WEB
Divertente quanto inquietante
questo libro di John Biggs. Di-
vertente perché affronta in modo
ironico un problema serio, inquie-
tante perché il problema è talmen-
te serio che un autore delle qualità
di John Biggs ha sentito l'esigenza di
scrivere un libro sull'argomento. In-
quietante anche perché le truffe sul
Web sono diventate talmente diffu-
se che al di là di alcuni casi partico-
larmente inquietanti non suscitano
più nell'utente la perplessità di un
tempo, a testimoniare che il Web è di-
ventato ormai un parallelo della realtà
e ne ricalca pregi e difetti. Come nel-
la realtà esiste sul web un popolo di
criminali e tal volta fantasiosi truffa-
tori che esercita la propria "profes-
sione" nei meandri della rete piut-
tosto che in quelli più tangibili della
realtà quotidiana. Black Hat si snoda
fra crimini e truffe di tutti i tipi, ri-
percorrendo puntualmente la storia
del crimine informatico e tracciando
parallelamente alla sua evoluzione
anche quella tecnologica. Il paralle-
lismo fra crimine informatico e avan-
zamento della tecnologia è d'altra
parte tangibile se si pensa alla quan-
tità di patch che vengono diffuse an-
nualmente per difendersi dai moderni
truffatori. Una storia di moderni "la-
dri di biciclette" che in più di un ca-
so non lascia spazio al sorriso.
Difficoltà: Bassa • Autore: John
Biggs • Editore: Mondadori •
ISBN: 88-04-53739-6 • Anno di
pubblicazione: 2004 • Lingua:
Italiana • Pagine: 189 • Prezzo:
€12,80
TCP/IP PER
LAVORARE MEGLIO
CheTCP/IP sia il protocollo di ba-
se su cui si fonda l'intero uni-
verso del Networking è noto. Tutti i
sistemi di comunicazione oggi più dif-
fusi si basano sulla trasmissione
dei dati in formato TCP/IP. Ma co-
me funziona TCP/IP? che differen-
za c'è fra TCP e IP? quali sono e co-
me funzionano i protocolli di livel-
lo superiore che si basano su que-
sto standard? A queste domande
e a molte altre trovate la risposta nel
bel libro di Karanjit S. Silvan eTim
Parker edito da Apogeo. Si tratta di
un volume dal notevole spessore
in cui si parte dalle basi del proto-
collo affrontandolo tramite le sue
RFC per arrivare nel corso dei ca-
pitoli ad affrontare argomenti qua-
li il protocollo SNMP o la sicurezza
delle trasmissioni, argomento di
grande attualità. Si tratta di un li-
bro veramente interessante per chi
non si accontenta di usare in modo
meccanico gli strumenti che la tec-
nologia mette a disposizione ma
ne vuole invece comprendere le ba-
si, vuole in un certo senso essere
parte della tecnologia e non subir-
la. Il libro è ben scritto e nonostan-
te la difficoltà nel porre informa-
zioni così complesse scorre con una
certa piacevolezza alternando infor-
mazioni storiche e di carattere ge-
nerale alle ben più difficoltose no-
tizie tecniche.
Difficoltà: Alta • Autore: Karanjit
S. Siyan eTim Parker • Editore: Apo-
geo • ISBN: 88-503-2044-2 • An-
no di pubblicazione: 2002 • Lin-
gua: Italiana • Pagine: 862 • Prez-
zo: €52,00
INTERMEDIATE
ROBOT BUILDING
La robotica sta entrando pesan-
temente nella nostra vita quo-
tidiana e quello che la rende anco-
ra più interessante è che la perso-
nalizzazione dei piccoli robot che
migliorano la qualità della vita co-
mincia a diventare accessibile anche
a programmatori non particolar-
mente esperti.
Se da un lato è vero che non si può
pretendere di affrontare immedia-
tamente la programmazione di un ro-
bot per l'automazione industriale,
è altrettanto vero che esistono una
serie di sistemi programmabili con
relativamente poco sforzo e con
una certa soddisfazione.
Il libro di David Cook introduce al-
la programmazione dei robot, in un
viaggio fra resistenze, motori, ten-
sioni, microprocessori.
Si tratta di un libro per molti versi di-
vertente anche se decisamente in-
dicato per un pubblico di fascia al-
tra.
Difficoltà: Alta • David Cook • Edi-
tore: T-l-A • ISBN (pbk): 1 -59059-
373-1 • Anno di pubblicazione:
2004 • Lingua: Inglese • Pagine:
381 • Prezzo: € 34,99
► 130 /Luglio-Agosto 2005
http://www.ioprogrammo.it