Studio ed implementazione di un servizio di prenotazioni

Transcript

Studio ed implementazione di un servizio di prenotazioni
UNIVERSITÀ POLITECNICA DELLE MARCHE
FACOLTÀ DI INGEGNERIA
CORSO DI LAUREA IN INGEGNERIA
INFORMATICA E DELL’AUTOMAZIONE
Studio ed implementazione di un servizio
di prenotazioni “on-line” su piattaforma MHP
utilizzando Set Top Box da sviluppo ADB X-75
Relatore :
Prof. Aldo Franco DRAGONI
Correlatore:
Prof. Paolo PULITI
Tesi di Laurea di:
Bracalente Fabio
Anno Accademico 2005-2006
Dietro ogni problema c’è un’opportunità…
Galileo Galilei
Ringraziamenti
Sarò in controtendenza ma vorrei iniziare dai ringraziamenti “tecnici” per arrivare poi a
quelli affettivi.
Ringrazio il Prof. Aldo Franco Dragoni per avermi spinto verso una nuova tecnologia in
fase di sviluppo, che speriamo apra nuovi sbocchi, grazie anche per avermi dato fiducia
e costante assistenza in questo progetto.
Al Dott. Fabio Tonucci per la sua disponibilità durante il periodo di tirocinio.
Al Prof. Paolo Puliti per avermi fatto scoprire un nuovo linguaggio di programmazione,
e per avermi insegnato a vedere il Mondo ad “Oggetti”.
Vorrei ricordare il Prof. Maurizio Panti e ringraziarlo perché devo a lui le conoscenze
in campo di analisi dei dati e Database.
Vorrei ringraziare anche tutti gli altri professori che ho incontrato nel cammino
scolastico e soprattutto universitario perché ognuno di loro ha contribuito a formarmi
sia culturalmente che caratterialmente.
Ai colleghi tirocinanti con cui ho condiviso questa nuova esperienza.
Un ringraziamento veramente di cuore va in particolare ai miei genitori perché mi
hanno sempre sostenuto e lasciato libero in tutte le mie scelte e soprattutto perché senza
di loro tutto questo non sarebbe stato possibile.
A mio fratello, spirito guida ed esempio di diligenza sicuramente da seguire.
A tutti i miei nonni che mi hanno trasmesso, una in particolare che continua tuttora a
farlo, un po’ della loro saggezza.
Ai tutti i miei zii che fin da piccolo mi hanno allietato tante giornate e qualcuno in
particolare mi ha anche aiutato, in tutti i campi, in fasi particolari della mia vita.
Ai miei cugini più o meno piccoli perché, anche se li trascuro un po’, li porto sempre
nel cuore.
Un grazie anche a tutti i diversi coinquilini con cui ho convissuto in tutti questi anni.
Un grazie a tutti gli amici e amiche che mi sono stati vicino e spero che continuino a
farlo.
Un ringraziamento doveroso all’Hotstaff che è stato sinonimo di sostentamento nel
periodo degli studi, ma soprattutto: di grandi amicizie, di innumerevoli conoscenze, di
momenti di divertimento allo stato puro, una vera e propria medicina nei periodi tristi di
questi ultimi anni; forse anche la causa dell’allungamento della mia carriera
universitaria, ma ne è valsa la pena, grazie a tutti di cuore.
Ultimo, ma sicuramente non per importanza, un grazie tutto per lei lo devo alla mia
ragazza; grazie semplicemente di esistere e di avermi sopportato e supportato in questi
ultimi periodi.
Sommario
Sommario
GLOSSARIO................................................................................................................... 1
INTRODUZIONE........................................................................................................... 2
CAPITOLO 1
IL DIGITALE TERRESTRE ........................................................................................ 3
1.1 INTRODUZIONE ......................................................................................................... 3
1.2 NOVITÀ TECNOLOGICHE ........................................................................................... 3
1.3 LO STANDARD MHP................................................................................................. 7
1.4 BROADCASTING DI APPLICAZIONI INTERATTIVE ..................................................... 10
1.5 IL DECODER INTERATTIVO ...................................................................................... 11
CAPITOLO 2
APPLICAZIONI MHP................................................................................................. 13
2.1 MODELLO DI BUSINESS E INTERFACCE ................................................................... 13
2.2 XLET ...................................................................................................................... 15
2.3 GESTIONE DELLE RISORSE SCARSE ......................................................................... 18
2.4 MODELLO GRAFICO ................................................................................................ 25
2.4.1 Background layer........................................................................................... 26
2.4.2 Video layer ..................................................................................................... 32
2.4.3 Graphics layer ............................................................................................... 35
2.5 GESTIONE DEGLI EVENTI DEL TELECOMANDO ........................................................ 38
CAPITOLO 3
CANALE DI RITORNO .............................................................................................. 41
3.1 LO SCENARIO ......................................................................................................... 41
3.2 I PROTOCOLLI SUPPORTATI ..................................................................................... 45
3.2.1 Livello 1: Fisico ............................................................................................. 45
3.2.2 Livello 2: PPP................................................................................................ 46
3.2.3 Protocollo di rete: IP ..................................................................................... 48
3.2.4 Protocolli di trasporto: TCP / UDP .............................................................. 48
3.2.5 Hypertext Transfer Protocol (HTTP)............................................................. 49
3.2.6 Secure Hypertext Transfer Protocol (HTTPS)............................................... 51
3.2.7 Servizi Specifici.............................................................................................. 51
3.3 GESTIONE DELLA CONNESSIONE ............................................................................. 52
3.3.1 Utilizzare il canale di ritorno: il package org.dvb.net.rc .............................. 52
3.3.2 Interfacce per il Canale di Ritorno ................................................................ 53
3.3.3 Acquisire l’interfaccia corretta...................................................................... 55
3.3.4 Stabilire la connessione ................................................................................. 57
3.3.5 I package java.net e java.io ........................................................................... 61
I
Sommario
CAPITOLO 4
AMBIENTE DI SVILUPPO ........................................................................................ 62
4.1 INTRODUZIONE....................................................................................................... 62
4.2 IL SIMULATORE XLETVIEW ................................................................................... 63
4.3 SET TOP BOX DA SVILUPPO ADB X-75.................................................................. 66
4.3.1 Caratteristiche Tecniche................................................................................ 67
4.3.2 Pannello frontale ........................................................................................... 69
4.3.3 Pannello posteriore........................................................................................ 70
4.3.4 Telecomando .................................................................................................. 71
4.3.5 Settaggio connessione di default.................................................................... 72
4.3.6 STB Firmware upgrade.................................................................................. 73
4.3.7 UpLoad e debug di un’applicazione ............................................................. 74
4.3.8 Xlet description file ........................................................................................ 76
CAPITOLO 5
IMPLEMENTAZIONE DI UN SERVIZIO DI PRENOTAZIONI ......................... 78
5.1 LE PROBLEMATICHE ............................................................................................... 79
5.2 INTERFACCIA GRAFICA ........................................................................................... 80
5.3 ACCESSO AI DATI ................................................................................................... 84
5.3.1 Socket ............................................................................................................. 85
5.3.2 JDBC.............................................................................................................. 91
5.3.3 Database ........................................................................................................ 94
5.4 SVILUPPI FUTURI .................................................................................................... 95
CONCLUSIONI............................................................................................................ 96
APPENDICE A
CODICE XLET............................................................................................................. 97
APPENDICE B
CODICE PROXY ....................................................................................................... 124
SITI E BIBLIOGRAFIA............................................................................................ 131
II
Glossario
Glossario
DTT: Digital Terrestrial Television
DVB: Digital Video Broadcasting
DTv: Digital Television
EPG: Electronic Program Guide
ETSI: European Telecommunications Standards Institute
FTV: Free To View
HDTV: High Definition Television Defined
MHP: Multimedia Home Platform
MPEG: Moving Picture Expert Group
PAL: Phase Alternative Line
QAM: Quadrature Amplitude Modulation
SCART: Syndicat Francais des Constructeurs d’Appareils Radio et Television
IP: Internet Protocol
ISP: Internet Service Provider
TCP: Transmission Control Protocol
UDP: User Data Protocol
HTTP: Hypertext Transfer Protocol
TLS: Transport Layer Security
LAN: Local Area Network
1
Introduzione
Introduzione
La digitalizzazione della televisione terrestre è forse l'ultimo anello importante che
manca alla catena che in poco più di 10 anni ha trasformato in digitale i principali flussi
informativi che viaggiano sul nostro pianeta. Da qualche tempo dopo aver tratto
insegnamento dalle prime esperienze effettuate in UK, Spagna e Finlandia, anche l'Italia
ha intrapreso il cammino che, nel corso dei prossimi due anni, porterà a trasformare le
centinaia di stazioni televisive che popolano l'etere nazionale in altrettanti canali
digitali. Con la televisione Digitale Terrestre (DTT: Digital Terrestrial Television)
arriveranno non solo trasmissioni televisive di miglior qualità e in maggior quantità,
ma anche nuove possibilità di servizi interattivi, che da un lato potranno rendere più
ricca ed interessante l'esperienza del telespettatore, e dall'altro apriranno un canale di
comunicazione bidirezionale, e quindi di natura del tutto nuova, che potrà raggiungere
qualsiasi famiglia del nostro paese. Proprio la bidirezionalità della comunicazione è un
fattore che suscita molte aspettative in diversi settori: dalle Pubbliche Amministrazioni,
che vedono nel sistema un’opportunità per avvicinare i cittadini e rendere loro più
facilmente disponibili tutti i propri servizi, ai vari privati che sfrutteranno al massimo un
nuovo strumento sia pubblicitario che informativo.
Tra i vari servizi che oggi sono già realizzati per internet spiccano sicuramente: ecommerce, e-health, e-banking e servizi di prenotazioni.
In questo elaborato si cercare di spiegare innanzitutto le tecnologie sia hardware che
software, che interessano il digitale terrestre, compresi i vari standard che entrano in
gioco, continuando poi con la spiegazione di come realizzare un ambiente di sviluppo
per applicazioni MHP e infine descrivendo l’implementazione di una piattaforma per
prenotazioni “on-line” con particolare riguardo all’uso del canale di ritorno e l’accesso
ai dati e le varie problematiche che ne conseguono.
2
Capitolo 1: Il Digitale Terrestre
Capitolo 1
Il Digitale Terrestre
1.1 Introduzione
La televisione digitale terrestre (DTT: Digital Terrestrial Television) è l'ultima
tecnologia di comunicazione digitale che sta bussando alla porta delle case degli italiani,
e certamente risulta essere piuttosto apprezzata, a giudicare dai numeri.
Nel volgere di poco più di due anni e mezzo, dall'inizio del 2004, circa 2,5 milioni di
decoder DTT (i cosiddetti Set-Top-Box) hanno trovato posto nei nostri salotti, aiutati
indubbiamente anche dall’iniziale contributo per l'acquisto stanziato dal governo, di 70
euro per famiglia. La trasformazione della TV da analogica a digitale nel nostro Paese
coinvolgerà progressivamente gli oltre 50 milioni di apparecchi televisivi presenti sul
territorio. La fine delle trasmissioni TV terrestri in tecnica analogica (switch-off) è
prevista normativamente entro il 31 dicembre 2008: in base alla legge n. 66/2001 a
partire da tale data tutte le trasmissioni TV terrestri dovranno essere solo digitali. In
questa fase quindi tutti i broadcaster, nazionali e regionali, stanno affrontando i
problemi collegati alla transizione.
1.2 Novità tecnologiche
La novità tecnologica della DTT risiede principalmente in due elementi:
l'adozione di un sistema di trasmissione numerico COFDM, che presenta caratteristiche
di robustezza rispetto alle interferenze, migliore qualità del segnale trasportato e,
soprattutto, minor banda occupata nello spettro elettromagnetico, da cui l’attesa
moltiplicazione dei canali disponibili; la contemporanea presenza di un canale di ritorno
(CdR) che, collegando il ricevitore ad una rete di telecomunicazioni, fornisce al
telespettatore uno strumento per interagire a ritroso con l’emittente televisiva o con il
fornitore del servizio.
La modulazione usata dalla DTT a parte l'aggiunta di specifici codici per la correzione
dell'errore, è affine a quella usata per le connessioni ADSL. Essa lavora sul principio di
3
Capitolo 1: Il Digitale Terrestre
utilizzare diverse frequenze portanti fra loro ortogonali, fino ad un massimo di 8k
frequenze, e poi suddividere l'informazione in altrettanti flussi di bit indipendenti; ogni
flusso di bit viene assegnato ad una diversa portante e poi alla ricezione i diversi flussi
vengono opportunamente ricombinati.
Tale modulazione, quando opportunamente utilizzata, presenta una caratteristica
interessante: permette di sfruttare in modo costruttivo gli echi isofrequenziali che
giungono all'antenna ricevente del televisore. Ciò porta almeno un paio di vantaggi
rispetto al mondo analogico. Da un lato facilita una migliore ricezione all'interno delle
aree urbane, dove il segnale elettromagnetico può facilmente arrivare all'antenna in
modo multiplo, per via delle riflessioni che subisce incontrando i vari edifici (e che nel
mondo della TV analogica produrrebbero immagini ghost), dall'altro abilita una più
semplice costruzione dell'intera rete broadcast, permettendone una organizzazione in
isole isofrequenziali, fino ad una dimensione massima che dipende dal rapporto fra
tempo di trasmissione e tempo di silenzio ("di guardia" in termini tecnici) che è stato
scelto da ogni broadcaster per i trasmettitori della propria rete.
La ricezione del segnale televisivo può avvenire attraverso lo stesso impianto d’antenna
utilizzato già oggi per la TV analogica, a patto che l'impianto stesso sia stato effettuato a
regola d'arte, rispettando i requisiti tecnici di base, com’è normale se ci si è affidati a
professionisti del settore per la sua realizzazione. Il canale di ritorno utilizzerà un
collegamento fornito dalla rete di telecomunicazioni, e potrà operare sia con tecnologia
tradizionale (RTG: Rete Telefonica Generale) sia con tecnologie a larga banda (ADSL)
o wireless (GPRS/UMTS). Grazie al canale di ritorno ed alle funzioni che permettono
interattività locale sarà possibile progettare e costruire una grande varietà di nuovi
servizi, che vanno dal settore dell'intrattenimento a quello informativo, per arrivare a
servizi di utilità forniti da soggetti pubblici (es. il Comune di residenza) o privati (es: la
propria banca).
Il segnale audiovisivo della DTT è codificato in digitale secondo lo standard MPEG-2,
lo stesso adottato da oltre un decennio per la TV digitale satellitare. La modulazione di
tale segnale sulla specifica frequenza portante, fatto secondo lo standard DVB-T,
permette di trasportare un flusso numerico di circa 24 Mbit/s su ogni frequenza,
consentendo così di trasmettere fino a 4 o 5 canali TV, con qualità audiovisiva
paragonabile a quella di un DVD, occupando la stessa banda di frequenza attualmente
4
Capitolo 1: Il Digitale Terrestre
utilizzata da un singolo canale analogico. In questa prima fase di diffusione del servizio,
e nell'attesa che si diffondano sul mercato televisori già naturalmente predisposti per
vedere le trasmissioni digitali, per ricevere il segnale digitale sarà necessario collegare il
normale apparecchio televisivo che popola i salotti degli italiani con un ricevitore
digitale, il cosiddetto Set-Top Box (STB), che realizza le funzioni di ricevitore/decoder
per il segnale della TV digitale terrestre, ed utilizza la televisione per visualizzare le
immagini e diffondere i suoni. Il collegamento fra Set-Top Box e televisore si può
realizzare tramite la consueta presa SCART. Come già accennato sopra, in aggiunta al
consueto contenuto audiovisivo, il segnale digitale può agevolmente trasportare, sempre
all'interno dello stesso flusso digitale, altri tipi di dati, associati o meno al canale che si
sta trasmettendo. Il Set-Top Box diventa così un vero e proprio ambiente di esecuzione
per applicazioni, in grado di fornire i diversi servizi aggiuntivi, grazie al dialogo
realizzato con telecomando e Canale di Ritorno. La componente interattiva della TV
digitale è stata standardizzata a livello europeo attraverso la specifica DVB-MHP
(Digital Video Broadcasting - Multimedia Home Platform), che definisce la piattaforma
standard per la distribuzione ed esecuzione di applicazioni televisive su STB, nonché le
specifiche per il loro sviluppo. Un STB MHP è un sistema basato su una Java Virtual
Machine che integra le funzionalità di controllo dei flussi audio/video necessarie per
operare nell'ambiente televisivo, mentre le applicazioni non sono altro che dei
programmi scritti in Java che utilizzano le risorse hardware e software presenti nel STB.
La combinazione di STB, piattaforma MHP e canale di ritorno, che sarà presente in
prospettiva in tutte le case degli italiani, è il fattore abilitante che favorirà lo sviluppo di
una molteplicità di servizi informativi, di pubblica utilità e commerciali. In questo
contesto la DTT si va ad inserire come essenziale tecnologia abilitante nel processo di
ammodernamento delle infrastrutture telematiche del paese, con l'obiettivo dichiarato di
diventare uno degli elementi chiave nel processo di riduzione dell'analfabetismo digitale
della popolazione italiana. E’ importante capire cosa possa essere e come si possa
costruire il nuovo fenomeno dell'interattività televisiva, i ruoli dei diversi soggetti che
opereranno nel ciclo di vita del prodotto digitale e come si struttureranno i rapporti tra i
vari attori che si inseriscono in tale ciclo; attori che possono essere tradizionali, come le
TV che conosciamo, oppure del tutto nuovi, come i fornitori di servizi interattivi, la loro
natura, il loro ciclo di sviluppo, messa in onda ed esercizio.
5
Capitolo 1: Il Digitale Terrestre
L'evoluzione prossima della DTT sarà quella dell'arrivo di STB con hard disk integrato
e collegata capacità di registrazione digitale, il cosiddetto PVR (Personal Video
Recording). Alcuni produttori hanno già pronto un tale oggetto, ma appaiono ancora
cauti sull’opportunità di passare alla fase commerciale vera e propria.
Passare dalla registrazione su cassetta a quella su hard-disk, oltre a costituire un vero
grande salto nelle abitudini quotidiane dei telespettatori, introduce, infatti, alcuni effetti
collaterali rispetto ai quali il mondo della televisione si sta ancora interrogando. Primo
fra tutti l'insorgere di un’abitudine diffusa nel fruire della cosiddetta "time-shifted TV".
Quanti di noi normalmente non riescono ad accendere la televisione nel momento giusto
per vedere un film, un telegiornale, uno show dall'inizio della programmazione? Un
videoregistratore digitale è l'oggetto ideale per registrare la trasmissione dal suo inizio, e
renderla visibile in un qualunque momento, anche mentre è ancora in corso. Ed a quel
punto posso anche "accelerare" la fruizione della parte già registrata, usando i comandi
di "avanti veloce" così come avviene su un registratore VHS. Immaginate cosa potrebbe
capitare agli spot pubblicitari?
Negli USA, dove i PVR cominciano ad essere diffusi sulle TV via cavo e via satellite, i
pubblicitari hanno dichiarato che quando tali dispositivi avranno conquistato la soglia di
30 milioni di case, gli investimenti pubblicitari fatti nelle TV si ridurranno di almeno il
20%. Conciliare evoluzione tecnologica e modelli di business è il prossimo importante
passo che la TV digitale deve affrontare, e non solo in Italia.
Fig. 1.2.1 – Digital Video Broadcasting Terrestre nel mondo
6
Capitolo 1: Il Digitale Terrestre
1.3 Lo standard MHP
MHP è uno standard molto giovane, la prima release è stata creata dal DVB Project e
standardizzata dall'ETSI Institute nell'anno 2000. Multimedia Home Platform definisce
l'interfaccia tra le applicazioni interattive e i terminali sulle quali queste possono essere
eseguite (Set-Top Box, digital TV e PC multimediali). L'architettura (come mostrato in
figura 2) è definita dai seguenti 3 layers:
Resources: MPEG processing, I/O devices,CPU, memoria e graphics system.
System Software: JVM, APIs, transport protocols e soprattutto l'Application
Manager (o navigatore) che permette di gestire il running delle applicazioni Java.
Applications: le applicazioni interattive come ad esempio Electronic Program Guide,
servizi informativi, giochi, TV-Commerce etc.
Fig. 1.3.1 - Architettura dell'MHP
Come già preannunciato il "core" di MHP si basa su Java e in particolare sull'adozione
di una Personal JVM e di una serie di API, tra cui spiccano le Java TV di Sun.
All'interno dello standard troviamo anche la definizione di 3 profili, utili ad evidenziare
i servizi possibili con le relative tecnologie. Esiste inoltre un legame univoco tra i profili
(Figura 1.3.2) e le release dello standard:
7
Capitolo 1: Il Digitale Terrestre
Enhanced Broadcast Profile è definito nelle specifiche MHP 1.0, è il profilo base e
permette solamente l'arricchimento del contenuto audio-video con informazioni e
immagini visualizzabili e navigabili sullo schermo tv. Per questo profilo non sono
richieste performance particolari dei set-top box.
Interactive TV Profile è definito sempre nelle specifiche MHP 1.0 ed è il profilo
intermedio che permette di utilizzare il canale di ritorno (sia di tipo modem, adsl, gprs,
ethernet, etc) per fornire servizi con interattività superiore rispetto al profilo base.
Questo profilo, infatti, supporta anche il caricamento di applicazioni MHP tramite il
canale di ritorno e non solo dal canale di broadcast.
Internet Access Profile è definito nelle specifiche MHP 1.1 e permette tramite il canale
di ritorno di accedere ai contenuti di Internet. Questo profilo necessita di performance di
alto livello essendo obbligatoria l'adozione di un browser internet e di un client di email
embedded nel set-top box.
Fig. 1.3.2 - Profili dell'MHP
8
Capitolo 1: Il Digitale Terrestre
Alcuni esempi di applicazioni interattive possibili in MHP sono:
Electronic Program Guide(EPG).
Servizi di news come ad esempio un Telegiornale Interattivo.
Un gioco tv.
Giochi stand-alone.
Servizi interattivi legati ad eventi sportivi.
TV Commerce e TV Banking.
Servizi Meteo, Traffico e di pubblica utilità.
Pubblicità interattive.
Fig. 1.3.3 - Esempi di applicazioni MHP
9
Capitolo 1: Il Digitale Terrestre
1.4 Broadcasting di applicazioni interattive
Dopo aver analizzato lo standard, le tecnologie coinvolte e fornito qualche esempio di
cosa è possibile realizzare, vediamo in che modo e con quali strumenti le applicazioni
Java-MHP possono essere trasmesse e ricevute dai decoder interattivi. Iniziamo
evidenziando uno dei già citati vantaggi della trasmissione in digitale, la maggior
disponibilità di canali televisivi, analizzando il caso del digitale terrestre. Nel sistema di
trasmissione analogico(PAL) il segnale occupa una banda di 8MHz, modulando il
segnale in digitale, nella stessa banda è possibile avere un flusso dati di 24Mbit/sec.
Considerando che un canale tv digitale grazie alla compressione A/V MPEG2
mediamente occupa 4Mbit/sec risulta evidente come sia possibile trasmettere un
maggior numero di canali televisivi. Ovviamente se volessimo trasmettere anche
applicazioni interattive dovremmo riservare parte della banda disponibile anche per
questo tipo di dati; mediamente il peso di un applicazione comprensiva di grafica si
aggira sui 500Kbyte. Lo schema generale di figura 1.4.1 ci mostra un esempio relativo
all'emissione e alla ricezione del segnale digitale comprensivo delle applicazioni
interattive. Nello standard MHP tali applicazioni sono chiamate Xlet e possono essere
composte solo da tipologie precise di dati: classi java, file xml o txt, immagini png o jpg
o gif, audio mp2 e video mpg. Le Xlet per essere broadcastate necessitano di un
preventivo impacchettamento in moduli di dimensione massima di 64Kbyte. La
sequenza di moduli che compone ogni applicazione viene tramessa in loop
costantemente e di solito viene associata ad un particolare canale tv. Tale associazione,
come evidenziato nello schema, avviene nel Multiplexer dove è possibile creare tali
associazioni per ogni Xlet broadcastata. A questo punto il flusso in uscita dal
Multiplexer (Trasport Stream) viene modulato in una delle 3 tipologie possibili definite
da Digital Video Broadcasting: Terrestre (specifiche DVB-T), Satellite (specifiche
DVB-S) e Cavo (specifiche DVB-C). Dopo la modulazione il segnale viene trasmesso e
quindi ricevuto sul decoder interattivo (set-top box) che come vedremo offre la
possibilità di connessioni a server dedicati o ad internet tramite modem o tecnologie più
avanzate come l'ADSL.
10
Capitolo 1: Il Digitale Terrestre
Fig. 1.4.1 - Emissione e ricezione del segnale digitale
1.5 Il decoder interattivo
Il decoder interattivo(Set-Top Box) è l'apparecchio indispensabile per ricevere i canali
digitali e utilizzare i software interattivi. Questo dispositivo permette innanzitutto la
decodifica del segnale da digitale ad analogico per gli apparecchi televisivi analogici
che attualmente nel nostro paese rappresentano la totalità del mercato. Infine consente
di eseguire applicazioni Java grazie alla presenza di una Personal JVM embedded.
Come mostra la Figura 1.5.1 un decoder è formato da una parte hardware(CPU,
memoria, modem, etc), da un sistema operativo real-time e da un middleware MHP.
Fig. 1.5.1 - Schema HW/SW di un Set-Top Box
Un attore importante che troviamo all'interno del decoder è il cosiddetto navigatore(o
application manager). Il suo compito è fondamentale, infatti esso si occupa di segnalare
11
Capitolo 1: Il Digitale Terrestre
all'utente la lista delle applicazioni MHP disponibili sul canale televisivo. Inoltre se
l'utente effettua una selezione da tale lista, il navigatore inizia la fase di download in
memoria dei moduli relativi all'applicazione scelta. Importante sottolineare che non tutti
i moduli che compongono l'applicazione verranno scaricati, infatti il download sarà
relativo solo a quelli neccessari alla partenza della Xlet. Successivamente e solo se
realmente necessari verranno scaricati anche quelli rimanenti. Tale meccanismo ricorda
il modello di caricamento delle pagine del televideo ed è pensato allo scopo di ridurre i
tempi di attesa del telespettatore. Un dispositivo tecnologico presente sul decoder che
rende operativo il concetto di canale di ritorno definito nello standard è il modem nei
decoder di fascia bassa e l'ADSL, Gprs, Ethernet, etc. sui decoder di fascia alta. Il
canale di ritorno permette di interagire con servizi esterni, su server dedicati o internet e
scaricare contenuti su richiesta del telespettatore. Nel caso di modem V90 i tempi di
connessione e recupero dati sono abbastanza lunghi(30-40 sec solo per la chiamata
telefonica e l'autenticazione presso un ISP), ma nel caso di connessioni "always on"
come con ADSL allora gli scenari possibili sono molto più interessanti, come la
possibilità di scaricare musica e video su richiesta dell'utente.
12
Capitolo 2: Applicazioni MHP
Capitolo 2
Applicazioni MHP
2.1 Modello di Business e interfacce
Un’applicazione mhp viene sviluppata seguendo un modello di business che si basa su
più fattori:
- MSO (Multiple Service Operator) i broadcaster, cioè coloro che forniscono la
possibilità di scaricare le applicazioni e i contenuti per la iTV
- MSV (Middleware Software Vendor) gli sviluppatori java/DVB-HTML
- I fornitori dei contenuti ISP (Internet Service Provider)
- Set-top-box Manufacturers
- Users: gli utenti dotati di set-top-box, televisore e telecomando
Date l’innumerevoli combinazioni che possono venire dall’incrocio dei diversi fattori
elencati, non poche possono sembrare le difficoltà nel produrre un’applicazione MHP.
Il trucco si trova nelle interfacce che lo standard MHP mette a disposizione sia a livello
software (figura 2.1.1) che hardware (figura 2.1.2).
MHP è basato su java, (platform indipendent), il set-top-box ha preinstallato una VM
java corredata da API proprietarie e TV API di SUN, che definiscono le modalità di
comunicazione tra l'utente, il set-top-box e il broadcaster.
MHP ha definito anche un sotto linguaggio HTML (DVB-HTML per la precisione) con
tag supplementari relativi alla cattura dell'input dell'utente (telecomanado).
Il set-top-box ha preinstallato un'applicazione che si chiama Application Manager (di
solito java) che gestisce il caricamento del software creando anche i menu dei canali
forniti dal broadcaster.
Oltre a ciò il set-top-box possiede anche un alloggiamento per smartcard (nonché un
lettore per le stesse), che offre servizi sicuri come login utente, servizi a pagamento
ecc..
13
Capitolo 2: Applicazioni MHP
Fig. 2.1.1 – Software layer
Fig. 2.1.2 - Interfaccia tra mhp application e mhp system
Per ogni tipo di interfaccia si avrà una suite di classi che permetterà di far dialogare
l’applicazione con i dispositivi a disposizione: schermo, telecomando, smart card,
canale di ritorno.
14
Capitolo 2: Applicazioni MHP
2.2 Xlet
Una Xlet è una particolare applicazione Java concepita per essere scaricata ed eseguita
su decoder interattivi nei quali un software specifico, l'Application Manager, è in grado
di controllarne il ciclo di vita. Come vedremo le similitudini con le Applet, dove
l'application manager è rappresentato dal browser sono molteplici.
Per iniziare a costruire una Xlet occorre innanzitutto capire cosa viene fornito da Sun
nelle API Java TV. Infatti è proprio in queste librerie che vengono definite le due
interfacce fondamentali da utilizzare:
•
javax.tv.xlet.Xlet che definisce i seguenti metodi:
public void initXlet(XletContext ctx);
public void startXlet();
public void pauseXlet();
public void destroyXlet(boolean unconditional);
•
javax.tv.xlet.XletContext che definisce i seguenti metodi:
public void notifyDestroyed();
public void notifyPaused();
public java.lang.Object getXletProperty(java.lang.String key);
public void resumeRequest();
Entrambe le interfacce servono per interagire con l'application manager in merito al
ciclo di vita e al contesto in cui la Xlet viene eseguita.
La classe principale di ogni Xlet deve sempre implementare javax.tv.xlet.Xlet per poter
essere eseguita sui decoder interattivi MHP.
Come si mostra in Figura 2.2.1 il ciclo di vita di una Xlet è caratterizzato da 4 stati:
Loaded: in questo stato la Xlet è stata creata ma non inizializzata, se durante questa
fase viene rilevata un eccezione allora si passa direttamente nello stato Destroyed. Da
notare che la Xlet si può trovare in questo stato solo una volta durante tutto il suo ciclo
di vita.
Paused: la Xlet è inizializzata e può trovarsi in questo stato sia dopo che il metodo
initXlet(XletContext) è ritornato con successo dallo stato Loaded oppure dopo che il
metodo pauseXlet() è ritornato con successo dallo stato Active. In entrambi i casi in
questo stato la Xlet dovrebbe limitare al massimo l'utilizzo delle risorse condivise e
soprattutto non impegnare la GUI televisiva.
Active: in questo stato la Xlet è attiva e utilizza le risorse necessarie per fornire i suoi
servizi, se dotata di GUI allora dovrebbe essere l'unica registrata per ricevere gli eventi
15
Capitolo 2: Applicazioni MHP
del telecomando.
Destroyed: in questo stato la Xlet deve rilasciare tutte le risorse in uso per predisporsi
alla terminazione.
Fig. 2.2.1 - Ciclo di vita di una Xlet
Una sequenza tipica di questo ciclo potrebbe essere:
L' application manager crea una nuova istanza di una Xlet chiamando il suo costruttore
(stato LOADED).
La
Xlet
usa
il
context
passatogli
dall'application
manager
nel
metodo
initXlet(XletContext) e si inizializza (stato PAUSED).
L'application manager chiama il metodo startXlet() e la Xlet inizia ad utilizzare le
risorse necessarie per fornire i servizi per cui è stata costruita(stato ACTIVE).
L'application manager chiama il metodo destroyXlet(true) e la Xlet rilascia le risorse,
dopodiché viene terminata (stato DESTROYED).
Il primo esempio: HelloXlet.java
import javax.tv.xlet.Xlet;
import javax.tv.xlet.XletContext;
import javax.tv.xlet.XletStateChangeException;
public class HelloXlet implements Xlet{
// L'XletContext è un oggetto usato dalla Xlet per accedere
// alle properties del suo environment e per comunicare con
// l'application manager che glielo ha passato nella fase
// di inizializzazione.
private XletContext context;
private boolean alreadyActive = false;
16
Capitolo 2: Applicazioni MHP
// Il costruttore tipicamente viene lasciato vuoto visto che tutte
// le inizializzazioni dovrebbero stare nel metodo initXlet()
public HelloXlet(){}
// In questo metodo vengono fatte tutte le inizializzazioni,
// se in questa fase qualcosa fallisse verrebbe lanciata un
// eccezione.
public void initXlet(XletContext context) throws XletStateChangeException{
// si memorizza il context passato dall'Application Manager
this.context = context;
System.out.println(this.getClass().getName()+" : Inizializzazione
avvenuta!");
}
// Con questo metodo la Xlet è avviata e vengono richieste
// le risorse necessarie come ad esempio lo schermo TV o
// gli eventi del telecomando
public void startXlet() throws XletStateChangeException{
// controlliamo se la Xlet era già stata messa in stato Active
// precedentemente
if (alreadyActive){
System.out.println(this.getClass().getName()+" : Hello Again
TV World! ");
}else {
System.out.println(this.getClass().getName()+" : Hello TV
World");
alreadyActive = true;
}
}
// Con questo metodo la Xlet viene messa in pausa e dovrebbe
// rilasciare tutte le risorse utilizzate
public void pauseXlet(){
System.out.println(this.getClass().getName()+" : Xlet in
pausa!");
// notifichiamo al Context l'avvenuta pausa della Xlet
context.notifyPaused();
}
// Con questo metodo la Xlet viene terminata, tramite il
// parametro boolean la Xlet ha la facoltà di accettare
// tale richiesta; nel caso non accettasse di
// terminare dovrebbe lanciare un eccezione.
public void destroyXlet(boolean unconditional) throws XletStateChangeException {
if (unconditional) {
System.out.println(this.getClass().getName()+" : la Xlet è
stata terminata!");
}else {
System.out.println(this.getClass().getName()+
" : la Xlet ha accettato di essere terminata!");
}
// notifichiamo al Context l'avvenuta terminazione della Xlet
context.notifyDestroyed();
}
}
17
Capitolo 2: Applicazioni MHP
2.3 Gestione delle risorse scarse
I Set Top Box sono sistemi che presentano risorse limitate a livello hardware come
poca memoria e processori “lenti”, rispetto agli attuali PC in commercio, quindi per
poter utilizzare le varie periferiche: input (telecomando), output (decoder Mpeg2 per
visualizzare le immagini su video) e modem si devono usare delle accortezze, le
precedenti periferiche vengono chiamate “risorse scarse”.
Se le risorse non vengono gestite in maniera ottimale potrebbe succedere che
un'applicazione non sia in grado di funzionare correttamente o non sia in grado di
sfruttare tutte le potenzialità del STB. Le API che andremo a considerare sono le
resource notification API che sono state definite da DAVIC nel package
org.davic.resource . Una cosa da tenere in considerazione è che queste non sono
API di gestione delle risorse, in quanto questa viene lasciata al middleware del
ricevitore ma ben si di notifica. Servono quindi a notificare all'applicazione se una
risorsa è stata tolta, oppure se è richiesta da un'altra applicazione concorrente.
Le resource notification API sono sostanzialmente composte da tre classi:
•
ResourceServer
•
ResourceClient
•
ResourceProxy
Tali classi non hanno funzionalità autonoma ma forniscono dei concetti comuni ai quali
le API che definiremo di “ utilizzo” dovranno attenersi per svolgere i loro compiti.
Questo significa che essendo queste delle
“interface” saranno le classi che le
implementano a definirne nello specifico i loro compiti.
Queste API si basano sul modello client-server con piccole variazioni.
L'interfaccia ResurceServer è implementata da quelle API di utilizzo responsabili di
effettuare la richiesta di risorse scarse, e di gestirne la loro allocazione. Tali risorse
scarse potrebbero essere di tipo software, ad esempio il section filter, oppure di tipo
hardware come per esempio un modem oppure un decoder MPEG, le API non fanno
distinzione e le trattano tutte in maniera simile.
Il codice sorgente dell'interfaccia ResurceServer è il seguente:
18
Capitolo 2: Applicazioni MHP
public interface ResourceServer
{
public abstract void addResourceStatusEventListener(
ResourceStatusListener listener);
public void removeResourceStatusEventListener(
ResourceStatusListener listener);
}
Come si può vedere questa interfaccia non definisce dei metodi specifici per attuare una
richiesta di accesso esclusivo ad una risorsa scarsa oppure per rilasciarla, questa è una
scelta voluta perché sarà compito delle API di utilizzo fare questo nella maniera più
consona. Gli unici metodi che questa interfaccia definisce permettono di registrare un
ascoltatore degli eventi che riporterà i cambiamenti di stato della nostra risorsa.
Il modo con cui noi riserviamo una risorsa come il modem è infatti completamente
differente da quello con cui gestiamo il un decodificatore MPEG per esempio.
Vedremo in seguito come la classe ConnectionRCInterface attraverso il metodo
reserve() andrà a richiedere l'accesso esclusivo sul modem.
L'interfaccia ResurceClient è come si può capire dal nome il lato client delle API.
Questa interfaccia viene implementata da quelle classi che richiedono l'accesso alla
risorsa scarsa, e viene utilizzata dal middleware per richiedere il rilascio della risorsa
oppure per informarla che il rilascio è già avvenuto.
Il codice sorgente è il seguente:
public interface ResourceClient
{
public abstract boolean requestRelease(
ResourceProxy proxy,
Object requestData);
public abstract void release(
ResourceProxy proxy);
public abstract void notifyRelease(
ResourceProxy proxy);
}
19
Capitolo 2: Applicazioni MHP
Come possiamo vedere questa interfaccia mette a disposizione del middleware tre
metodi per comunicare all'applicazione di rilasciare la risorsa.
Il metodo requestRelease() è il meno incisivo dei tre perché lascia al client di
scegliere se soddisfare la richiesta oppure rifiutare. Nel caso in cui il client abbia ancora
bisogno di trattenere la risorsa ritornerà false e il middleware dovrà se necessario
attuare un sistema di rilascio forzato. Altrimenti nel caso in cui la risorsa non è più
necessaria per il client esso riporterà true ed il middleware potrà procedere con il
rilascio.
Il metodo release() è il più aggressivo perché obbliga al nostro client di rilasciare la
risorsa senza scelta. Quando il metodo ritorna, il middleware sarà certo di poter fornire
la risorsa ad un altro client che ne fa richiesta. Se il metodo release() non ritorna in
tempo accettabile il middleware può ritenere che l'applicazione sia andata in crash e
metterà comunque a disposizione la risorsa rimuovendone i diritti di utilizzo. Se
succede questo il middleware chiamerà il metodo notifyRelease() dell'interfaccia
ResourceClient, andando a informare l'applicazione che è stata privata della risorsa
scarsa e che dovrà quindi agire terminando i processi che la impiegavano. Questa è una
operazione brutale per il client ma viene utilizzata quando questo non collabora con il
middleware.
L’interfaccia ResourceProxy mette a disposizione solo un metodo di notifica utilizzato
per restituire il client della risorsa a cui esso fa riferimento ed è composta come di
seguito:
public interface ResourceProxy {
public ResourceClient getClient();
}
La classe che implementa ResourceProxy si pone nel mezzo tra il client e la risorsa da
utilizzare questo porta ad un grande livello di sicurezza perché l’applicazione ottiene un
accesso mediato alla risorsa e ciò per permettere al middleware di controllarla.
Le classi che implementano il proxy mettono a disposizione dei metodi che possono
essere utilizzati per fare il set-up delle risorse. In questo modo quando si attuano più
cicli di request/release delle risorse non si dovrà tutte le volte settare i parametri ma
20
Capitolo 2: Applicazioni MHP
questi verranno forniti dal proxy precedentemente impostato. Con una risorsa come il
modem, dove ogni volta, per effettuare una connessione, bisogna fornire numero
telefonico, username e password questo comporta un elevato risparmio di tempo.
Un altro beneficio portato è che le classi che implementano il client possono allacciarsi
a più proxy differenti, ognuno dei quali capace di fornire un setting differente per i
parametri della risorsa, sarà poi l’applicazione a decidere quali proxy utilizzare nel
momento in cui ne fa richiesta.
Vediamo di capire meglio cosa succede quando un’applicazione richiede l’accesso a
una risorsa seguendo i passi di seguito illustrati:
1. Innanzi tutto l’applicazione, o meglio le classi che implementano il client
creano un’istanza del ResourceProxy appropriato e settano tutti i parametri
del caso in base alla risorsa che andranno a utilizzare:
2. Il client va a chiamare un metodo appropriato sulle API che implementano il
ResourceServer
e
richiedono
accesso
alla
risorsa
passando
il
ResourceProxy come parametro:
3. Se l’accesso alla risorsa è garantito il server chiama un metodo privato sul
proxy per convalidarlo:
21
Capitolo 2: Applicazioni MHP
4. Si stabilisce un collegamento diretto tra il proxy e la risorsa, collegamento
che fino a questo momento non esisteva per ragioni di sicurezza:
5. A questo punto la nostra applicazione può cominciare ad utilizzare la risorsa
utilizzando i metodi del proxy,sarà poi questo a riportare le richieste alla
risorsa:
Il processo di rilascio delle risorse è sostanzialmente l’inverso di quello
precedentemente illustrato:
1. Quando il client ha terminato di utilizzare una risorsa chiama un metodo
appropriato sul server per rilasciare la risorsa passandogli il proxy come
parametro:
22
Capitolo 2: Applicazioni MHP
2. Ora il server chiamerà un metodo appropriato sul proxy per invitarlo a rilasciare
la risorsa a cui fa riferimento invalidandolo:
3. Nel caso in cui l’applicazione è cooperante il proxy procede con il rilascio della
risorsa:
Vediamo cosa succede quando un’applicazione “maliziosa” non vuole rilasciare la
risorsa, ed interviene il middleware:
1. il server chiama il metodo release() sul client;
2. il client fallisce nel ritorno da questa chiamata e continua a trattenere la
risorsa;
3. dopo un certo periodo di tempo il middleware notifica al server che la
richiesta non ha avuto un responso;
23
Capitolo 2: Applicazioni MHP
4. il server chiama un metodo privato sul proxy per informarlo che non e più
valido e che non può più trattenere la risorsa;
5. il proxy rompe il collegamento con la risorsa;
6. il middleware chiama allora il metodo notifyRelease() su quella classe
che
implementa
l’interfaccia
ResourceClient.
In
questo
modo
l’applicazione sa che non ha più accesso alla risorsa e che si dovrà
comportare di conseguenza;
7. tutti i futuri tentativi di accesso alla risorsa da parte dell’applicazione non
avranno alcun effetto.
L’aver descritto in maniera cosi approfondita, come avviene la gestione delle risorse e
quali sono le API Java che ci permettono di fornire supporto per la notifica di gestione
da parte del middleware, ci da la possibilità in futuro di introdurre i package che
contengono le classi da utilizzare per stabilire una connessione, impostarne i parametri,
settare un background, spostare e/o ridimensionare il video che come vedremo
implementano le classi del package org.davic.resource.
24
Capitolo 2: Applicazioni MHP
2.4 Modello grafico
Il modello grafico definito dallo standard MHP è basato su tre differenti piani (o layers)
come mostrato in Figura 2.4.1.
Fig. 2.4.1 - Layer grafici dell'MHP
Partendo da dietro, rispetto a chi guarda la TV, troviamo il Background layer che può
contenere un solid color o una still image (rappresentata da un particolare frame MPEG2, quello di tipo I), avanti troviamo il Video layer rappresentato dal flusso A/V del
canale TV o di una qualsiasi altra fonte in formato MPEG-2.
L’ultimo livello è il Graphics layer che può contenere la grafica creata nella Xlet, che a
sua volta può essere strutturata su n-livelli sovrapposti.
Tutti e tre i livelli lavorano ad una risoluzione standard di 720x576 o 768x576 se viene
impostato il modo di visualizzazione su schermo 4:3.
Per chi è abituato a fare applicazioni grafiche su PC non ci sono molte differenze, ma è
il caso di tenere in considerazione alcuni aspetti:
•
i pixel non sono perfettamente quadrati;
•
il file che costituisce il background deve essere un’immagine video in formato
mpeg2 (.mpg) in loop e non un immagine statica (.jpg), dato che il STB usa per
la sua visualizzazione lo stesso decoder del video layer. (Programma di
conversione consigliato .jpg -> .mpg “ImageMagic”);
25
Capitolo 2: Applicazioni MHP
•
le linee spesse 1 pixel non vengono visualizzate perfettamente o addirittura non
sono visibili (consigliato spessore >=2);
•
le coordinate assolute (x,y) partono da 0,0 angolo superiore sinistro;
•
possono essere usate coordinate indipendenti dalla risoluzione chiamate
“normalizzate” e si esprimo con numeri decimali da 0.0 a 1.0 supportate nelle
API Havi;
•
nella gestione del livello grafico è assicurata la visualizzazione completa in un
area ridotta rispetto alle coordinate totali, detta SAFE AREA di ampiezza
562x460.
0,0
79,58
Safe Area
641,518
720,576
Dopo aver visto le accortezze da usare andiamo ad analizzare in dettaglio, con porzione
di codice commentato, i tre livelli di grafica.
2.4.1 Background layer
Per settare uno sfondo sulla nostra applicazione bisognerà innanzitutto tener conto che
la periferica che andremo ad utilizzare, il decoder mpeg2 è una risorsa scarsa, quindi per
effettuare qualsiasi tipo di settaggio bisognerà prima acquisirla usando le resource
notification API introdotte nei paragrafi precedenti.
Il package più importante da importare per la gestione di questo layer è org.havi.ui.* ,
riportiamo la classe più rilevante “HScreen”:
26
Capitolo 2: Applicazioni MHP
Method Summary
HBackgroundConfiguration getBestConfiguration(HBackgroundConfigTempla
te[] hbcta)
Returns an HBackgroundConfiguration from an
HBackgroundDevice which is present on this HScreen
that best matches at least one of the specified
HBackgroundConfigTemplates, or null if this is not
possible.
HGraphicsConfiguration getBestConfiguration(HGraphicsConfigTemplate
[] hgcta)
Returns an HGraphicsConfiguration from an
HGraphicsDevice which is present on this HScreen
that best matches at least one of the specified
HGraphicsConfigTemplates.
HVideoConfiguration getBestConfiguration(HVideoConfigTemplate[])
Returns an HVideoConfiguration from an
HVideoDevice which is present on this HScreen that
best matches at least one of the specified
HVideoConfigTemplates.
HScreenConfiguration[] getCoherentScreenConfigurations(HScreenConfi
gTemplate[] hscta)
Return a coherent set of
HScreenConfigurations matching a set of templates.
HBackgroundDevice getDefaultHBackgroundDevice()
Return the default background device for this
screen.
HGraphicsDevice getDefaultHGraphicsDevice()
Return the default graphics device for this
screen.
static HScreen getDefaultHScreen()
Returns the default HScreen for this application.
HVideoDevice getDefaultHVideoDevice()
Return the default video device for this screen.
HBackgroundDevice[] getHBackgroundDevices()
Returns a list of background devices for this
screen.
HGraphicsDevice[] getHGraphicsDevices()
Returns a list of graphics devices for this screen.
static HScreen[] getHScreens()
Returns all HScreens in this system.
HVideoDevice[] getHVideoDevices()
Returns a list of video device for this screen.
27
Capitolo 2: Applicazioni MHP
Vediamo in breve le operazioni da eseguire: per prima cosa si sceglie lo schermo da
usare mediante l’oggetto HScreen, a questo oggetto bisogna associare una
HGraphicsDevice che conterrà il set d’impostazioni assegnate per mezzo di
HGraphicsConfigTemplate.
Il set di ogni singola impostazione è eseguito dal metodo setPreference al quale viene
passato il parametro e la priorità; sarà poi il midlleware a decidere se il settaggio
richiesto è attuabile in base alla priorità data e alle esigenze delle altre eventuali
applicazioni che sono in running.
Conviene
creare
una
classe
che
implementa
obbligatoriamente
org.davic.resources.ResourceClient dove inserire tutti i metodi per l’acquisizione, il
settaggio e il rilascio della risorsa.
Di seguito viene riportata la classe “BackgroundController”, creata da Steve Morris,
che mostra in dettaglio oltre alle fasi sopra elencate la gestione delle eccezioni .
/**********************************************************************************
* THIS SOFTWARE IS COPYRIGHT STEVEN MORRIS 2002. ALL RIGHTS RESERVED
* THIS SOFTWARE IS FREE FOR NON COMMERCIAL USE FOR THE PURPOSE OF LEARNING
*MHP. ANY USE FOR OTHER PURPOSES IS PROHIBITED UNLESS WRITTEN AGREEMENT
* IS OBTAINED.
* DISTRIBUTION OF THIS CODE IS PROHIBITED
*/
// Import the HAVi UI classes that we need for this
import org.havi.ui.HScreen;
import org.havi.ui.HBackgroundDevice;
import org.havi.ui.HBackgroundConfiguration;
import org.havi.ui.HBackgroundImage;
import org.havi.ui.HStillImageBackgroundConfiguration;
import org.havi.ui.HConfigurationException;
import org.havi.ui.HPermissionDeniedException;
import org.havi.ui.HBackgroundConfigTemplate;
// Since some of the graphics configuration requires scarce resources, we
// need to use the DAVIC resource notification API
import org.davic.resources.*;
// We need these (under some circumstances) to let our background be seen
import javax.tv.service.selection.*;
import javax.tv.media.AWTVideoSizeControl;
import javax.tv.media.AWTVideoSize;
import java.awt.Rectangle;
import javax.tv.xlet.XletContext;
28
Capitolo 2: Applicazioni MHP
/**
* This class handles the display of background images in an easy-to
* use way. Setting this up can be quite complex, and so in the case
* we've relegated it to a separate class to make it a little easier to
* understand.
*/
class BackgroundController implements org.davic.resources.ResourceClient{
// The background device that we will use for displaying the image
private HBackgroundDevice backdevice;
// The configuration for that background device
private HStillImageBackgroundConfiguration backconfig;
/**
* This method will initialise the video and graphics devices to allow
* them to display the background image.
*/
public boolean init(){
// We first need to get the background device that we will use for displaying the image. To do
//this, we need to first decide which
// HScreen we will use. In this case (and most others), we will use
// the default one.
HScreen screen = HScreen.getDefaultHScreen();
// Once we have that, we get the default background device for that HScreen
HBackgroundDevice backdevice = screen.getDefaultHBackgroundDevice();
// Once we have a reference to the device, we can get a configuration for it.
// Get a configuration that we can use
HBackgroundConfigTemplate backgroundConfigurationTemplate = new
HBackgroundConfigTemplate();
backgroundConfigurationTemplate.setPreference(HBackgroundConfigTemplate.FLICKER_FILTERING
,HBackgroundConfigTemplate.REQUIRED);
HBackgroundConfiguration backconfig;
backconfig = backdevice.getBestConfiguration(backgroundConfigurationTemplate);
// Can we reserve the device so that we can change the settings on it?
if (backdevice.reserveDevice(this)){
// If we can, we set the configuration of the background device to
// the one we got above - this is the default configuration for this device.
try{
backdevice.setBackgroundConfiguration(backconfig);
}catch (Exception ex){
System.out.println("Can't initialise the background device");
// Release the device so that other applications can use it, if necessary.
backdevice.releaseDevice();
return false;
}
// We need to check if we can put an image in the background in
// this configuration, since we can't do this in every configuration.
if(backconfig instanceof HStillImageBackgroundConfiguration){
// We can use this
this.backconfig = (HStillImageBackgroundConfiguration)backconfig;
this.backdevice = backdevice;
return true;
}else{
// If we can't, we again release the device since it's no use to us.
backdevice.releaseDevice();
}
}
return false;
}
29
Capitolo 2: Applicazioni MHP
/**
* Free the resources we needed to display background images.
* Some implementations leave the image there, but there is an explicit
* warning in the MHP specification that this may not happen on all
* implementations. If you want to be sure that your image is still
* visible, don't do this.
*/
public void dispose(){
// Check if we have something to dispose of
if (backdevice != null){
// RZlease the device and clear any references
backdevice.releaseDevice();
backdevice = null;
backconfig = null;
}
}
/**
* Display a background image
*/
public void display(String filename){
// Check we have the resources we need to display a background image
if(backconfig != null) {
// Create a new background image. The image is loaded from the
// filename that we pass in.
HBackgroundImage backimage = new HBackgroundImage(filename);
// Now display the image. This can throw several exceptions, so we
// enclose it in a 'try' block
try {
backconfig.displayImage(backimage);
}
catch (java.io.IOException ioe) {
// Ignore it, but print a message to tell the user what's happened.
System.out.println("Can't display background image - IO exception");
ioe.printStackTrace();
}
catch (HPermissionDeniedException hpde) {
// We don't have permission to displayan image. We just ignore it.
System.out.println("Can't display background image - permission denied");
}
catch (HConfigurationException hce) {
// We don't have permission to displayan image. We just ignore it.
System.out.println("Can't display background image - configuration exception");
hce.printStackTrace();
}
}
}
30
Capitolo 2: Applicazioni MHP
Implementazione metodi di org.davic.resource:
/**************************************************************************
*
* These methods are inherited from the ResourceClient interface and are used
* to tell the application when it has lost access to its resources (or
* when it is about to lose access to them). This gives the application a
* chance to clean up when it loses access to a resource, and gives it a
* chance to handle things gracefully.
*/
/**
* This method gets called when the resource manager requests that we give
* up a resource. We can refuse to do so, and that's what we do in this
* case (even though we shouldn't).
*/
public boolean requestRelease(ResourceProxy proxy, Object requestData) {
return false;
}
/**
* This method gets called when the resource manager informs us that we must
* release a resource
*/
public void release(ResourceProxy proxy) {
// release control of the background device
backdevice.releaseDevice();
}
/**
* This method gets called when the resource manager tells us we've
* lost access to a resource and we should clean up after ourselves.
*/
public void notifyRelease(ResourceProxy proxy) {
// release control of the background device. Even though we don't
// have control of it, this makes sure everything is synchronized.
backdevice.releaseDevice();
}
L’ultima cosa da aggiungere per quanto riguarda la gestione di questo livello è che, per
rendere visibile il background, bisognerà ridimensionare o nascondere del tutto il livello
superiore, ovvero il video layer.
Trascurando la gestione del primo livello si possono implementare interfacce usando il
livello grafico e come sfondo avranno la trasmissione corrente del canale dove si è
sintonizzati.
31
Capitolo 2: Applicazioni MHP
2.4.2 Video layer
La gestione del video layer è molto importante non solo per l’aspetto estetico della
nostra interfaccia grafica.
Non dobbiamo dimenticare, che il broadcaster ha si interessi a fornire l’applicazione
MHP, ma si deve preoccupare anche della trasmissione che sta andando in onda.
Mentre in una fase iniziale, nelle prime applicazioni, si preferiva ridimensionare il video
e spostarlo in modo da non sovrapporsi a menù, pulsanti etc. ora la tendenza e di
lasciarlo invariato il più possibile, usando trasparenze o sovrapposizioni di grafica
temporanee, in modo da distogliere il meno possibile l’attenzione del telespettatore
dalla trasmissione.
Comunque ci sono casi in cui l’applicazione può pretendere e merita tutta l’attenzione
dell’utente, basta pensare all’inserimento dati o alla conferma di un movimento in una
applicazione e-banking per esempio.
Quindi è bene alternare fasi, come la navigazioni di menù, dove il video sia in primo
piano e altre in cui venga eliminato o ridotto.
Per realizzare tutto ciò ci vengono in aiuto le classi javaTV della Sun, ecco un esempio
di due metodi per nascondere o ridimensionare e posizionare il video tratti dalla classe
“BackgroundController”, creata da Steve Morris.
import javax.tv.service.selection.*;
import javax.tv.media.AWTVideoSizeControl;
import javax.tv.media.AWTVideoSize;
import java.awt.Rectangle;
import javax.tv.xlet.XletContext;
/**
* Hide the video that may be obscuring our background
*/
public void hideVideo(XletContext context) {
// The background may be hidden by the video when the Xlet starts up. To
// solve this, we have to do two things:
// 1) stop the video
// 2) resize the video so that we can see the app underneath it. This is
// not always necessary, but some emulators (xletview, I'm looking at you)
// don't hide the video when you stop it
// Get a reference to the JavaTV ServiceContextFactory
ServiceContextFactory factory;
factory = ServiceContextFactory.getInstance();
32
Capitolo 2: Applicazioni MHP
// From this, we can get a reference to the parent
// service context of our Xlet. To do this, we need a
// reference to our Xlet context. It's times like this
// that show why your application should always keep a
// reference to its Xlet context
ServiceContext myContext = null;
try {
myContext = factory.getServiceContext(context);
}
catch (Exception e) {
e.printStackTrace();
}
if (myContext != null) {
// ServiceContentHandler objects are responsible for
// presenting the different parts of the service. This
// includes the media components
ServiceContentHandler[] handlers;
handlers = myContext.getServiceContentHandlers();
for(int i=0; i < handlers.length ; i++) {
if (handlers[i] instanceof ServiceMediaHandler) {
// This is a Player for part of the service, since
// ServiceMediaHandler objects are instances of JMF
// Player objects
javax.media.Player p = (javax.media.Player) handlers[i];
// this may be all we need to do in order to see our background...
p.stop();
p.deallocate();
// ...but some emulator require more
AWTVideoSizeControl awtVideoSizeControl;
awtVideoSizeControl = (AWTVideoSizeControl)
p.getControl("javax.tv.media.AWTVideoSizeControl");
// in this case, we set the size of the video to be 0, but we could set it
// to display on part of the screen
awtVideoSizeControl.setSize(new AWTVideoSize(new Rectangle(0, 0, 720, 576), new
Rectangle(0,0,0,0)));
}
}
}else {
System.out.println("Can't get our service context. May not be able to " +
"resize the video, so our background may not be visible");
}
}
public void resizeVideo(XletContext context,int x,int y,int l,int h) {
// The background may be hidden by the video when the Xlet starts up. To
// solve this, we have to do two things:
// 1) stop the video
// 2) resize the video so that we can see the app underneath it. This is
// not always necessary, but some emulators (xletview, I'm looking at you)
// don't hide the video when you stop it
// Get a reference to the JavaTV ServiceContextFactory
ServiceContextFactory factory;
factory = ServiceContextFactory.getInstance();
33
Capitolo 2: Applicazioni MHP
// From this, we can get a reference to the parent
// service context of our Xlet. To do this, we need a
// reference to our Xlet context. It's times like this
// that show why your application should always keep a
// reference to its Xlet context
ServiceContext myContext = null;
try {
myContext = factory.getServiceContext(context);
}
catch (Exception e) {
e.printStackTrace();
}
if (myContext != null) {
// ServiceContentHandler objects are responsible for
// presenting the different parts of the service. This
// includes the media components
ServiceContentHandler[] handlers;
handlers = myContext.getServiceContentHandlers();
for(int i=0; i < handlers.length ; i++) {
if (handlers[i] instanceof ServiceMediaHandler) {
// This is a Player for part of the service, since
// ServiceMediaHandler objects are instances of JMF
// Player objects
javax.media.Player p = (javax.media.Player) handlers[i];
// this may be all we need to do in order to see our background...
// ...but some emulator require more
AWTVideoSizeControl awtVideoSizeControl;
awtVideoSizeControl = (AWTVideoSizeControl)
p.getControl("javax.tv.media.AWTVideoSizeControl");
// in this case, we set the size of the video to be 0, but we could set it
// to display on part of the screen
awtVideoSizeControl.setSize(new AWTVideoSize(new Rectangle(0, 0, 720, 576), new
Rectangle(x,y,l,h)));
}
}
}else {
System.out.println("Can't get our service context. May not be able to " +
"resize the video, so our background may not be visible");
}
}
Prima di ridimensionare o eliminare addirittura il video layer è bene aver impostato una
immagine nel background layer come descritto nel paragrafo precedente, onde
intercorrere a videate nere o addirittura alla non visualizzazione del livello grafico che
ora andremo a trattare.
34
Capitolo 2: Applicazioni MHP
2.4.3 Graphics layer
Il livello grafico è supportato da diversi package messi a disposizione dallo standard;
uno dei fondamentali è Havi 1.1.
In tale package il container di più alto livello in una Xlet è rappresentato dalla classe
org.havi.ui.HScene, che concettualmente è equivalente a java.awt.Window o
java.awt.Frame ma con caratteristiche specifiche per i decoder digitali, ne riportiamo i
metodi principali:
Method Summary
void dispose()
Disposes of this HScene.
java.awt.Image getBackgroundImage()
Retrieve any image used as a background for this HScene.
int getBackgroundMode()
Get the background mode of this HScene.
boolean isOpaque()
Returns true if the entire HScene area, as given by the
java.awt.Component#getBounds method, is fully opaque, i.e.
boolean isVisible()
Determines if the HScene (or more properly its added child
components) is Visible.
void paint(java.awt.Graphics g)
HScene objects override the paint method (defined in
java.awt.Component) to paint the added "child" components on
top of an optional background color or image.
void setActive(boolean focus)
Set whether the HScene is prepared to accept focus.
void setBackgroundImage(java.awt.Image image)
Set an image which shall be painted in the background of
the HScene, after the background has been drawn according to the
current mode set with setBackgroundMode, but before any
children are drawn.
void setBackgroundMode(int mode)
Set the background mode of this HScene.
void setVisible(boolean visible)
Shows or hides this HScene depending on the value of the
input parameter visible.
void show()
Shows this HScene, and brings it to the front.
35
Capitolo 2: Applicazioni MHP
La classe factory che ci permette di richiedere l'unica istanza della HScene è fornita
dallo stesso package e si chiama org.havi.ui.HSceneFactory.
MHP supporta anche le classi AWT contenute nella versione 1.1.8 del JDK Sun ma
spesso è possibile trovare un equivalente nel package Havi e che quindi dovrebbe essere
preferita (es: org.havi.ui.HContainer è più specifica per MHP di java.awt.Container).
Oltre ad Havi e AWT è possibile utilizzare anche un package specifico DVB org.dvb.ui
dove è possibile trovare classi di utilità come la DvbColor che permette di gestire anche
le trasparenze tramite il parametro alpha non presente nella classe java.awt.Color.
Data l’importanza vediamone un tipo di costruttore:
DVBColor (int r, int g, int b, int a) dove r,g,b sono i colori primari red, green, blue e
“a”- alpha è la trasparenza, tutti e quattro i parametri hanno un range da 0 a 255.
Non tutti i tipi di decoder MHP gestiscono le trasparenze in questo modo, ma lo
standard garantisce almeno 3 livelli: 0 % (opaco), 100 % (completamente trasparente),
30 % di trasparenza.
Il font di sistema supportato in MHP è il “Tiresias”, che deve essere disponibile almeno
nelle seguenti dimensioni:
•
36 punti (Title)
•
31 punti (Subtitle)
•
26 punti (Body)
•
24 punti (Footnote)
I formati grafici supportati sono: png, jpg, gif e mpg (I-Frame).
Vediamo un esempio di codice che recupera l'istanza di HScene e ci posiziona un
HContainer e un HText.
HSceneFactory hsf = HSceneFactory.getInstance();
HScene scene = hsf.getFullScreenScene(
HScreen.getDefaultHScreen().getDefaultHGraphicsDevice());//
risoluzione a schermo pieno
scene.setSize(720,576);
scene.setLayout(null);
HContainer cont = new HContainer(50,50,650,500);
HText text = new HText("Casella di testo!",160, 200, 300, 60,
new Font("Tiresias", Font.PLAIN, 26),Color.black,Color.white,
new HDefaultTextLayoutManager());
cont.add(text);
scene.add(cont);
scene.setVisible(true);
scene.repaint();
36
Capitolo 2: Applicazioni MHP
Un altro oggetto molto utile nella creazione di un’interfaccia è HIcon che permette di
caricare un immagine e visualizzarla, per esempio per creare un bottone, in dettaglio:
HIcon
(java.awt.Image image,
int x,
int y,
int width,
int height)
utilizzando
java.awt.Toolkit ecco il nostro bottone:
bottone = new HIcon(Toolkit.getDefaultToolkit().getImage("bottone.jpg"), 50, 100, 140, 30);
Per dare facilmente l’effetto che il pulsante venga premuto si possono creare due oggetti
HIcon con le rispettive immagini, pulsante rilasciato/pulsante premuto, posizionati sulle
stesse coordinate e agire con il metodo setVisible(true/false) per nascondere l’uno e
visualizzare l’altro.
37
Capitolo 2: Applicazioni MHP
2.5 Gestione degli eventi del telecomando
La gestione del telecomando è banale per chi conosce Java, si basa sugli ascoltatore di
eventi; in MHP si possono usare i package org.havi.ui.event che necessita del focus di
un’ oggetto grafico oppure org.dvb.event.
Iniziamo dal primo caso, nel package sono definite le costanti che corrispondono al
codice restituito dal metodo getKeyCode di java.awt.event.KeyEvent ad ogni pressione
di un tasto.
L’ascoltatore
degli
eventi
va
scene.addKeyListener((KeyListener)this)
aggiunto
e
rimosso
nel
nel
metodo
metodo
initXlet:
destroyXlet:
scene.removeKeyListener((KeyListener)this).
Come mostrato in Figura 2.5.1 gli eventi associati alla pressione dei un tasto del
telecomando definiti nell'MHP sono solamente quelli numerici, le frecce di navigazione,
il tasto “OK”, il tasto teletext e i quattro tasti colorati (rosso,verde,giallo,blu).
Fig. 2.5.1 - Input event in MHP
Da notare che lo standard MHP non definisce eventi per i tasti “EXIT” e “BACK” che
sono
presenti
sulla
quasi
totalità
dei
decoder
e
comunque
gestiti
da
org.havi.ui.event.HRcEvent ma potrebbero generare eventi non-standard e non sempre
coerenti sui diversi apparecchi.
Vediamo ora un esempio di codice necessario per l’implementazione del primo
metodo, il codice seguente deve risiedere nella classe principale della Xlet.
38
Capitolo 2: Applicazioni MHP
public void keyPressed (KeyEvent key) {
int pulsantePremuto = key.getKeyCode();
switch(pulsantePremuto){
//Tastierino numerico
case KeyEvent.VK_0:
case KeyEvent.VK_1:
case KeyEvent.VK_2:
case KeyEvent.VK_3:
case KeyEvent.VK_4:
case KeyEvent.VK_5:
case KeyEvent.VK_6:
case KeyEvent.VK_7:
case KeyEvent.VK_8:
case KeyEvent.VK_9:
//Tasti colorati
case HRcEvent.VK_COLORED_KEY_0:
//rosso
case HRcEvent.VK_COLORED_KEY_1:
//verde
case HRcEvent.VK_COLORED_KEY_2:
//giallo
case HRcEvent.VK_COLORED_KEY_3:
//blu
//Tasti direzionali
case HRcEvent.VK_UP:
case HRcEvent.VK_DOWN:
case HRcEvent.VK_RIGHT:
case HRcEvent.VK_LEFT:
case HRcEvent.VK_ENTER:
//OK
//Vengono passati gli eventi.
interfaccia.keyPressed(key);
break;
//Alla pressione del tasto EXIT del telecomando si
//richiama il metodo destroyXlet, per fermare la xlet.
case HRcEvent.VK_ESCAPE:
try{
destroyXlet(true);
}
catch (XletStateChangeException xsce){
System.out.println("Premuto tasto EXIT "+xsce.toString());
}
break;
default:
break;
}
}
public void keyTyped(KeyEvent ignored) { }
public void keyReleased(KeyEvent ignored) { }
39
Capitolo 2: Applicazioni MHP
Essendo il primo metodo ampiamente documentato e conosciuto vediamo nel dettaglio
quello specifico del DVB. Tale meccanismo è consigliabile rispetto ad AWT perché
permette un uso più oculato e collaborativo nella gestione degli eventi. Infatti rispetto
ad AWT dove la registrazione degli eventi è esclusiva e comprende tutti gli eventi
possibili, nel modello dvb è possibile registrarsi per la notifica dei soli eventi a cui si è
realmente interessati.
Le classi principali di questo package sono la EventManger e la UserEventRepository
che gestiscono rispettivamente la registrazione degli eventi e il repository degli eventi a
cui si è interessati.
UserEventRepository repository = new userEventRepository("UserRepository");
repository.addAllColourKeys();
EventManager manager = EventManager.getInstance();
// this rappresenta la classe listener
manager.addUserEventListener(this, repository);
Questo approccio ha l'ulteriore vantaggio di non imporre alla Xlet di richiedere il focus
tramite la HScene (come prevede il modello awt sui Component) risulta quindi essere
l'unico approccio da seguire in tutte quelle applicazioni in cui si vogliono ricevere
eventi del telecomando ma non si dispone una GUI.
40
Capitolo 3: Canale di ritorno
Capitolo 3
Canale di ritorno
3.1 Lo scenario
L’infrastruttura trasmissiva per il digitale terrestre consente l’invio broadcast di
contenuti televisivi, radio ed informatici (es. dati) a ricevitori DTT quali: televisori
associati ad un “decoder” supplementare o Set Top Box, televisori digitali corredati di
decoder interno, altri apparecchi (sintoamplificatori, videoregistratori, sistemi AV
compatti, etc.) che incorporano un sintonizzatore DTT.
A ciascun “programma” (televisivo, radio, informatico) il Content Provider può
associare un servizio informazioni sul programma, un servizio Teletext tradizionale, un
servizio “super Teletext” (“Enhanced Broadcasting”) supportato dallo standard “mhp”
(Multimedia Home Platform); quest’ultimo, infatti, realizza e presenta sullo schermo del
televisore “pagine” contenenti testo e grafica al cui interno è possibile “navigare” in
modo molto simile ad Internet.
Se poi il ricevitore DTT è corredato da un modem (analogico, ISDN o xDSL),
connettendosi ad Internet o ad altre reti di telecomunicazioni, la DTT diventa lo
strumento attraverso il quale sviluppare servizi interattivi più o meno estesi (da forme
minimali di “televoto” realizzato collegandosi a numeri telefonici o a indirizzi Internet
corrispondenti al voto che si vuole esprimere, fino all’impiego di sofisticate
applicazioni di commercio elettronico, e-government, fornitura di servizi Video On
Demand, etc). Più in generale, l’interattività estesa è destinata all’impiego di servizi
Internet.
41
Capitolo 3: Canale di ritorno
Fig. 3.1.1 - Scenario canale di ritorno
Tutte tali possibilità (che si comprendono meglio utilizzando un ricevitore) sono
ampiamente chiarite già nell’introduzione dallo Standard ETSI ES 201 812 V1.1.1
(2003-12) relativo e Digital Video Broadcasting (DVB) e Multimedia Home Platform
(MHP) Specification 1.0.3 , capitolo 0, paragrafo 0.2:
“… At the beginning the following application areas are considered - Enhanced
Broadcasting, Interactive Broadcasting and Internet Access. Enhanced Broadcasting
combines digital broadcast of audio/video services with downloaded applications which
can enable local interactivity. It does not need an interaction channel. The application
area Interactive Broadcasting enables a range of
interactive services associated or independent from broadcast services. This application
area requires an interaction channel. The application area of Internet Access is
intended for the provisioning of Internet services. It also includes links between those
Internet services and broadcast services. …”
In pratica, il protocollo mhp è costituito da una raccolta di “metodi java” (programmi di
base) che consentono di accedere alle funzioni di base del ricevitore DTT e di far partire
automaticamente o manualmente, utilizzando il telecomando, le applicazioni java
trasmesse dal Content Provider Broadcaster. Queste “applicazioni Java” sono a tutti gli
42
Capitolo 3: Canale di ritorno
effetti dei programmi informatici che utilizzano, in luogo di un personal computer o di
un PDA, le capacita di elaborazione e lo schermo del ricevitore DTT. Pertanto, le
applicazioni di “Enhanced Broadcasting” costituiscono il presupposto per il lancio di
applicazioni di “Interactive Broadcating”, che consentono il lancio delle applicazioni di
“Internet Access” realizzate attraverso l’uso di reti PSTN, ISDN o xDSL (si prevede che
la prossima generazione di Set Top Boxes incorpori anche tecnologia WiFi).
Si noti che una volta attivato il “canale di interattività”, il ricevitore DTT può diventare
autonomo dalle applicazioni broadcast e, per esempio, accedere, puramente via Internet.
ad un servizio “pay per view” o “pay to download” via xDSL. Sempre attraverso il
“canale di interazione”, il ricevitore può continuare a ricevere nuovi programmi java
che sviluppano nuove funzionalità.
Di fatto, nel corso della attivazione, l’utente può configurare nel ricevitore DTT il
numero di telefono, il codice utente e la password fornitagli dal proprio fornitore di
accesso ad Internet, ma i servizi di “Enhanced Broadcasting” e “Interactive
Broadcating” possono imporre al ricevitore DTT interattivo di utilizzare per l’“Internet
Access” numero di telefono, codice utente e password diversi da quelli predefiniti
dall’utente.
In altre parole, l’accesso alle applicazioni di “Enhanced Broadcasting” rappresenta una
insuperabile barriera alla realizzazione e alla fornitura di servizi della società
dell’informazione diretti a quel 60% della popolazione che non usa il computer, ma che
dopo lo switch off previsto per il 31 dicembre 2008, userà un televisore DTT.
Accordi tra content provider su piattaforma DTT (che hanno il potere di associare al
programma televisivo o radiofonico opportune applicazioni) e alcuni operatori di rete
fissa, potrebbero avere l’effetto di una totale esclusione da tale mercato dei restanti
operatori di rete fissa. Il pericolo è particolarmente elevato nel caso di Telecom Italia
che, attraverso Telecom Italia Media, dispone già oggi di un accesso ai servizi di
“Enhanced Broadcasting” e “Interactive Broadcating” su piattaforma DTT, ma non è
da sottovalutare l’ipotesi che anche gli altri operatori di rete DTT possano seguire
politiche discriminatorie.
In assenza di adeguate misure volte a garantire l’accesso alle applicazioni di “Enhanced
Broadcasting” (che, si è visto, costituiscono il presupposto per il lancio di applicazioni
di “Interactive Broadcating”), l’accesso al “canale di ritorno” del decoder a condizioni
43
Capitolo 3: Canale di ritorno
oggettive e non discriminatorie tra i diversi operatori di telecomunicazioni e fornitori di
accesso ad Internet e ad assicurare l’ interoperabilità dei servizi, si potrebbe assistere ad
inquietanti fenomeni di “walled garden”, consistenti in un’offerta integrata di contenuti
Internet (es. Video On Demand, e-commerce, etc…), promossi per il tramite delle
applicazioni di Enhanced ed Interactive Broadcasting, accessibili mediante la DTT
(sfruttando il canale di ritorno del decoder) esclusivamente per il tramite di uno
specifico fornitore di accesso e contenuti Internet predeterminato dal fornitore della rete
e/o del servizio DTT, così impedendo l’accesso degli utenti del servizio DTT ai
servizi/contenuti/siti dei content providers non predeterminati dal gestore della rete e/o
del servizio DTT, che verrebbero ad essere relegati in una sorta di “universo recintato”,
a scapito della concorrenza, del pluralismo e, da ultimo, a danno degli utenti finali.
44
Capitolo 3: Canale di ritorno
3.2 I protocolli supportati
Per comprendere appieno le potenzialità del canale di ritorno, che si possono utilizzare,
partiremo dall’analisi dei protocolli supportati da MHP che sono mostrati in figura
3.2.1.
Fig. 3.2.1 - Protocolli canale di ritorno
3.2.1 Livello 1: Fisico
A livello fisico (Livello 1), si definiscono le caratteristiche del mezzo trasmissivo, del
tasso di trasmissione e gli schemi di codifica su cui avverrà la comunicazione. I
ricevitori DTT attualmente in commercio almeno per quanto riguarda quelli interattivi,
sono dotati al loro interno di un modem 56K che permette di aprire una connessione su
canale bidirezionale sfruttando la linea telefonica commutata.
Tuttavia il canale di ritorno via modem analogico (PSTN) presenta i seguenti problemi:
•
banda disponibile ridotta ;
•
impossibilità di uso contemporaneo del servizio di fonia sulla stessa linea
telefonica;
•
canale non always-on, con conseguente necessità di effettuare eventualmente
diverse aperture e chiusure di connessione durante una sessione di utilizzo del
servizio interattivo;
45
Capitolo 3: Canale di ritorno
•
tempi di attesa dovuti alla procedura di connessione;
•
costi di utilizzo basati sulla durata della connessione a circuito;
•
canale con flusso di dati bilanciato, proprietà non utile in molte applicazioni.
Lo standard PSTN (definito nelle specifiche ETSI ETS 300 801) è sicuramente uno
standard affermato e ancora di largo consumo, ma che limita enormemente quelle che
sono le possibilità di creare applicativi di alto livello. Uno dei vantaggi di questa scelta
è però la garanzia che vi sia un più vasto numero di utenti che può, data la ormai
affermata tecnologia e la vasta disponibilità sul territorio, sfruttare i servizi interattivi
messi a disposizione sul digitale terrestre. Il fatto che questa tecnologia sia accessibile
da quasi la totalità della popolazione è un fattore da non sottovalutare per poter rendere
il DTT una tecnologia aperta a tutti.
In futuro con una prevista evoluzione dei servizi messi a disposizione, si inizieranno a
vedere sul mercato STB di fascia media con supporto per linea ISDN oppure di fascia
alta con connessione a banda larga ADSL, anche se ci sarà ancora molto da aspettare
dati i costi attualmente più elevati rispetto a quelli di fasci bassa.
L’ ultima ipotesi è certamente la più interessante, perché da la possibilità di avere una
connessione sempre attiva, cosa non permessa dai normali ricevitori con modem PSTN
che chiudono la chiamata al Centro Servizi dopo un tempo prestabilito di inattività, ma
soprattutto darà la possibilità di portare sul digitale terrestre applicazioni che ora sono
riservate solo ai PC su Internet.
3.2.2 Livello 2: PPP
A livello di collegamento (Livello 2) si trova il protocollo PPP (Point-to-Point
Protocol) che è ad oggi il protocollo più utilizzato per il collegamento a Internet degli
host residenziali tramite doppino telefonico. Standardizzato in RFC 1661 e RFC 2152 .
Il PPP gestisce il riconoscimento degli errori, supporta molti protocolli, permette che gli
indirizzi IP siano stabiliti al momento della connessione e supporta l’autenticazione.
I vantaggi del PPP sono:
•
i frame vengono suddivisi in maniera tale da garantire una non ambigua
separazione tra un frame e quello successivo;
46
Capitolo 3: Canale di ritorno
•
possiede un protocollo di controllo di collegamento per la gestione delle linee
che prende il nome di LCP (Link Control Protocol);
•
le opzioni del livello di rete sono negoziate in maniera indipendente dal
protocollo che si utilizzerà a livello di rete. Si avrà quindi un diverso NCP
(Network Control Protocol) per ogni livello rete supportato.
Nel nostro caso vediamo come il STB riesce ad acquisire un indirizzo IP dal Centro
Servizi per poter poi comunicare con esso ed accedere ai servizi interattivi messi a
disposizione.
Innanzi tutto si esegue la chiamata verso il CS ad un numero che può essere univoco per
tutto il territorio impostato di default dall’utente oppure settato dalla Xlet.
Dopo che il modem del CS ha risposto e stabilito una connessione a livello fisico nel
nostro caso su linea commutata, il STB manda al router una serie di pacchetti LCP
inseriti nel campo Payload di uno o più frame PPP. I pacchetti inviati andranno a
selezionare i parametri PPP da utilizzare nella comunicazione.
Una volta selezionati i parametri, si passa all’invio dei pacchetti NCP per configurare il
livello rete. Nel nostro caso il ricevitore vuole eseguire un insieme di protocolli TCP/IP
e quindi necessita di ottenere un indirizzo IP.
Ogni CS avrà a disposizione un set di indirizzi IP da assegnare e ne verrà assegnato
dinamicamente uno a chi ne fa richiesta, una volta eseguita la connessione e per tutta la
sua durata.
A questo punto il nostro STB e formalmente un host Internet e può inviare e ricevere
pacchetti IP proprio come se fosse connesso fisicamente con CS.
La disconnessone avviene quando NCP sgancia la connessione a livello di rete e va cosi
a liberare l’indirizzo IP. Quindi LCP sgancia la connessione del livello di collegamento
e quindi la PPP ed infine il STB ordina al modem interno di liberare la linea telefonica
rilasciano la connessione a livelli fisico.
47
Capitolo 3: Canale di ritorno
3.2.3 Protocollo di rete: IP
Il livello di rete, è occupato dal protocollo IP (Internet Protocol), che nel suo datagram
incapsula tra i numerosi campi previsti, l’indirizzo IP del server di destinazione, cioè
quello del Centro servizi e quello del client, quindi quello assegnato dal CS stesso al
momento dell’autenticazione in maniera dinamica.
Il suo compito è quello di garantire che i pacchetti arrivino dalla sorgente, nel nostro
caso il STB, alla destinazione il CS, indipendentemente dalla presenza di reti intermedie
durante il suo percorso e indipendentemente dal fatto che più utenti stiano utilizzando
nello stesso momento l’applicazione per mettersi in contatto con il CS.
In pratica sarà il livello di trasporto che riceverà il flusso di dati ed andrà a
frammentarlo in datagram. Ogni datagram viene poi trasmesso e frammentato
ulteriormente durante il percorso, una volta che tutti i pezzi hanno raggiunto la
destinazione, vengono riassemblati dal livello di rete nel datagram di partenza.
Il datagram riassemblato viene poi passato al livello trasporto, che lo inserisce nel flusso
di input del processo ricevente.
Il protocollo IP supporta sopra di se i protocolli TCP e UDP entrambi previsti dalle
specifiche MHP.
3.2.4 Protocolli di trasporto: TCP / UDP
I protocolli di trasporto supportati sono sia il TCP che l’ UDP questi si differenziano
principalmente per il fatto che il primo e orientato alla connessione mentre il secondo e
sostanzialmente fondato sull’IP e non orientato alla connessione.
Il TCP (Transmission Control Protocol) progettato per garantire connessioni affidabili,
necessita di una entità di trasporto TCP, che va a gestire i flussi di dati TCP e si
interfaccia con il livello IP.
I datagram IP ricevuti che contengono dati TCP vengono passati all’entità TCP che
ricostruisce il flusso originale di byte, precedentemente spezzettato dall’entità TCP del
trasmettitore. Il livello IP non da garanzia sulla avvenuta consegna dei datagram; e
quindi compito del TCP ritrasmetterli quando necessario. Un altro compito del TCP è
riassemblare i messaggi nella sequenza corretta tutte cose che da solo IP non può fare.
48
Capitolo 3: Canale di ritorno
Il TCP si basa su punti accesso chiamati socket, sia il client (STB) che il server (CS) ne
devono possedere uno univoco. Ogni socket e caratterizzato da un indirizzo IP che
identifica l’host e da un numero di 16 bit detto “porta”, per esempio l'HTTP comunica
sulla porta 80.
Il protocollo UDP (User Data Protocol) è un protocollo non orientato alla connessione,
è usato per spedire datagram IP, senza dover stabilire una connessione, in maniera più
rapida ma senza garantire il controllo degli errori e del flusso dei pacchetti.
3.2.5 Hypertext Transfer Protocol (HTTP)
Lo standard HTPP (Hyper Text Tranfer Protocol) è il protocollo che sta alla base del
WWW. Le specifiche MHP incentivano l'uso di questo protocollo come possiamo
vedere dalla figura 3.2.1, questo perché il suo utilizzo da la possibilità di trasferire
informazioni di varia natura, come file di testo, immagini, ecc..
L'HTTP è un protocollo orientato alla comunicazione tra un client ed un server. Come si
può vedere anche in figura esso è supportato dal TCP, ma nonostante questo resta
comunque un protocollo “senza stato”, quindi ogni transazione viene trattata in maniera
indipendente dalle altre almeno per quanto riguarda l'HTTP 1.0. Viene quindi creata una
connessione TCP ogni volta che il STB dovrà interagire con il Centro Servizi per lo
scambio dei dati.
I terminali MHP di nuova generazione sono nati per supportare sia HTTP 1.0 (IETF
RFC 1945[92]) che HTTP1.1 (IETF RFC 2616[40]) con alcune considerazioni del caso
soprattutto per quanto riguarda le connessioni di tipo keep-alive.
I terminali MHP e quindi le applicazioni che implementano i profili Interactive TV
Profile e Internet Access Profile, che sfruttano il protocolli HTTP 1.0/1.1 dovranno
supportare connessioni di tipo persistente. L' HTTP 1.1 è stato introdotto per colmare le
lacune del vecchio HTTP1.0 in quanto una delle caratteristiche fondamentali è quella di
stabilire una sola connessione TCP per tutta la durata della comunicazione.
Il fatto che nella maggior parte dei casi il terminale MHP comunicherà per tutta la
durata della connessione con un unico ISP, il nostro Centro Servizi, è opportuno
stabilire una connessione sempre attiva per tutta la durata della comunicazione evitando
problemi di congestione della rete.
Per stabilire una connessione di tipo persistente i terminali MHP che sfruttano il
49
Capitolo 3: Canale di ritorno
protocollo HTTP1.0 dovranno inserire all'interno del messaggio HTTP nel campo
Connection del General-Header il token Keep-Alive:
Connection: keep-alive
Il server HTTP che si vede arrivare un messaggio di richiesta di connessione KeepAlive risponderà instaurando una connessione persistente.
Se si fa questo,il campo Keep-Alive del General-Header, del messaggio HTTP, conterrà
il tempo durante il quale il trasmettitore mantiene la connessione aperta in attesa della
richiesta successiva, oppure il numero massimo di richieste aggiuntive che sono
consentite sulla connessione in corso. Il campo Keep-Alive sarà composto come sotto:
Keep-Alive-header="Keep-Alive" ":" 0# keepalive-param
keepalive-param = param-name "=" value
dove la notazione ''0#'' significa che i parametri potranno essere ripetuti 0 o più volte e
saranno separati da '','' se il campo ne contiene più di uno.
Se i terminali MHP dovranno comunicare con un server proxy HTTP1.0 non sarà
necessario inviare il token Keep-Alive, questo perché un proxy HTTP1.0 non ubbidisce
alle regole dell' HTTP1.1 per fare il parsing del campo Connection. Questo significa che
si dovrà impedire ai terminali MHP di richiedere connessioni sempre attive quando
questi devono comunicare con server proxy che non ne potrà supportare le funzionalità.
I server di tipo HTTP1.1 che si basano su connessioni persistenti dovranno supportare
anche l' HTTP1.0 per richieste da parte di terminali MHP che lavorano con protocollo
HTTP1.0 e che cercheranno comunque di instaurare connessioni sempre attive
utilizzando il token Keep-Alive.
50
Capitolo 3: Canale di ritorno
3.2.6 Secure Hypertext Transfer Protocol (HTTPS)
Nei casi in cui si richiede un canale di comunicazione protetto con il CS, le specifiche
MHP consigliano l'uso del protocollo HTTPS. Questo protocollo è sostanzialmente
basato sull'HTTP ma affianca ad esso il protocollo TLS (Transport Layer Security).
TLS 1.0 è molto simile a SSL 3.0 anche se non è compatibile con questo. Lo scopo di
TLS è quello di permettere una comunicazione sicura tra due applicazioni, fornendo al
flusso di dati servizi di autenticazione, integrità e riservatezza. Il profilo MHP non
implementa l'intero TLS ma solamente una parte degli algoritmi di cifratura previsti tra
cui in particolare :
• RSA ()
• MD5
• SHA-1
• DES
3.2.7 Servizi Specifici
Qualora si trovasse la necessita di supportare da parte dei ricevitori protocolli
proprietari per il canale di ritorno lo standard MHP lascia ai costruttori la possibilità di
definirne alcuni e di portarli sulle piattaforme. Ad oggi tutti i ricevitori STB in
commercio utilizzano protocolli di uso comune che garantiscono più robustezza,
integrità e maggiore possibilità di integrazione alla rete Internet per una prospettiva
futura di accesso a contenuti Web con l'introduzione del profilo “Internet Access
Profile”.
51
Capitolo 3: Canale di ritorno
3.3 Gestione della connessione
Indipendentemente dal tipo di modem di cui è provvisto il set top box, le connessioni
sono gestite dal package Org.dvb.net.rc in questo paragrafo andremo a vedere nel
dettaglio le classi di questo package e cercheremo di capire come queste interagiscono
tra di loro per poter poi essere utilizzate per stabilire una connessione con un ISP
remoto.
Successivamente daremo uno sguardo agli altri package, java.net e java.io, che verranno
utilizzati una volta che la connessione è stabilita.
3.3.1 Utilizzare il canale di ritorno: il package org.dvb.net.rc
Tra i tre profili che MHP introduce sia “Interactive Broadcast” che “Internet Access”,
includono il supporto per utilizzate il canale di ritorno sui STB. In base al tipo di
ricevitore che stiamo utilizzando il canale di ritorno può assumere diverse forme in base
alla fascia di appartenenza, anche se ad oggi quella più comune è il modem PSTN, che
garantisce: una maggiore reperibilità sul territorio, quindi un bacino di utenza più ampio
e semplicità di installazione, anche se porta dei limiti che come abbiamo gia visto sono
molto “pesanti”. I STB quando stabiliscono una connessione con canale di ritorno non
fanno altro che appoggiarsi al protocollo IP del livello di rete e come molte applicazioni
Java che fanno uso di una connessione remota, anche le nostre Xlet si appoggeranno
alle classi del package java.net, con alcune restrizioni del caso.
Le specifiche MHP 1.0.x richiedono supporto per HTTP1.0 e DNS su canale di ritorno
al di sopra del TCP, ma ogni altro protocollo è opzionale, questo lascia un po’ di liberta
anche se può portare a dei mal funzionamenti, sarà quindi compito del programmatore
fare le opportune verifiche. Con MHP 1.1 le specifiche aggiungono il supporto per
HTTPS un protocollo per connessioni sicure che unisce l’ HTTP con i certificati TLS.
Le specifiche non aggiungono niente per quanto riguarda il supporto ad altri protocolli
come FTP o SMTP. Anche se l’ultimo è sicuramente necessario per la creazione di un
client e-mail.
Una considerazione va fatta per quanto riguarda la gestione della connessione, in quanto
le API java.net assumono che una connessione permanente sia eseguibile, è questo
52
Capitolo 3: Canale di ritorno
può diventare un problema da non sottovalutare specialmente quando il nostro STB usa
un modem PSTN. In certi casi non ci dovremo curare di gestire la connessione, sarà il
ricevitore a connettersi automaticamente all’ISP di default, dovremmo solo
preoccuparci
di
utilizzare
correttamente
le
classi
java.net.socket
e
java.net.URLConnection per fare riferimento ad un file remoto. In maniera simile il
ricevitore dopo un periodo di inattività andrà a disconnettere automaticamente il canale
di ritorno, questo porta ad una sorta di sistema di sicurezza da xlet maligne che però è
facilmente aggirabile.
3.3.2 Interfacce per il Canale di Ritorno
Ogni tipo di interfaccia supportata dai STB è rappresentata da un’istanza della classe
RCInterface :
public class RCInterface extends java.lang.Object {
public static final int TYPE_PSTN = 1;
public static final int TYPE_ISDN = 2;
public static final int TYPE_DECT = 3;
public static final int TYPE_CATV = 4;
public static final int TYPE_LMDS = 5;
public static final int TYPE_MATV = 6;
public static final int TYPE_RCS = 7;
public static final int TYPE_UNKNOWN = 8;
public static final int TYPE_OTHER = 9;
private int type;
private int dataRate;
protected RCInterface() {
}
public int getType() {
return this.type;
}
public int getDataRate() {
return this.dataRate;
}
}
Con questa classe è possibile modellare nome, velocità in Kbps e tipo di interfaccia da
utilizzare per ottenere una connessione IP. Come possiamo vedere questa classe
definisce un insieme di costanti dove ognuna rappresenta un tipo di canale di ritorno.
53
Capitolo 3: Canale di ritorno
COSTANTI
TYPE_PSTN
TYPE_ISDN
TYPE_DECT
TYPE_CATV
TYPE_LMDS
TYPE_MATV
TYPE_RSC
TYPE_UNKNOWN
TYPE_OTHER
TIPO DI CANALE
PSTN
ISDN
DECT
Cable modem
Local Multipoint Distribution System.
Canale di Ritorno Wireless
Master antenna TV
DVB-RCS (Canale di ritorno via
satellite)
Utilizzato quando è presente una
interfaccia hardware tra il ricevitore e il
dispositivo senza conoscerne i dettagli
Usato per standard futuri di cui DVB non
intende aggiungere costanti proprie
Tabella 3.3.2.1 – Costanti canale di ritorno
Il metodo getType()ritorna un intero corrispondente al tipo di interfaccia a cui la classe
ConnectionRCInterface fa riferimento. Mentre il metodo getDataRate() ritorna il
massimo “data rate” dell’interfaccia a cui siamo connessi in questo momento, se
nessuna interfaccia è connessa il ricevitore ci restituirà il valore dell’ultima interfaccia
utilizzata oppure il valore –1 se non reperibile.
54
Capitolo 3: Canale di ritorno
3.3.3 Acquisire l’interfaccia corretta
Come abbiamo visto nelle specifiche MHP esiste più di un tipo differente di interfaccia,
in base al supporto che implementa il canale di ritorno, questo rende necessaria la
presenza di una classe che sia in grado di ottenere l’accesso a quella corretta e ne
controlli l’utilizzo in base al quello che dovrà fare la nostra xlet.
A tal proposito e stata creata la classe RCInterfaceManager che ci fornisce alcuni
metodi per controllare l’accesso a questo tipo di risorsa scarsa.
public class RCInterfaceManager
implements org.davic.resources.ResourceServer {
public static RCInterfaceManager getInstance();
public RCInterface[] getInterfaces();
public RCInterface getInterface(java.net.InetAddress addr);
public RCInterface getInterface(java.net.Socket s);
public RCInterface getInterface(java.net.URLConnection u);
public void addResourceStatusEventListener(
org.davic.resources.ResourceStatusListener listener);
public void removeResourceStatusEventListener(
org.davic.resources.ResourceStatusListener listener);
}
La classe RCInterfaceManager mette a disposizione un metodo il getInstance()che
viene innanzitutto utilizzato per ottenere un’istanza di se stessa da utilizzare nei passi
successivi. Una volta che abbiamo ottenuto il riferimento a questa classe possiamo
procedere
con
l’acquisire
l’interfaccia
appropriata,
cosi
con
il
metodo
getInterfaces() ci viene restituito un array di RCInterface in base ai permessi
associati alla nostra applicazione e alla disponibilità di interfacce che il nostro ricevitore
supporta. Questo array potrebbe risultare anche vuoto se la nostra applicazione non ha i
permessi necessari per l’uso della risorsa magari perché viene identificata come
maligna. Il metodo getInterfaces() ha tre varianti legate alla classe del package
java.net che andremo ad utilizzare; java.net.URLConnection, java.net.Socket,
java.net.InetAdress.
Nei primi due casi il middleware del ricevitore assume che la connessione sia già attiva,
questo perché sarà esso stesso che non appena nota la richiesta di connessione ad una
55
Capitolo 3: Canale di ritorno
URL o ad una Socket cerca di creare una connessione su linea di ritorno, e ritornerà
l’interfaccia utilizzata per questa connessione. Nell’ultimo caso invece il middleware
ritornerà l’interfaccia che dovremmo poi utilizzare per stabilire la connessione. Come
ho gia detto i STB in commercio sono forniti solamente di modem PSTN e quindi i
metodi appena citati riporteranno se ben formati sempre l’interfaccia ‘1’ o nulla se non
presente, bisognerà tenere conto di questo per non incorrere in mal funzionamenti.
Essendo il canale di ritorno una risorsa scarsa, questa classe come possiamo vedere dal
codice, implementa l’interfaccia ResourceServer
permettendo di associare un
ResourceStatusListener al nostro oggetto per informarlo sugli eventi che accadono
alla risorsa.
Gli eventi generati del tipo ResurceStatusEvents possono essere:
•
RCInterfaceReservedEvent:
Questo
evento
viene
generato
quando
un’applicazione ha correttamente riservato una RCInterface, può essere
generato anche quando un’altra entità del sistema ha riservato la risorsa
rendendola cosi non più utilizzabile.
•
RCInterfaceReleasedEvent: Viene generato quando un’applicazione che
precedentemente aveva riservato una RCInterface ha poi deciso di rilasciare la
risorsa chiamando il metodo release() della classe ConnectionRCInterface
su quella interfaccia. Può essere generato anche da un’altra entità del sistema
informando cosi l’applicazione che la risorsa è ora disponibile.
56
Capitolo 3: Canale di ritorno
3.3.4 Stabilire la connessione
Tra le interfacce per il canale di ritorno dichiarate nella classe RCInterface alcune
rappresentano una connessione sempre attiva. Il middleware non tratta quindi tutti i tipi
di interfacce in maniera uguale questo perché le connessioni sempre attive come
possono essere TYPE_CATV o ADSL non vengono trattate come se fossero risorse
scarse, semplicemente perché l’applicazione non ha bisogno di sapere a quale provider
il canale di ritorno è connesso. La gestione della connessione e quindi completamente
lasciata nelle mani del middleware che si occupa di tenerla sempre disponibile nel
momento in cui l’applicazione ne fa richiesta, senza preoccuparsi di troppi dettagli.
Questo però non accade con altri tipi di interfacce, come per esempio con un modem
PSTN, in questo caso dovremo conoscere il numero di telefono dell’ISP a cui
connettersi, prima di poter utilizzare la risorsa, oltre a dati come user e password. In
base al service
provider al quale ci si vuole connettere potrebbe succedere che
un’applicazione sia abilitata a stabilire una comunicazione, mentre altre no per problemi
di sicurezza, e ciò pone dei limiti ma anche dei vantaggi.
La classe ConnectionRCInterface estende la classe RCInterface ed implementa
l’interfaccia ResourceProxy , di seguito possiamo vedere i metodi di cui e composta.
public class ConnectionRCInterface
extends RCInterface
implements org.davic.resources.ResourceProxy {
public boolean isConnected();
public float getSetupTimeEstimate();
public void reserve(
org.davic.resources.ResourceClient c,
java.lang.Object requestData)
throws PermissionDeniedException;
public void release();
public void connect()
throws java.io.IOException, PermissionDeniedException;
public void disconnect()
throws PermissionDeniedException;
public ConnectionParameters getCurrentTarget()
throws IncompleteTargetException;
public void setTarget(ConnectionParameters target)
throws IncompleteTargetException,
PermissionDeniedException;
57
Capitolo 3: Canale di ritorno
public void setTargetToDefault()
throws PermissionDeniedException;
public int getConnectedTime();
public org.davic.resources.ResourceClient getClient();
public void addConnectionListener(
ConnectionListener l);
public void removeConnectionListener(
ConnectionListener l);
}
Questa classe mette a disposizione i metodi necessari per riservare la risorsa, settarne i
parametri ed eseguire connessione e disconnessione dall’ISP impostato.
Prima di eseguire la connessione all’ISP utilizzando l’interfaccia specificata è
necessario riservare la risorsa per la nostra applicazione, per fare questo si utilizza il
metodo riserve() per evitare che altri cerchino di utilizzarla generando dei conflitti.
Questo metodo è utilizzato per associare all’interfaccia un ResurceClient utilizzato
per i messaggi di notifica sullo stato della risorsa e per quando questa viene rimossa,
questo metodo può generare un’eccezione del tipo PermissionDeniedException
quando la nostra applicazione non ha i permessi necessari per riservare la risorsa.
L’applicazione che utilizza il canale di ritorno deve per forza essere a conoscenza dei
dati che ci permettono di chiamare ad autenticarci presso un ISP e quindi prima di
chiamare il metodo connect() sarà necessario che questi vengano impostati
nell’interfaccia selezionata, per fare questo si utilizza il metodo setTarget(). Ogni
interfaccia ha in genere un ISP associato al quale ci si connette quando la si usa,
solitamente questo è strettamente legato al broadcaster che trasmette l’applicazione
interattiva e che quindi mette a disposizione anche un Centro Servizi con cui interagire.
Il metodo setTargetToDefault() rimuove tutti i target impostati e setta quello che il
produttore del middleware ha definito come standard. Se la nostra applicazione non ha i
permessi per reimpostare il target o se quello di default non è valido viene lanciata una
SecurityException.
Ogni target è rappresentato da un oggetto della classe ConnectionParameters, questa
classe è utilizzata per creare un oggetto in cui sono definiti i parametri necessari per
stabilire una connessione.
58
Capitolo 3: Canale di ritorno
La classe e strutturata come segue:
public class ConnectionParameters {
public ConnectionParameters(
java.lang.String number,
java.lang.String username,
java.lang.String password);
public ConnectionParameters(
java.lang.String number,
java.lang.String username,
java.lang.String password,
java.net.InetAddress[] dns);
public java.lang.String getTarget();
public java.lang.String getUsername();
public java.lang.String getPassword();
public java.net.InetAddress[] getDNSServer();
}
Come possiamo vedere i parametri di cui una interfaccia ha bisogno sono un numero
telefonico un “username” e una password che il middleware utilizza per autenticare la
connessione. E’ possibile passare se necessario un array con gli indirizzi dei server DNS
(Domain Name System) da utilizzare, prima di riportare un messaggio di fallimento,
che vengono utilizzati per trasformare nomi di host e indirizzi di posta elettronica in
indirizzi IP, da utilizzare quindi per stabilire connessioni TCP/UDP sulle socket.
Chiamando
il
metodo
setTarget()
è
possibile
che
venga
lanciata
una
IncompleteTargetException nel caso in cui l’oggetto ConnectionParameters fosse
stato creato con qualche parametro a null.
Ogni cambiamento sull’oggetto ConnectionParameters avrà effetto nel momento in
cui noi stabiliamo la connessione, se pero delle modifiche vengono fatte durante una
connessione gia attiva per rendere effettivi i nuovi parametri sarà necessario
disconnettere l’interfaccia e poi riconnetterci per puntare al nuovo ISP. Una volta
impostato un target possiamo chiamare il metodo connect() sulla nostra interfaccia, ed
una volta ottenuto l’IP utilizzare le classi del package java.net per connetterci all’host
desiderato.
Nel caso dell’interfaccia PSTN il middleware andrà a chiedere il consenso dell’utente
prima che l’applicazione effettui la chiamata, se l’utente acconsente il
numero
telefonico viene messo in una “white list” per evitare che si richieda il consenso al
prossimo accesso.
59
Capitolo 3: Canale di ritorno
Se l’utente blocca la chiamata viene generato un ConnectionFailedEvent, mentre se si
acconsente ed il middleware riesce a creare la connessione viene generato un
ConnectionEstablischedEvent.
Nel caso in cui la risorsa sia riservata ma non sia possibile stabilire una connessione con
il target specificato il middleware lancerà una IOException per notificare all’ascoltare
che ci sono stati dei problemi non direttamente dovuti all’hardware del ricevitore.
Una volta che l’applicazione ha terminato di utilizzare la risorsa si chiama il metodo
disconnect() e quindi il metodo release() per rilasciare la risorsa e permettere ad
altre applicazioni di utilizzarla, lanciando l’evento ConnectionTerminatedEvent.
Ottenere la connessione ad un ISP può richiedere del tempo ed avere delle complicanze,
specialmente nel caso di un modem PSTN, per questo la nostra applicazione dovrà
registrare come ascoltatori quelle classi che intendono sfruttare la connessione.
Queste classi dovranno implementare l’interfaccia ConnectionListener e quindi
definire come il metodo astratto connectionChanged dovrà comportarsi quando si vede
arrivare un evento del tipo ConnectionRCEvent.
La classe ConnectionListener è strutturata come segue:
public interface ConnectionListener {
public abstract void connectionChanged(ConnectionRCEvent e);
}
Le classi che implementano ConnectionListener vengono associate all’oggetto
ConnectionRCInterface utilizzando il metodo addConnectionListener in modo
che il middleware saprà a chi inviare gli eventi che si generano da quell’ interfaccia.
Tali eventi sono di tre tipi:
•
ConnectionEstabliscedEvent: Generato nel caso in cui la connessione ha
avuto successo.
•
ConnectionFailedEvent: Se non è stato possibile stabilire la connessione per
motivi tecnici, se per esempio qualcuno sta utilizzando in quel momento il
telefono.
•
ConnectionTerminateEvent: Generato nel caso in cui si rompe la connessone
oppure se l’applicazione stessa ha deciso di disconnettersi.
60
Capitolo 3: Canale di ritorno
Gli eventi appena descritti possono essere generati anche dall’applicazione stessa nel
caso in cui si verifichino più sequenze di request sulla stessa connessione per notificare
alla classe in ascolto che la sessione è ancora attiva e si possono trasferire file senza
stabilirne una nuova.
3.3.5 I package java.net e java.io
Ottenuta la connessione con il Centro Servizi tramite i metodi messi a disposizione dal
package org.dvb.net.rc le applicazioni si vanno ad appoggiare alle API java.net per
accedere alle URL. Lo standard MHP pone alcune restrizioni sulle classi che si possono
utilizzare di questo package, in particolare le classi messe a disposizione da DVB-J che
è la versione ridotta di Java per MHP basata oltre che sui package specifici per il DVB
sulla Personal Java 1.1.8 sono:
•
java.net.URL
•
java.net.URLConnection
•
java.net.Socket
•
java.net.InetAdress
Tutte queste classi possono essere utilizzate per ottenere un collegamento con il Centro
Servizi tramite protocolli HTTP e HTTPS.
La particolarità delle classi del package java.net è quella di essere in grado di
interfacciarsi tramite l’uso della JVM con il sistema operativo della macchina su cui si
sta eseguendo l’applicazione sarà poi questa a gestire l’accesso al modem e a far
transitare le richieste attivando una connessione su protocollo TCP.
Ottenuto il link con la risorsa desiderata ci si appoggia al package java.io per i metodi
di gestione dei file veri e propri. Anche altri metodi possono essere utilizzati per gestire
i file in particolare per quanto riguarda immagini ci si appoggia alla classe
java.awt.image.ImageProducer e per file audio e video al package javax.media. E’
sempre opportuno controllare nelle specifiche MHP quali classi possono essere
utilizzate per evitare mal funzionamenti nelle applicazioni dato che non tutti i tipi
MIME sono supportati dai STB.
61
Capitolo 4: Ambiente di sviluppo
Capitolo 4
Ambiente di sviluppo
4.1 Introduzione
Dopo aver visto nei capitoli precedenti le tecnologie usate nel digitale terrestre, le
caratteristiche principali di un’applicazione MHP, con particolare riguardo all’uso del
canale di ritorno, andremo a proporre come è possibile realizzare l’ambiente di sviluppo
per l’implementazione, debug e test di una Xlet.
Per l’implementazione, come prima cosa, si avrà bisogno di un PC con installata una
Java Virtual Machine e magari un programma di sviluppo Java, come Eclipse, per
evitare errori di sintassi.
Per la fase di debug e una prima fase di test è molto utile utilizzare un simulatore per
applicazioni MHP, per vedere errori in fase di compilazione.
Per la fase di test vera e propria ci sono due modi di procedere:
•
il primo consiste nel simulare un broadcaster; necessità di un modulatore
COFDM al quale in ingresso verrà mandata l’applicazione insieme ad un
eventuale flusso video, mentre in uscita vi sarà collegato uno (o più) Set Top
Box commerciali;
•
il secondo, che è quello che poi andremo ad analizzare in dettaglio, consiste
nell’acquisto di un Set Top Box da sviluppo, che a differenza di quelli
commerciali, permette l’ upload di applicazioni non solo dall’etere, ma anche in
locale, tramite porta seriale (RS-232).
62
Capitolo 4: Ambiente di sviluppo
4.2 Il Simulatore XletView
Tra i vari software freeware di simulazione per applicazioni MHP, che si trovano in
rete, spicca XletView, distribuito da GNU General Public License (GPL) , scaricabile
all’indirizzo www.sourceforge.net.
Per l’installazione si richiede che sul PC sia presente una Java Virtual Machine a partire
da JRE 1.4, JSDK1.4 o versione superiore, della Sun; mentre all’interno del pacchetto
troviamo già le API JMF 2.1.1 (Java Media Framework) che servono per incorporare
media-data come audio e video nelle applicazioni Java, applets e Xlet.
Dopo aver installato il simulatore bisogna copiare nella cartella base, le API Javatv
scaricabili dal sito della Sun, praticamente le directory presenti devono essere:
Fig. 4.2.1 - Directory XletView
A questo punto non ci dovrebbero essere problemi per l’avvio del programma; è
importante che insieme alla finestra principale ne compaia anche un’altra, dove
verranno scritti gli eventuali messaggi d’errore, insieme ai messaggi di debug voluti dal
programmatore, ovvero tutti i System.out.println presenti nella Xlet.
63
Capitolo 4: Ambiente di sviluppo
Per fare ciò è necessario scrivere la seguente riga di comando, magari mettendola in un
file batch:
cd xletview
java -cp
%CLASSPATH%;javatv_fcs/javatv.jar;xletview.jar;jars/metouia.jar;jars/javassist.jar;
jars/nanoxml-2.2.3.jar net.beiker.xletview.Main
cd ..
Ora le finestre visibili dovrebbero essere le seguenti come mostrato in figura 4.2.2 e
4.2.3.
Fig. 4.2.2 - Finestra principale XletView
Nella schermata principale vengono visualizzati:
•
a destra il telecomando con tutti i tasti per simulare, mediante mouse, la
periferica d’ingresso;
•
nel riquadro giallo l’eventuale background layer, video layer e il graphics layer;
•
i menù per poter eseguire la nostra applicazione.
Dal menu Applications, sotto ManageApplication, bisognerà inserire il classpath e il
nome della classe principale della Xlet.
64
Capitolo 4: Ambiente di sviluppo
Una volta che l’applicazione è stata mandata in “run”, dalla schermata di debug
mostrata in figura 4.2.3, sarà possibile vedere gli eventuali errori e messaggi.
Fig. 4.2.3 - Finestra di debug XletView
Mentre si programma bisogna tener conto che a differenza del PC, dove abbiamo a
disposizione tutte le API fornite dal JR1.4, sul Set Top Box vi è una versione ridotta
della JVM , quindi bisogna fare attenzione alle specifiche sulle API che si andranno ad
usare messe a disposizione su www.mhp.org e www.dvb.org, altrimenti si rischia di
sviluppare applicazioni che “girano” solo su simulatore.
65
Capitolo 4: Ambiente di sviluppo
4.3 Set Top Box da sviluppo ADB X-75
Per completare il ciclo di sviluppo di una applicazione MHP, dopo che è stata
sviluppata con Eclipse e mandata in esecuzione su Xletview, manca solo la fase finale
di test, che come abbiamo detto consiste nel caricare l’applicazione su un Set Top Box
da sviluppo. Riassumendo il nostro ambiente di sviluppo quindi sarà costituito da un
Personal Computer, da una normale TV munita di presa SCART e da il Set Top Box da
sviluppo ADB X-75.
Le interconnessioni tra gli apparati appena elencati, come mostrato in figura 4.3.1, sono:
•
il STB riceve in ingresso il segnale televisivo (da una comune antenna), è
collegato tramite SCART alla TV, e tramite seriale RS-232 al PC;
•
sia il PC che il STB sono connessi in rete (LAN) successivamente ne chiariremo
il motivo.
Fig. 4.3.1 - Ambiente di sviluppo
66
Capitolo 4: Ambiente di sviluppo
4.3.1 Caratteristiche Tecniche
Una delle più prestigiose ditte che forniscono strumenti per lo sviluppo in campo
broadcast è l’ADB, acronimo di Advanced Digital Broadcast.
La serie X-75 comprende Decoder da sviluppo per tre tecnologie: Cable, Satellite,
Terrestrial, in conformità con lo standard DVB, quindi è più corretto chiamare il nostro
strumento ADB T.75.
Andiamo ad elencare innanzitutto i componenti e le caratteristiche Hardware mostrate
nella seguente tabella 4.3.1.1:
ARCHITETTURA
DESCRIZIONE
CPU STi5517 166MHz
Tuner/Front end DVB-T
Flash Memory 16 MB
RAM Memory 72 MB
EEPROM 32 kB
Power Supply 90-264 VAC, 47-63Hz
Casing 440x260x50mm
OUTPUTS
RF input/output RF in & RF out (loop trought)
Audio/Video outputs 2xRCA (Stereo Audio), 2xSCART, S/PDIF optical
Return channel Ethernet 10BaseT, PSTN modem v.92
Data port Debug serial port (RS-232 up to 115.2 kbps)
Front panel display 4x7-segment LED display, 2 LEDs
DVB-CI slot Located on front panel
Smart card slot Located on front panel
ACCESSORI
Power Cable 1.8 m
SCART Cable 1.5 m
RS 232 Cable 2.0 m
67
Capitolo 4: Ambiente di sviluppo
CARATTERISTICHE
DESCRIZIONE
MPEG VIDEO DECODING
Standards MPEG-2 MP@ML, CD ISO/IEC 13818-1, CD
ISO/IEC13818-2
Video Data Rate 0.5 – 15 Mbps
Format Conversion 4:3 > 16:9 with Pan & Scan and Letterbox
Graphics Planes 4 planes(Background, Still-plane, Video, OSD)
AUDIO DECODING
Standards MPEG-1 Layer 1&2; 16 bit precision, CD ISO/IEC 13818-3
Sampling Rate 32 kHz, 44.1 kHz, 48 kHz
Variable Output Level 16 steps @ 2dB per step
DOLBY Digital AC3 Pass throught to S/PDIF
TERRESTRIAL FRONT END – T.75
COFDM(DVB-T) ETSI EN 300 744
Modulation QPSK, QAM16, QAM64
Code rate ½, 2/3, ¾, 5/6, 7/8
Guard Interval ¼, 1/8, 1/16, 1/32
Transmission modes 2k, 8k
Tabella 4.3.1.1 - Aspetti tecnici
Un STB di buon livello, reperibile attualmente in commercio, presenta sicuramente le
seguenti caratteristiche:
•
modem V.90;
•
lettore Smart Card;
•
doppia presa SCART;
•
uscita audio RCA;
•
uscita audio ottica;
•
connettore seriale RS-232 per eventuali periferiche di ingresso;
Il nostro Set Top Box da sviluppo, oltre ad avere tutte le caratteristiche di un normale
decoder, presenta:
68
Capitolo 4: Ambiente di sviluppo
•
una maggior capacità di elaborazione (frequenza processore più elevata)
•
una maggiore capacità di memorizzazione;
•
interfaccia Ethernet;
•
CI Common Interface;
•
presa seriale RS-232 bidirezionale (permette l’upload dell’applicazione e il
debug).
4.3.2 Pannello frontale
Mostriamo ora nei dettagli il pannello frontale e le funzionalità dei suoi componenti,
rispettivamente in figura 4.3.2.1 e nella tabella 4.3.2.1. I componenti sono: 7 pulsanti, 2
indicatori LEDs, un display, una CI Common Interface e una OCF Smart Card slot.
Fig. 4.3.2.1 - Pannello frontale ADB T.75
69
Capitolo 4: Ambiente di sviluppo
Tabella 4.3.2.1 - Funzionalità pannello frontale
4.3.3 Pannello posteriore
In figura 4.3.3.1 si mostra come è composto il pannello posteriore.
Fig. 4.3.3.1 - Pannello posteriore ADB T.75
Vediamo in dettaglio i suoi componenti:
1. interruttore alimentazione;
2. presa alimentazione 220V ~ 50Hz;
3. connettore RS-232;
4. ingresso antenna;
5. uscita antenna (alla TV);
6. RJ11 jack modem;
7. RJ45 jack Ethernet;
8. Uscita AC3 ottica (Audio digitale);
9. SCART (connessione verso TV);
10. SCART (connessione verso eventuale VCR);
11. uscita audio RCA.
70
Capitolo 4: Ambiente di sviluppo
4.3.4 Telecomando
Il telecomando e le funzionalità dei sui tasti sono mostrate in figura 4.3.4.1; non ci sono
molte differenze rispetto a quello dei comuni decoder, da notare il tasto “APP” che apre
e chiude la finestra delle applicazioni.
Fig. 4.3.4.1 - Telecomando ADB T.75
71
Capitolo 4: Ambiente di sviluppo
4.3.5 Settaggio connessione di default
Diamo uno sguardo al menù principale del decoder ADB:
Tra i vari sottomenù della voce “Setting”, in fondo
troviamo “Internet Connection”, una volta entrati
apparirà una schermata dove è possibile scegliere
se usare come predefinita la connessione tramite
modem PSTN o tramite Ethernet.
Internet Connection
Ethernet Setting
La connessione Ethernet permette di far comunicare
Il STB con gli altri dispositivi della rete, a patto che
al decoder sia associato un indirizzo IP;
per fare questo ci sono due modi Auto e Manual .
Se si imposta “Auto” dal menù, verrà assegnato un
Indirizzo IP dinamico (si suppone che il server della
Ethernet (Auto)
rete abbia abilitato il DHCP).
In caso contrario si dovrà impostare su “Manual” e
Si dovranno inserire i valori: IP (statico), Mask,
Default Gateway e il DNS primario e secondario.
Ethrnet (Manual)
72
Capitolo 4: Ambiente di sviluppo
Modem Setting
Se si vuole far connettere in rete il STB, usando il
Modem, bisogna impostare in questa schermata:
N telefonico ISP, eventuale prefisso, tipo composizione
(impulsi/tone), username, password ed eventualmente
l’opzione “aspetta segnale di libero”.
4.3.6 STB Firmware upgrade
Prima di passare all’upload di un’applicazione è necessario aggiornare il firmware
fornito dalla casa madre.
Il software che risiede nel Set Top Box (STB), consiste in due parti principali: il
decoder code e il loader.
Il decoder code, anche chiamato codice di alto livello, è responsabile della ricezione,
codifica e visualizzazione di audio/video, e altri componenti come teletext, sottotitoli,
etc.
Il loader non mostra ne video ne audio, ma visualizza all’utente alcune informazioni
riguardanti le fasi del processo o errori di download.
L’ADB fornisce insieme al STB un software chiamato ADB FastTerm, che interagisce
con il loader per aggiornare il firmware tramite la porta RS-232.
L’applicazione di cui sopra, si mostra come in figura 4.3.6.1; prima di utilizzarla per
prima cosa bisogna impostare il numero della porta COM (dal menù a tendina), sulla
quale è connesso il STB, assicurandosi che questa sia settata con i seguenti parametri:
•
Baud rate
•
N° bit di dati
•
Parità
•
N° bit di stop
115200
8
NO
1
A questo punto, dal menù File - Open download file, si va ad aprire il file contenente
l’aggiornamento (*.enc), si spegne il STB per almeno 2 secondi, si tiene premuto il tasto
freccia sinistro situato sul pannello frontale e contemporaneamente si riaccende il STB.
Il tasto deve continuare ad essere premuto fino a che non si accendono i LED frontali, a
questo punto si lascia il pulsante e inizia il download del firmware sul STB; in questa
fase la barra sulla destra dell’applicazione inizia a colorarsi fino ad arrivare al 100%.
73
Capitolo 4: Ambiente di sviluppo
Fig. 4.3.6.1 - Finestra FastTerm
4.3.7 UpLoad e debug di un’applicazione
La ditta produttrice del STB oltre al software “FastTerm”, fornisce anche l’ADB
APPLOADER che permette di trasferire la nostra applicazione, dal PC alla memoria del
decoder, sempre tramite porta seriale.
In realtà la comunicazione tra i due dispositivi deve passare attraverso un secondo
programma, che fa da Proxy, il quale può risiedere localmente o trovarsi in un’altra
macchina, l’importante è che abbia un indirizzo IP valido e che sia connessa al STB.
Quando si fa partire il Proxy, bisogna specificare il numero di porta COM (con gli
eventuali settagli) dove è connesso il STB e il nome del file di log comprensivo di
classpath; questo programma lavora usando il protocollo TCP sulla porta standard 4444,
a meno che non specificato diversamente.
Il file di log è un file di testo che viene generato dal STB ed è proprio questo che viene
usato, in maniera simile al simulatore, come strumento di debug.
74
Capitolo 4: Ambiente di sviluppo
La riga di comando con le varie opzioni e un’ esempio per inizializzare il Proxy sono:
stbproxy.exe -com <port_number>[-port <TCP port number>]
[-rate<115200,8,N,1>][-log <logfile>]
stbproxy.exe –com 3 –log c:\stb.log
Lavorando sotto Windows apparirà un’icona sulla barra degli strumenti, dove è
possibile accedere, tra le altre cose, all’opzione “svuotare” il file di log.
L’ultima cosa da fare prima di passare all’ upload, è configurare il STB, a questo ci
pensa un terzo tool chiamato “stbconfig” al quale bisogna specificare l’indirizzo IP
dell’host dove si trova il Proxy e l’eventuale numero di porta, se non si usa quella di
default; il suo scopo è quello di abilitare/disabilitare l’opzione di debug output e
security manager. La riga di comando con le varie opzioni e un’ esempio per
configurare il STB ipotizzando che il Proxy sia in locale, si usi il numero di porta TCP
di default e si attivi solo l’opzione di debug sono:
stbconfig.exe proxy_host_IP[:port] [-debug] [-security]
stbconfig.exe localhost -debug
Dopo aver effettuato la configurazione del STB si richiede il riavvio dello stesso.
Finalmente siamo arrivati alla fase finale, il Proxy è in running, il STB è configurato,
ora manca di avviare il trasferimento dell’applicazione.
Il tool stbupload richiede l’IP del proxy, il nome del description file (vedremo in seguito
cosa sia) completo di classpath, e il classpath della directory base dov’è contenuta
l’applicazione (*.class e file accessori).
La riga di comando con le varie opzioni e un’ esempio per fare l’upload di una
applicazione contenuta nella directory c:\xlet\class, supponendo il Proxy in locale con
porta di default sono:
stbupload <proxy_host[:port]> <xlets_descr_file> <pc_base_dir>
stbupload localhost c:\xlet\xlet_desciption_file c:\xlet\class
Ora l’applicazione è copiata nella directory /home del file system del STB.
75
Capitolo 4: Ambiente di sviluppo
4.3.8 Xlet description file
L’Xlet description file riflette il contenuto della tabella AIT (Application Identification
Table); contiene tutte le informazioni e i parametri utili per l’identificazione e
l’esecuzione dell’applicazione da parte del Set Top Box.
Vediamone un’ esempio:
Parametri obbligatori:
#app <Application ID> <Organisation ID>
app
0xhex_number 0xhex_number
#Application control flag: 1=autostart 2=present 3=destroy 4=kill
control 1
#service bound flag (0 or 1)
bound
0
#Basedir of application (must be relative to /home directory)
basedir "/home"
#Initial class name (fully qualified name)
class "your.company.Test"
Parametri opzionali:
#Name of application preceded by language code
name
eng "Test"
#Parameter of service on which the application should be visible to application
manager
tsid
0x7
onid
0x46
svid
0x2bd
#other flags
priority 137
visibility 3
#Classpath extension
classpath ""
#String params passed to Xlet
param = "value 0"
param = "value 1"
76
Capitolo 4: Ambiente di sviluppo
Ora cercheremo di spiegare in dettaglio i campi principali:
app
Ogni organizzazione che produce Xlet, deve essere riconosciuta, ed avere un codice
identificativo univoco, in più ogni applicazione prodotta da quell’ organizzazione deve
anch’essa essere riconoscibile univocamente.
Questo per permettere al STB di non eseguire applicazioni maligne non riconosciute.
control
Questo flag dice al STB come si deve comportare con l’applicazione che ha ricevuto:
se = 1 la manda in esecuzione appena caricata (per avvenire effettivamente
l’esecuzione, nelle impostazioni base del STB deve essere abilitata l’opzione autostart);
se = 2 il STB riceve l’applicazione è la mette disponibile nell’application manager, sarà
l’utente poi a decidere quando farla avviare (premendo app oppure OK, in base al
modello di decoder, si visualizzano le applicazioni disponibili);
se =3 viene distrutta (si usa se caricata con autostart);
se =4 viene killata (si usa se caricata con present);
bound
Questo flag dice se si tratta di una applicazione bound (1) legata al canale e alla
trasmissione o unbound (0) non legata alla trasmissione o addirittura non legata al
canale (quindi disponibile indifferentemente dal canale su cui si è sintonizzati);
basedir
campo contenente il classpath di destinazione dell’applicazione del file system nel STB
(deve essere relativo a /home);
class
deve contenere il nome della classe principale dell’applicazione (per es.:“main.class” )
name
specifica il codice della lingua usata e il nome, relativo all’applicazione, che comparirà
sull’Application Manager;
priority
nel caso in cui l’opzione “autostart” sia settata (nel menu di base del STB) e
nell’Application Manager, vi sono più di una Xlet caricata con il control flag=1, il STB
manda in esecuzione l’applicazione con la priorità più alta.
77
Capitolo 5: Implementazione di un servizio di prenotazioni
Capitolo 5
Implementazione di un servizio di prenotazioni
A questo punto avendo a disposizione un ambiente di sviluppo ed essendo a conoscenza
delle potenzialità, dei limiti e degli standard MHP, possiamo pensare di mettere in
pratica i concetti acquisiti, per implementare la nostra applicazione.
Essendo il STB un dispositivo che per alcuni aspetti può essere paragonato ad un PC
connesso in rete e volendo sfruttare il canale di ritorno, abbiamo dato uno sguardo alle
innumerevoli applicazione che sono sul web, che svariano da semplici portali
informativi fino ad arrivare a complesse applicazioni e-commerce/e-banking/e-healt.
La scelta infine è stata quella di realizzare un servizio di prenotazioni “on-line” rivolto
agli utenti del digitale terrestre, ovvero implementare una Xlet che si in grado di
effettuare una generica prenotazione.
Lo scopo di questo elaborato si basa sull’analizzare tutte le problematiche tecniche
comuni che possono produrre un insieme di servizi del genere, trovarne le soluzioni
ottimali e ovviamente verificarne le funzionalità.
Non si è data molto importanza al tipo di prenotazione, anche se per motivi realizzativi
è stato scelto un caso reale riguardante la prenotazione di “tavoli” in un generico
esercizio pubblico ad esempio.
Anticipiamo che l’applicazione gestirà una semplice anagrafica clienti, una fase di
autenticazione (log-in) per mezzo di username e password, la visualizzazione della
disponibilità del servizio, e la possibilità di prenotazione.
La scelta per quanto riguarda la gestione dei dati (anagrafica e disponibilità) a favore di
robustezza, sicurezza e velocità nel recupero delle informazioni è stata convergente
verso l’uso di un database.
78
Capitolo 5: Implementazione di un servizio di prenotazioni
5.1 Le problematiche
Le
prime
problematiche
riscontrate
riguardano
l’interfaccia
grafica,
alcuni
programmatori MHP ne paragonano la gestione a un ambiente simile a Windows
“vecchio stampo” senza l’uso del mouse. La risoluzione video disponibile, infatti, per
ora è ancora limitata (720x576) e non permette l’implementazione di sofisticati metodi
di accesso ai menù, visto e considerato anche che per la navigazione, abbiamo
solamente a disposizione il telecomando del decoder.
Un altro problema si è riscontrato nell’inserimento di testo nei campi delle varie form,
essendo abituati all’uso di tastiera e mouse non è stata così immediata la soluzione.
Le scelte potevano essere due: creare una tastiera, simile al PC, su video, navigabile con
le quattro frecce, con lo svantaggio di grosso ingombro a livello grafico e scarsa
velocità d’inserimento dei caratteri; oppure quella per cui si è optato, ovvero
l’implementazione di un ridotto tastierino a dodici pulsanti simile a quelli usati nei
telefoni cellulari,con la possibilità di sviluppare in futuro un sistema d’inserimento
simile al T9.
Per quanto riguarda la gestione del canale di ritorno, ampiamente trattata nel capitolo 3,
l’unico accorgimento è stato quello di differenziare i controlli (connesso/disconnesso) in
base al tipo di connessione utilizzata; tramite Modem o tramite Ethernet.
L’ultimo problema, e anche il più rilevante, è stato risolvere la fase di accesso a
Database tramite Xlet.
Considerando un database e alle modalità di accesso ai dati, tramite una piattaforma
Java, viene immediatamente da pensare al potente strumento offerto da Sun, il JDBC
(Java Database Connectivity).
Il fatto è però, che nelle specifiche MHP e DVB, questo strumento non è nemmeno
menzionato. Dopo diversi tentativi di forzare l’uso del JDBC, facendo l’upload su STB
di driver meno moderni e addirittura intere API compilate con JVM simile a quella
residente nel STB, si è deciso di cambiare strada.
L’idea è stata quella di realizzare una applicazione Java che funge da Proxy tra il STB,
quindi l’applicazione MHP, e il Database.
79
Capitolo 5: Implementazione di un servizio di prenotazioni
5.2 Interfaccia grafica
L’interfaccia grafica è costituita principalmente dai seguenti elementi: uno sfondo, un
menù principale costituito da tre pulsanti navigabili ciclicamente con i tasti direzionali
(su/giù), una finestra orizzontale posizionata in basso dove compare una descrizione del
sottomenù evidenziato come mostrato in figura 5.2.1.
La gestione dello sfondo è affidata ad una specifica classe GestoreSfondi, la quale viene
usata sia nella classe principale Main dal metodo startXlet(), sia ogni qualvolta servirà
di ridimensionare o eliminare il video; infatti usa i metodi di BackGroundController per
effettuare tutte le operazioni possibili sui primi due layer grafici.
La creazione della schermata principale è affidata alla classe InterfacciaGrafica che si
preoccupa di gestire per mezzo di variabili si stato, i vari componenti descritti sopra e
inoltre implementa il metodo keyPressed il quale riceve l’evento tasto premuto, ne legge
il codice corrispondente e in base allo stato chiama i metodi appropriati.
Fig. 5.2.1 - Schermata principale
80
Capitolo 5: Implementazione di un servizio di prenotazioni
Le tre scelte possibili sono: Servizi, Registrati, Prenota.
Servizi serve per dare informazioni utili al cliente sull’attività dell’esercizio pubblico,
registrati permette di registrarsi al servizio inserendo nome, cognome, numero di
telefono, username, password; prenota permette di effettuare il log-in, visualizzare la
disponibilità dei tavoli (verde libero, rosso occupato) e l’eventuale prenotazione.
In questa fase si usa un delle caratteristiche principali di Java, il polimorfismo, infatti
ogni opzione delle tre è implementata da una classe omonima Servizi, Registra, Prenota
e tutte estendono la classe Strumenti.
Nella classe Strumenti infatti sono contenuti i metodi comuni a tutte le opzioni come
init, stop, mentre il metodo start e implementato diversamente per ogni strumento.
Strumenti
Init()
Stop()
Servizi
Registra
Prenota
Start()
Start()
Start()
Il metodo init() provvede ad attivare il tastierino, stop() lo rimuove, start() in base al
servizio crea i campi per l’inserimento dati della corrispettiva form.
L’implementazione della tastiera è contenuta nelle classi KeyPad e KeypadButton
mentre i campi di testo da EditableTextBox.
Sia entrando nel menù “Registrati” (figura 5.2.2) che “Prenotazioni” è possibile scorrere
ciclicamente i campi di testo con le frecce (su/giù), premendo “ok” si inizia la
compilazione del campo evidenziato, per confermare tutti i campi inseriti bisogna
premere il tasto rosso, mentre per annullare l’inserimento e tornare al menù principale
quello blu. In caso di username o password errati (figura 5.2.3) o già esistenti (nel caso
di prima registrazione) comparirà un messaggio informativo per l’utente, realizzato dal
metodo messFlash; per continuare si dovrà premere il tasto verde.
81
Capitolo 5: Implementazione di un servizio di prenotazioni
Fig. 5.2.2 - Form Registrati
Fig. 5.2.3 - Form Prenotazioni
82
Capitolo 5: Implementazione di un servizio di prenotazioni
Una volta effettuato correttamente il log-in viene visualizzata la disponibilità dei tavoli,
(usando il metodo visualizzaDisp nella classe Prenota) come mostrato nella figura
5.2.4; ora con le frecce direzionali (destra/sinistra) è possibile selezionare il tavolo
desiderato, e premendo “ok” prenotarlo, sempre se non sia già occupato (colore rosso).
Fig. 5.2.4 Disponibilità tavoli
Per quanto riguarda la parte grafica c’è da aggiungere che si è cercato di mantenere
sempre presente il video layer opportunamente ridimensionato e posizionato, ad
esclusione delle fasi di inserimento dati.
Il codice di questa parte di applicazione è disponibile nell’appendice A suddivisa per
classi.
83
Capitolo 5: Implementazione di un servizio di prenotazioni
5.3 Accesso ai dati
Come accennato all’inizio del capitolo per la gestione dei dati si usa un DBMS Mysql
scaricabile dal sito www.mysql.com. Nel nostro ambiente di sviluppo sia il Database
che l’applicazione Proxy risiedevano sullo stesso PC (con Java 2 Platform Standard
Edition ver. 1.4.2) connesso in rete LAN; sono state anche effettuate prove lavorando
con DB e Proxy remoti. La comunicazione tra Xlet e Proxy avviene tramite Socket
utilizzando le API java.net e java.io, mentre tra Proxy e Database si sfruttano appieno le
potenzialità del JDBC.
Xlet
Proxy
Socket
DBMS
JDBC
Le potenzialità di adottare questo meccanismo sono molteplici:
•
la comunicazione tra xlet e proxy è più sicura in quanto le socket supportano il
TSL e SSL;
•
la Xlet fungerà solo da interfaccia grafica e penserà a gestire la connessione con
il Proxy;
•
la Xlet in questo modo viene alleggerita dalle funzioni d’interrogazione
database, può richiedere complicate query con pochi byte comunicando con il
Proxy, al quale viene lasciato tutto il lavoro (mediante messaggi standard o
codici proprietari comuni a entrambi);
•
il Proxy risiederà su un server con adeguate caratteristiche tecniche è avrà a
disposizione tutte le API comprese java.sql e com.java.jdbc;
•
il Proxy se implementato con un sistema multithread sarà capace di gestire più
richieste contemporanee e l’accesso dei dati in concorrenza;
•
pensando a un centro servizi dove lo stesso Database viene usato da diverse
utenze:operatori che accedono da terminali remoti, utenti via web, utenti del
digitale terrestre; il Proxy può fungere da interfaccia per garantire la coerenza
dei dati.
84
Capitolo 5: Implementazione di un servizio di prenotazioni
5.3.1 Socket
Una socket rappresenta il terminale (end-point) di un canale di comunicazione
bidirezionale. Permette a due processi, residenti sulla stessa macchina o anche molto
distanti, di comunicare fra loro nello stesso modo.
Si basano tipicamente sul modello client/server:
•
il servitore deve stare in attesa di possibili comunicazioni in arrivo (ente
passivo), nel nostro caso è il Proxy
•
i clienti (anche più di uno), nel nostro caso le Xlet, quando vogliono,
comunicano con il servitore (sono enti attivi).
Il protocollo usato è il TCP, il servitore crea la sua ServerSocket con un numero noto di
porta e si mette in attesa.
Un cliente, quando vuole comunicare col servitore, crea la sua Socket specificando con
chi vuole parlare:
• nome dell’host
• numero di porta
Questo fa scattare il protocollo di 3-way handshaking per la connessione.
Il servitore accetta la richiesta dal cliente: con ciò si stabilisce una connessione e il
server crea una nuova Socket già collegata al cliente, tramite la quale i due comunicano.
A questo punto i processi possono parlare senza più bisogno di specificare ogni volta
con chi si vuol parlare (come nelle telefonate: una volta presa la linea, tutti i segnali
transitano automaticamente tra le due parti) come mostrato in figura 5.3.1.1.
Fig. 5.3.1.1 - Comunicazione via Socket stream
85
Capitolo 5: Implementazione di un servizio di prenotazioni
Vediamo la classe Socket nel package java.net.
Alla Socket sono associati due stream:
1. uno dal cliente verso il servitore
2. uno dal servitore verso il cliente.
La comunicazione cliente/servitore è bidirezionale. I ruoli “cliente” e “servitore” sono
tali solo nella fase iniziale, quando si instaura la connessione. Una volta connessi, i due
processi si parlano reciprocamente “alla pari”.
Costruire una Socket significa aprire la comunicazione verso l’altra parte; “public
Socket(String remoteHost, int remotePort)” crea una socket stream e la collega alla
porta specificata della macchina remota corrispondente al nome dato.
ESEMPIO
Socket s = new Socket(“univpm.it”,13);
oppure
String str = “prova.com”; int port = 14;
Socket s = new Socket(ss,port);
Non bisogna specificare il nome dell’host locale, è implicito.
Per il client, non bisogna specificare il numero della porta (non deve essere noto a
priori). Il sistema assegna automaticamente un numero di porta tra quelli liberi.
Il servizio di DNS è invocato automaticamente.
Si può fare anche:
“public Socket(InetAddress remoteAddr, int remotePort)”
Crea una socket stream e la collega alla porta specificata della
macchina remota corrispondente all’indirizzo IP dato.
Vediamo ora la classe InitAddress del package java.net.
Gli oggetti della classe InetAddress rappresentano indirizzi internet (IP).
La classe InetAddress fornisce dei metodi per gestire facilmente gli indirizzi Internet.
• public static InetAddress getByName(String host) - determina l’indirizzo IP di host.
86
Capitolo 5: Implementazione di un servizio di prenotazioni
ESEMPI
InetAddress.getByName(null);
InetAddress.getByName(“localhost”);
InetAddress.getByName(“127.0.0.1”);
Una volta creata la socket per comunicare: si recuperano dalla socket i due stream (di
ingresso e di uscita) tramite i metodi della classe Socket:
• public InputStream getInputStream()
- restituisce lo stream di input da cui leggere i dati (byte) che giungono dall’altra parte.
• public OutputStream getOutputStream()
- restituisce lo stream di output su cui scrivere i dati (byte) da inviare all’altra parte.
Quindi su questi stream si può leggere / scrivere come su qualunque altro stream di
byte.
Per chiudere la comunicazione: si chiude la socket col metodo:
• public synchronized void close()
- chiude la connessione e libera la risorsa.
Di seguito riportiamo un esempio di un semplice client e di un server multithread:
87
Capitolo 5: Implementazione di un servizio di prenotazioni
//Simple Client
import java.net.*;
import java.io.*;
public class SimpleClient
{
public static void main(String args[])
{
try {
// Apre una connessione verso un server in ascolto sulla porta 7777. In questo caso utilizziamo localhost
// che corrisponde all'indirizzo IP 127.0.0.1
System.out.println("Apertura connessione...");
Socket s1 = new Socket ("127.0.0.1", 7777);
// Ricava lo stream di input dal socket s1 ed utilizza un oggetto wrapper di classe BufferedReader
// per semplificare le operazioni di lettura
InputStream is = s1.getInputStream();
BufferedReader dis = new BufferedReader(new InputStreamReader(is));
// Legge l'input e lo visualizza sullo schermo
System.out.println("Risposta del server: " + dis.readLine());
// Al termine, chiude lo stream di comunicazione e il socket.
dis.close();
s1.close();
System.out.println("Chiusura connessione effettuata");
}catch (ConnectException connExc){
System.err.println("Errore nella connessione ");
}catch (IOException ex){
ex.printStackTrace();
}
}
}
//Server multithread
import java.net.*;
import java.io.*;
public class SimpleServer
{
private int port;
private ServerSocket server;
private Socket client;
public SimpleServer (int port) {
this.port = port;
if(!startServer()) System.err.println("Errore durate la creazione del Server");
}
private boolean startServer(){
try{
server = new ServerSocket(port);
}catch (IOException ex){
ex.printStackTrace();
return false;
}
System.out.println("Server creato con successo!");
return true;
}
88
Capitolo 5: Implementazione di un servizio di prenotazioni
public void runServer()
{
while (true){
try{
// Il server resta in attesa di una richiesta
System.out.println("Server in attesa di richieste...");
client = server.accept();
System.out.println("Un client si e' connesso...");
ParallelServer pServer = new ParallelServer(client);
Thread t = new Thread (pServer);
t.start();
}catch (IOException ex){
ex.printStackTrace();
}
}
}
public class ParallelServer implements Runnable
{
private Socket client;
public ParallelServer (Socket client) {
this.client = client;
}
public void run(){
try{
// Ricava lo stream di output associate al socket e definisce una classe wrapper di tipo
// BufferedWriter per semplificare le operazioni di scrittura
OutputStream s1out = client.getOutputStream();
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(s1out));
// Il server invia la risposta al client
bw.write("Benvenuto sul server!\n");
// Chiude lo stream di output e la connessione
bw.close();
client.close();
System.out.println("Chiusura connessione effettuata");
}catch (IOException ex){
ex.printStackTrace();
}catch (Exception e){
e.printStackTrace();
}
}
}
public static void main (String args[])
{
SimpleServer ss = new SimpleServer(7777);
ss.runServer();
}
}
89
Capitolo 5: Implementazione di un servizio di prenotazioni
La Xlet con il Proxy si scambiano oggetti usando gli input e output stream.
Gli oggetti sono di tre tipi, implementati dalle classi: ObjectPrenotation,
ObjectRegistration, ObjectTavoli.
Ogni oggetto contiene la struttura dati che rappresenta, coerenti con le relative tabelle
del database, dove sono realmente memorizzati, e i metodi per renderli disponibili
all’applicazione.
Il codice della Xlet (lato client) è contenuto nell’appendice A le classi di interesse sono
Comunica che ha come parametri IP del Proxy e numero della porta, PrenotaSingolo.
Il codice dell’applicazione Proxy (lato server), nel nostro caso si usa un singolo thread,
è contenuto nell’appendice B, dove si vede che oltre alla gestione della socket gestisce
anche il recupero dati dal DB e l’invio degli stessi alla Xlet. La classe di interesse è
SimpleServer; in più vi sono i .java dei tre oggetti di cui sopra.
90
Capitolo 5: Implementazione di un servizio di prenotazioni
5.3.2 JDBC
Per effettuare l’interrogazione del Database da parte del nostro Proxy si usa il JDBC
(Java Database Connectivity), è la parte delle API di J2SE che fornisce le primitive per
la connessione a basi di dati relazionali e si basa su due semplici concetti:
•
si inviano comandi SQL;
•
si recuperano i risultati dei comandi.
Il package a cui faremo riferimento è java.sql.
Ogni DB ha una sua API e un suo protocollo (implementato dal driver) particolare.
JDBC astrae dalle particolarità a basso livello delle API e dei protocolli, fornendo
un’interfaccia comune. I driver sottostanti si preoccupano poi di effettuare la traduzione
dei comandi SQL nelle interfacce native dei database supportati come mostrato in figura
5.3.2.1.
L’accesso al database può essere locale o remoto.
Fig. 5.3.2.1 - Schema JDBC
91
Capitolo 5: Implementazione di un servizio di prenotazioni
I driver JDBC sono divisi in 4 Classi:
•
Classe 1: sono i driver JDBC-ODBC.
•
Classe 2: sono i driver che si aspettano di trovare sulla macchina su cui sono
utilizzati uno strato di software, scritto in linguaggio nativo per quella
Macchina/Piattaforma, i quali si preoccupano di connettersi e di operare sul
Database. (nel caso di Oracle i driver in questione vengono chiamati OCI,
Oracle callback Interface, e vogliono che sulla macchina su cui girano ci sia
installato SQL-Net)
•
Classe 3: sono i driver che si aspettano di trovare un Gateway, o in generale un
server a cui connettersi il quale provvederà a ritornare una connessione dal
Database. (i JDBC-RMI driver sono di classe 3)
•
Classe 4: sono i driver totalmente scritti in Java ed autonomi. (Nel caso di
Oracle vengono chiamati THIN).
Vediamo le procedure principali e i metodi che le implementano per effettuare la
connessione ed utilizzare un database:
•Caricare il gestore di driver
DriverManager
•Aprire la connessione
Connection
•Creare un oggetto istruzione
Statement
•Eseguire l’interrogazione (query)
executeQuery
•Elaborare i risultati
ResultSet
•Chiudere la connessione
close
Drive manager implementa un gestore di driver. Il metodo (statico) per ottenere una
connessione al DB è:staticConnectiongetConnection(String,String,String) che prende
tre argomenti: URL, username, password.
Sintassi dell’URL:–jdbc:mysql://<host>[:<port>]/<dbname>
Siccome le connessioni occupano risorse, in un ambiente multiutente e multitasking è
opportuno adottare la seguente politica: aprire una connessione solo quando necessario,
assicurarsi di chiuderla, non aprire/chiudere connessioni inutilmente.
92
Capitolo 5: Implementazione di un servizio di prenotazioni
Connection rappresenta una connessione al DB. Prima di fare qualunque cosa con un
db, devo stabilire una connessione. Ad esempio, per creare un comando, devo disporre
di una connessione (c): Statement s = c.createStatement().
Per chiudere la connessione, si utilizza il metodo close().
Statement rappresenta un’istruzione/comando. Ha i metodi sia per eseguire
un’interrogazione (query) SQL che restituisca un insieme di dati che per eseguire una
query di aggiornamento/modifica del DB:
ResultSet executeQuery(String) int executeUpdate(String).
Quando non serve più va chiuso con il metodo: close
ResultSet rappresenta il risultato di un’interrogazione.
Tramite il metodo next() possiamo spostarci da un record al successivo: boolean next().
Abbiamo a disposizione una serie di metodi per recuperare i dati memorizzati nei vari
campi di ogni record, in base al tipo di dato originario del DB:String getString(int)[gli
indici partono da 1!], getXxx()[Byte,Boolean, Blob, …]. E’ buona norma chiudere il
ResultSet quando non serve più con il metodo: close.
I Driver usati nel Proxy sono “mysql-connector-java-3.1.12”, di clase 4, da ricordare di
importare l’omonimo .jar nel progetto.
Il codice riguardante l’uso dei JDBC nel nostro Proxy è riportato nell’appendice B; le
classi d’interesse sono: ServerPrenotaSingolo, ServerPrenotation, ServerRegistration.
93
Capitolo 5: Implementazione di un servizio di prenotazioni
5.3.3 Database
Il DBMS usato in questo progetto per la precisione è MySQL Server versione 5.0.
Le operazioni svolte direttamente su DB sono solamente la creazione di due tabelle: una
chiamata “cliente” contenente una ridotta anagrafica dell’utente che si andrà a registrare
e l’altra “tavoli” che serve per memorizzare la disponibilità corrente del servizio;
contenente un codice identificativo del tavolo, lo username dell’utente che lo andrà a
prenotare e un campo flag (libero/occupato).
Per completezza riportiamo il DLL:
CREATE TABLE `cliente` (
`user` char(20) NOT NULL default '',
`password` char(20) NOT NULL default '',
`nome` char(20) default NULL,
`cognome` char(20) default NULL,
`telefono` char(15) default NULL,
PRIMARY KEY (`user`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `tavoli` (
`num_tavolo` int(3) NOT NULL default '0',
`utente` char(20) default NULL,
`libero` int(1) default NULL,
PRIMARY KEY (`num_tavolo`),
UNIQUE KEY `IDtav` USING BTREE (`num_tavolo`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
94
Capitolo 5: Implementazione di un servizio di prenotazioni
5.4 Sviluppi futuri
All’inizio del capitolo si è anticipato che il lavoro svolto è stato basato più sull’
ingegnerizzazione del software per permettere di effettuare una prenotazione “on-line”
generica da Xlet, che sul servizio in sé.
Il progetto, così come è descritto nei paragrafi precedenti, è stato implementato e testato
su Set Top Box da sviluppo ed è risultato essere funzionante.
Gli sviluppi possibili, partendo da questa piattaforma, sono innanzitutto basare
l’attenzione su un servizio specifico e quindi curare di più la parte grafica e fare
un’analisi dei dati ad hoc; dal lato tecnico si potrà sviluppare una procedura per
velocizzare l’inserimento dati nei campi di testo, implementare il Proxy in multithread
per gestire l’accesso in concorrenza di più utenti e magari implementare l’uso di lettore
smart card da usare per l’identificazione dell’utente tramite carta d’identità elettronica
per esempio.
95
Conclusioni
Conclusioni
Attualmente l'Italia, come raramente accade per il nostro paese si trova in una posizione
di avanguardia e per molti aspetti sta guidando a livello europeo questa rivoluzione,
soprattutto in ambito Televisione Digitale Terrestre. In commercio possiamo trovare
diverse marche di decoder interattivi MHP e tutti i principali broadcaster sono
impegnati a scoprire le potenzialità, i limiti e le possibili fonti di guadagno di questa
nuova tecnologia. Difficile fare pronostici soprattutto in merito alla legge del nostro
parlamento che sancisce entro dicembre 2008 il cosiddetto "switch-off" delle
trasmissioni analogiche per passare esclusivamente al sistema digitale. Difficile anche
capire se un maggior numero di canali implicherà una maggior scelta e auspicabilmente
maggior qualità per noi telespettatori. Le certezze maggiori rimangono quindi quelle
tecnologiche legate ad uno standard europeo aperto che mira alla convergenza del
mondo terrestre, cavo e satellite, dove invece oggi regnano sovrane troppe tecnologie e
standard proprietari. Siamo di fronte ad una tecnologia in grado di abilitare una delle più
profonde trasformazioni che si possa pensare in una società fortemente fondata
sull'immagine e sulla comunicazione come è la nostra. Un trasformazione che con ogni
probabilità non si limiterà ad influenzare l'ambiente casalingo, ma si estenderà
rapidamente anche ai terminali mobili e tascabili, il DVB-H e le sue applicazioni.
96
APPENDICE A: Codice Xlet
APPENDICE A
Codice Xlet
Main.java
import javax.tv.xlet.Xlet;
import javax.tv.xlet.XletContext;
import javax.tv.xlet.XletStateChangeException;
import org.havi.ui.*;
import org.havi.ui.event.*;
import java.awt.event.*;
//Classe iniziale è questa che implementa l'interfaccia Xlet e KeyListener e che quindi sarà
//utilizzata dall'application manager per gestire il
//ciclo di vita della xlet. Si occupa di inizializzare
//i vari componenti, ascoltare gli eventi dal telecomando,
//creare l'oggetto scene fondamentale per la parte grafica.
public class Main implements Xlet, KeyListener {
public XletContext context;
public static GestoreSfondi gestoreSfondi;
public HScene scene;
public int tastoPremuto = 0;
private InterfacciaGrafica interfaccia;
//Costruttore vuoto.
public Main() {
}
// Definisco un metodo che inizializza la Xlet, e i suoi componenti.
public void initXlet(XletContext xletContext) throws XletStateChangeException {
//xletContext utilizzato per la gestione del ciclo di vita della xlet
context = xletContext;
HSceneFactory hsceneFactory = HSceneFactory.getInstance();
//estraggo l'oggetto scene su cui si appoggiano i componenti grafici.
scene =
hsceneFactory.getFullScreenScene(HScreen.getDefaultHScreen().getDefaultHGraphicsDevice());
//le dimensioni sono quelle standard di risoluzione di un televisore analogico.
scene.setSize(720, 576);
scene.setVisible(false);
scene.setLayout(null);
//aggiungo l'ascoltatore degli eventi in questo caso
//la classe stessa.
scene.addKeyListener((KeyListener)this);
}
// Definisco il metodo che permette di mandare in esecuzione la Xlet
public void startXlet() throws XletStateChangeException {
System.out.println("Eseguo startXlet");
gestoreSfondi = new GestoreSfondi(context);
interfaccia = new InterfacciaGrafica(scene);
97
APPENDICE A: Codice Xlet
// Imposto lo sfondo e ridimensiono il video
gestoreSfondi.displayBackground("background.mpg",240,100,420,320);
// Inizializzo l'interfaccia grafica
interfaccia.disegnaInit();
// Richiedo il Focus per la scene corrente
scene.requestFocus();
scene.setVisible(true);
}
// Definisco il metodo per mettere in pausa la Xlet
public void pauseXlet() {
System.out.println("Xlet in pausa");
context.notifyPaused();
}
// Definisco il metodo che distrugge la Xlet e rilascia le risorse.
public void destroyXlet(boolean flag) throws XletStateChangeException {
if(flag){
System.out.println("Distruggi Xlet");
/*
try{
RCconnector.Disconnect();
}catch (IOException e){
e.printStackTrace();
}
*/
interfaccia.distruggi();
//rimuovo l'ascoltatore degli eventi.
scene.removeKeyListener(this);
scene.removeKeyListener((KeyListener)this);
scene.setVisible(false);
HSceneFactory.getInstance().dispose(scene);
scene = null;
gestoreSfondi.displayBackgroundExit();
context.notifyDestroyed();
}
}
// Ascoltatore degli eventi legati al telecomando. Gli eventi vengono
//passati alla classe InterfacciaGrafica che poi li passerà alle
//funzioni attive.
public void keyPressed (KeyEvent key) {
int pulsantePremuto = key.getKeyCode();
switch(pulsantePremuto){
//Tastierino numerico
case KeyEvent.VK_0:
case KeyEvent.VK_1:
case KeyEvent.VK_2:
case KeyEvent.VK_3:
case KeyEvent.VK_4:
case KeyEvent.VK_5:
case KeyEvent.VK_6:
case KeyEvent.VK_7:
case KeyEvent.VK_8:
case KeyEvent.VK_9:
98
APPENDICE A: Codice Xlet
//Tasti colorati
case HRcEvent.VK_COLORED_KEY_0:
//rosso
case HRcEvent.VK_COLORED_KEY_1:
//verde
case HRcEvent.VK_COLORED_KEY_2:
//giallo
case HRcEvent.VK_COLORED_KEY_3:
//blu
//Tasti direzionali
case HRcEvent.VK_UP:
case HRcEvent.VK_DOWN:
case HRcEvent.VK_RIGHT:
case HRcEvent.VK_LEFT:
case HRcEvent.VK_ENTER:
//OK
//Vengono passati gli eventi.
interfaccia.keyPressed(key);
break;
//Alla pressione del tasto EXIT del telecomando si
//richiama il metodo destroyXlet, per fermare la xlet.
case HRcEvent.VK_ESCAPE:
try{
destroyXlet(true);
}
catch (XletStateChangeException xsce){
System.out.println("Premuto tasto EXIT "+xsce.toString());
}
break;
default:
break;
}
}
public void keyTyped(KeyEvent ignored) { }
public void keyReleased(KeyEvent ignored) { }
}
InterfacciaGrafica.java
import java.awt.*;
import java.awt.event.*;
//import java.io.IOException;
//import javax.tv.xlet.XletStateChangeException;
import org.havi.ui.*;
import org.havi.ui.event.HRcEvent;
//import javax.tv.xlet.XletContext;
//Questa classe amministra la parte grafica del frame
//principale gestendo l'accesso alle varie funzionalità e
//passando gli eventi ai processi attivi.
public class InterfacciaGrafica {
public static final int MENU=0;
public static final int PRIMO=1;
public static final int DISP=2;
public static final int POPUP=3;
public static final int INIT=4;
99
APPENDICE A: Codice Xlet
public static int STATO;
private HScene scene;
private HIcon [] vetON,vetOFF;
private HText [] mess;
private String como;
private String [] str;
private HIcon tastor;
private GestoreSfondi gestoreSfondi;
private Registra registra;
private Servizi servizi;
private Prenota prenota;
private int tasto=0;
//Utilizzata per il polimorfismo.
private Strumenti strumento=null;
//Costruttore.
public InterfacciaGrafica(HScene scenemain){
//viene passato l'oggetto scene su cui verranno
//aggiunti i vari componenti grafici.
scene=scenemain;
//STATO=INIT;
}
//Crea i componenti grafici e li visualizza.
public void disegnaInit(){
STATO=MENU;
Font font1=new Font("Arial",Font.BOLD,30);
str =new String[3];
str[0]="Questo è un servizio di prenotazioni MHP";
str[1]="Inserisci tutti i campi richiesti";
str[2]="Fai il login ed effettua la prenotazione";
como=str[1];
mess= new HText[3];
for(int i=0;i<3;i++){
mess[i]=new HText(str[i]);
mess[i].setFont(font1);
mess[i].setForeground(Color.white);
mess[i].setBackgroundMode(HVisible.BACKGROUND_FILL);
mess[i].setBackground(new Color(100,100,100,120));
mess[i].setBounds(0,456,720,100);
mess[i].setBordersEnabled(true);
scene.add(mess[i]);
mess[i].setVisible(false);
}
System.out.println("Entrato in disegnainit:mess ok");
vetON = new HIcon[3];
vetOFF = new HIcon[3];
vetOFF[0]=new HIcon(Toolkit.getDefaultToolkit().getImage("servizi_off.jpg"), 50, 100, 140, 30);
vetOFF[1] = new HIcon(Toolkit.getDefaultToolkit().getImage("registra_off.jpg"), 50, 135, 140, 30);
vetOFF[2] = new HIcon(Toolkit.getDefaultToolkit().getImage("prenota_off.jpg"), 50, 170, 140, 30);
vetON[0] = new HIcon(Toolkit.getDefaultToolkit().getImage("servizi_on.jpg"), 50, 100, 140, 30);
vetON[1] = new HIcon(Toolkit.getDefaultToolkit().getImage("registra_on.jpg"), 50, 135, 140, 30);
vetON[2] = new HIcon(Toolkit.getDefaultToolkit().getImage("prenota_on.jpg"), 50, 170, 140, 30);
for(int i=0;i<3;i++){
scene.add(vetON[i]);
scene.add(vetOFF[i]);
100
APPENDICE A: Codice Xlet
vetOFF[i].setVisible(true);
vetON[i].setVisible(false);
}
System.out.println("Entrato in disegnainit:icone ok");
//Istanzio gli oggetti che implementano le funzioni
//della xlet.
prenota=new Prenota(scene);
System.out.println("pre ok");
servizi=new Servizi(scene);
System.out.println("serv ok");
registra=new Registra(scene);
System.out.println("Entrato in disegnainit:strumenti ok");
accendi(tasto);
scene.setVisible(true);
scene.requestFocus();
System.out.println("fine disegnainit");
}
//Gestione grafica dei pulsanti
public void accendi (int numero){
vetOFF[numero].setVisible(false);
vetON[numero].setVisible(true);
mess[numero].setVisible(true);
scene.repaint();
}
public void spegni (int numero){
vetOFF[numero].setVisible(true);
vetON[numero].setVisible(false);
mess[numero].setVisible(false);
scene.repaint();
}
//Viene oscurata la parte grafica e
//si ferma il processo attivo.
//Richiamato del metodo destroyXlet nel momento di
//distruzione della xlet.
public void distruggi(){
this.scene.setVisible(false);
//strumento.stop();
}
public static void premutoEnter(){
}
//Ascoltatore degli eventi del telecomando.
//La gestione delle funzionalità è stata fatta utilizzando le
//interfacce Java questo permette di gestire i processi in maniera dinamica,
//passando gli eventi solo al processo attivo.
public void keyPressed (KeyEvent key) {
int pulsantePremuto = key.getKeyCode();
switch(pulsantePremuto){
case KeyEvent.VK_0:
101
APPENDICE A: Codice Xlet
case KeyEvent.VK_1:
case KeyEvent.VK_2:
case KeyEvent.VK_3:
case KeyEvent.VK_4:
case KeyEvent.VK_5:
case KeyEvent.VK_6:
case KeyEvent.VK_7:
case KeyEvent.VK_8:
case KeyEvent.VK_9:
case HRcEvent.VK_COLORED_KEY_0:
System.out.println("Rosso");
if(STATO==POPUP) break;
if(STATO==INIT){
System.out.println("Passa a menu");
STATO=MENU;
disegnaInit();
break;
}
case HRcEvent.VK_COLORED_KEY_1:
if(STATO==POPUP){
Strumenti.messFlashOff();
break;
}
case HRcEvent.VK_COLORED_KEY_2:
if(STATO==POPUP) break;
if(STATO==PRIMO){
strumento.passaEvento(key);
break;
}
case HRcEvent.VK_COLORED_KEY_3:
if(STATO==POPUP) break;
if(STATO==PRIMO){
strumento.Stop();
mess[tasto].setVisible(true);
//Main.gestoreSfondi.displayBackgroundExit();
Main.gestoreSfondi.displayBackground("background.mpg",240,100,420,320);
scene.repaint();
STATO=MENU;
break;
}
if(STATO==DISP){
Prenota.stopVisu();
mess[tasto].setVisible(true);
//Main.gestoreSfondi.displayBackgroundExit();
Main.gestoreSfondi.displayBackground("background.mpg",240,100,420,320);
scene.repaint();
STATO=MENU;
break;
}
case HRcEvent.VK_RIGHT:
if(STATO==POPUP) break;
System.out.println("stato init"+InterfacciaGrafica.STATO);
System.out.println("Init:Destro");
102
APPENDICE A: Codice Xlet
if(STATO==DISP){
strumento.passaEvento(key);
break;
}
case HRcEvent.VK_LEFT:
if(STATO==POPUP) break;
if(STATO==DISP){
strumento.passaEvento(key);
break;
}
if(STATO==MENU)break;
case HRcEvent.VK_UP:
if(STATO==POPUP) break;
if(STATO==MENU){
spegni(tasto);
if(tasto==0) tasto=2;
else tasto--;
accendi(tasto);
break;
}
case HRcEvent.VK_DOWN:
if(STATO==POPUP) break;
if(STATO==MENU){
spegni(tasto);
if(tasto==2)tasto=0;
else tasto++;
accendi(tasto);
break;
}
case HRcEvent.VK_ENTER:
if(STATO==POPUP) break;
if(STATO==MENU){
if(tasto==0)break;
mess[tasto].setVisible(false);
Main.gestoreSfondi.displayBackgroundExit();
Main.gestoreSfondi.displayBackground("background.mpg",0,0,0,0);
scene.repaint();
/*if(tasto==0){
strumento=servizi;
}*/
if(tasto==1){
strumento=registra;
}
if(tasto==2){
strumento=prenota;
}
strumento.init();
strumento.Start();
break;
}
strumento.passaEvento(key);
default:
break;
}
}
103
APPENDICE A: Codice Xlet
}
GestoreSfondi.java
import javax.tv.xlet.XletContext;
//Gestisce gli sfondi della xlet.
//Utilizzato dalla classe principale "Main". I metodi si appoggiano alla
//classe BackgroundController per modificare l'immagine in background.
public class GestoreSfondi {
//Context della xlet in esecuzione.
private XletContext xContext;
private BackgroundController backgroundManager;
public GestoreSfondi(XletContext context){
backgroundManager = new BackgroundController();
xContext=context;
}
//
Carica l'immagine di sfondo della xlet, oscurando il video.
public void displayBackground(String back) {
if (backgroundManager.init()) {
backgroundManager.hideVideo(xContext);
backgroundManager.display(back);
}
}
// Carica l'immagine di sfondo ridimensionando il video.
public void displayBackground(String back,int x,int y,int l,int h) {
if (backgroundManager.init()) {
backgroundManager.resizeVideo(xContext,x,y,l,h);
backgroundManager.display(back);
}
}
// Rilascia la risorsa Background.
public void displayBackgroundExit() {
backgroundManager.dispose();
}
}
Keypad.java
import java.awt.*;
import java.awt.event.*;
public class Keypad extends Container implements Runnable {
private class NewKey {
private int nk = -1;
public NewKey() { }
public void set(int i) { nk = i; }
public int get() { return nk; }
}
104
APPENDICE A: Codice Xlet
// Keypad size constants, don't change.
private static final int width = 500;
private static final int height = 237;
private String editedText = null;
private char pendingChar = 0;
// Used for keypad output, set by setOutput method
private EditableTextBox textBox = null;
// Images of buttons
private Image redButtonImage = null;
private Image blueButtonImage = null;
private Image okButtonImage = null;
public Keypad() {
setSize(width,height);
// Load button images
redButtonImage = Toolkit.getDefaultToolkit().getImage("Rosso_Off.gif");
blueButtonImage = Toolkit.getDefaultToolkit().getImage("Blu_Off.gif");
// Create numeric buttons and layout
buttons = new KeypadButton[10];
buttons[0] = new KeypadButton(0,"0_-");
buttons[1] = new KeypadButton(1,"1@,;.");
buttons[2] = new KeypadButton(2,"abc2äå");
buttons[3] = new KeypadButton(3,"def3");
buttons[4] = new KeypadButton(4,"ghi4");
buttons[5] = new KeypadButton(5,"jkl5");
buttons[6] = new KeypadButton(6,"mno6ö");
buttons[7] = new KeypadButton(7,"pqrs7");
buttons[8] = new KeypadButton(8,"tuv8");
buttons[9] = new KeypadButton(9,"wxyz9");
buttons[1].setLocation(259,5);
buttons[2].setLocation(339,5);
buttons[3].setLocation(419,5);
buttons[4].setLocation(259,62);
buttons[5].setLocation(339,62);
buttons[6].setLocation(419,62);
buttons[7].setLocation(259,119);
buttons[8].setLocation(339,119);
buttons[9].setLocation(419,119);
buttons[0].setLocation(339,176);
for (int i=0;i<10;i++) {
buttons[i].setVisible(true);
add(buttons[i]);
}
newKey = new NewKey();
}
public void paint(Graphics g) {
// Paint keypad border
//g.setColor(Color.black);
//g.fillRect(254,0,245,height);
// Paint keypad background
g.setColor(new Color(100,100,100,120));
g.fillRect(0,0,width,height);
//g.fillRect(256,2,241,height-4);
// Paint backspace button
105
APPENDICE A: Codice Xlet
//g.drawImage(redButtonImage,10,40,this);
g.setColor(Color.white);
g.setFont(new Font("Tireasias",Font.PLAIN,23));
g.drawString("Su/Giù :Seleziona campo",10,20);
g.drawString("OK :Inserimento",10,50);
g.drawString("< :Backspace",10,80);
g.drawString("> :Space",10,110);
g.setFont(new Font("Tireasias",Font.BOLD,23));
g.setColor(Color.red);
g.drawString("CONFERMA DATI",10,140);
g.setColor(Color.blue);
g.drawString("ANNULLA",10,170);
// Paint space button
//g.drawImage(blueButtonImage,10,90,this);
//g.drawString("= space",90,121);
// Paint components (keys)
super.paint(g);
}
// activeChar is the character in editText which is edited at the moment
int activeChar = 0;
private int numKeyActive = -1; // -1 = no key
private char chosenChar = 0; // 0 = no character
private int timer = 0;
boolean backSpaceSet = false; // backspace has been pressed?
boolean spaceSet = false; // space has been pressed?
private NewKey newKey = null;
private KeypadButton[] buttons;
// Main loop of the thread.
public void run() {
int i=0;
while (true) {
// Sleep for a while so that the CPU won't be too busy.
try { Thread.sleep(100); } catch (Exception e) { System.out.println(e); }
// newKey is shared by two thread, allow only one access at a time
synchronized (newKey) {
if (newKey.get() != -1) {
if (numKeyActive == -1) {
// New key pressed
numKeyActive = newKey.get();
buttons[numKeyActive].press();
textBox.setPending(buttons[numKeyActive].getChar());
newKey.set(-1);
} else {
if (numKeyActive == newKey.get()) {
// Old key re-pressed.
buttons[numKeyActive].press();
textBox.setPending(buttons[numKeyActive].getChar());
newKey.set(-1);
} else {
// New key pressed while old still active
106
APPENDICE A: Codice Xlet
System.out.println("Chosen: "+buttons[numKeyActive].getChar());
textBox.addPending();
buttons[numKeyActive].unpress();
numKeyActive = newKey.get();
buttons[numKeyActive].press();
textBox.setPending(buttons[numKeyActive].getChar());
newKey.set(-1);
}
}
// Every key press sets the timer to 10
timer = 10;
} else {
// If no key press, the timer is decremented.
timer--;
}
if (timer==0) {
// If timer reaches 0 it means that it has gone 10x100ms=1 second since
// the active key was pressed. The active key is now added to the edited
// string (in the text box).
System.out.println("Chosen: "+buttons[numKeyActive].getChar());
textBox.addPending();
buttons[numKeyActive].unpress();
numKeyActive = -1;
}
// If a key is active, backspace always inactivates it.
if (backSpaceSet) {
textBox.backSpace();
if (numKeyActive != -1) {
buttons[numKeyActive].unpress();
numKeyActive = -1;
}
timer = -1;
backSpaceSet = false;
}
// A space is always added to the edited string. If a key is active
// when space is pressed, it get inactivated.
if (spaceSet) {
textBox.space();
if (numKeyActive != -1) {
buttons[numKeyActive].unpress();
numKeyActive = -1;
}
timer = -1;
spaceSet = false;
}
}
}
}
// Acknowledge key press.
public void keyPressed (KeyEvent key) {
int code = key.getKeyCode();
if ((code >= KeyEvent.VK_0) && (code <= KeyEvent.VK_9)) {
// newKey can also be changed by the run method.
System.out.println("keyPad.keyevent"+code);
synchronized (newKey) {
107
APPENDICE A: Codice Xlet
newKey.set(code-KeyEvent.VK_0);
}
}
}
public void backSpace() {
backSpaceSet = true;
}
public void space() {
spaceSet = true;
}
// Assign the key pad to a EditableTextBox.
public void setOutput (EditableTextBox e) {
textBox = e;
}
}
KeypadButton.java
import java.awt.*;
public class KeypadButton extends Component {
// Constants
private static final int KB_WIDTH = 75;
private static final int KB_HEIGHT = 52;
private int keyNumber = 0;
private char[] subChars = null;
private int activeSubChar = -1;
private int subCharCount = 0;
private boolean focused = false;
private static Font mainCharFont = null;
private static Font subCharFont = null;
// Create KeypadButton.
public KeypadButton(int keyNum, String keyChars) {
setSize(KB_WIDTH,KB_HEIGHT);
keyNumber = keyNum;
subChars = keyChars.toCharArray();
subCharCount = keyChars.length();
mainCharFont = new Font("Tirealias",Font.PLAIN,27);
subCharFont = new Font("Tirealias", Font.PLAIN,20);
}
public void paint(Graphics g) {
super.paint(g);
FontMetrics fm = null;
String s = null;
// If the button if focused it is painted with a white border.
if (focused) {
108
APPENDICE A: Codice Xlet
g.setColor(Color.white);
} else {
g.setColor(new Color(0x2a,0x2a,0x2a));
}
g.fillRect(0,0,this.getSize().width,this.getSize().height);
g.setColor(Color.black);
g.fillRect(2,2,this.getSize().width-4,this.getSize().height-4);
// Draw subcharacters. FontMetrics is used for getting character
// widths, so that the text can be centered.
g.setFont(subCharFont);
fm = g.getFontMetrics();
int textWidth = fm.stringWidth(String.valueOf(subChars));
int charX = (KB_WIDTH-textWidth)/2;
for (int i=0;i<subCharCount;i++) {
if (i==activeSubChar) {
g.setColor(Color.white);
} else {
g.setColor(new Color(0x3f,0x3f,0xff));
}
s = String.valueOf(subChars[i]);
g.drawString(s,charX,46);
charX += fm.charWidth(subChars[i]);
}
// Draw key number.
g.setFont(mainCharFont);
fm = g.getFontMetrics();
g.setColor(Color.white);
s = String.valueOf(keyNumber);
g.drawString(s,(KB_WIDTH-fm.stringWidth(s))/2,26);
}
public void press() {
if (activeSubChar == -1) {
focused = true;
activeSubChar = 0;
} else {
activeSubChar = (activeSubChar+1) % subCharCount;
}
repaint();
}
public void unpress() {
activeSubChar = -1;
focused = false;
repaint();
}
public char getChar() {
if (subChars != null) return subChars[activeSubChar];
return 0;
}
}
109
APPENDICE A: Codice Xlet
EditableTextBox.java
import java.awt.*;
public class EditableTextBox extends Component {
private int width = 0;
private int height = 0;
private String editStr = null; // The currently edited text
private String nullStr = null; // The text shown if there's no editStr present
private char pendingChar = 0; // An character which has not yet been added to editStr
private boolean focused = false;
private boolean editActive = false;
// Create the component.
public EditableTextBox(String nullStr, int width,int height) {
setSize(width,height);
this.nullStr = nullStr;
this.width = width;
this.height = height;
}
// Paint the component.
public void paint(Graphics g) {
if (focused) {
if(editActive)g.setColor(Color.red);//colore riquadro focused e edit active
else g.setColor(Color.darkGray); //colore riquadro focused
g.fillRect(0,0,width,height);
}
g.setColor(Color.white);
g.fillRect(2,2,width-4,height-4);
if ((editStr == null) || ((editStr.length() == 0) && !editActive)) {
g.setColor(Color.black);
g.setFont(new Font("Tireasias",Font.PLAIN,height-10));
g.drawString(nullStr,10,height-10);
} else {
g.setColor(Color.black);
g.setFont(new Font("Tireasias",Font.PLAIN,height-10));
g.drawString(editStr,10,height-10);
if (pendingChar != 0) {
g.setColor(Color.red);
FontMetrics fm = g.getFontMetrics();
g.drawString(String.valueOf(pendingChar),10+fm.stringWidth(editStr),height-10);
}
}
}
public void setFocused(boolean b) {
focused = b;
}
// Set the pending character (shown after editStr)
public void setPending(char c) {
pendingChar = c;
repaint();
}
110
APPENDICE A: Codice Xlet
// Add the pending character to editStr
public void addPending() {
editStr += pendingChar;
pendingChar = 0;
repaint();
}
// Remove the last character (pending or last of editStr)
public void backSpace() {
if (pendingChar != 0) {
pendingChar = 0;
} else {
if (editStr.length()>0) {
editStr = editStr.substring(0,editStr.length()-1);
}
}
repaint();
}
// Add space.
public void space() {
if (pendingChar != 0) {
addPending();
}
editStr = editStr + ' ';
}
// Return the edited string.
public String getString() {
return editStr;
}
// Set edit mode on/off
public void setEdit(boolean b) {
editActive = b;
if (editStr == null) { editStr = new String(); }
}
}
111
APPENDICE A: Codice Xlet
Strumenti.java
import java.awt.Color;
import java.awt.Font;
import java.awt.event.KeyEvent;
import java.io.IOException;
import org.havi.ui.*;
//import java.net.*;
//import java.sql.*;
import org.dvb.net.rc.*;
public class Strumenti {
//0:non esistono connessioni 1:conn persistenete 2:conn non persistenete
public static final String IPPROXY="localhost";
//"192.168.1.1"; deit
//"localhost";
//"10.6.160.143"; Asur
public static int connesso=0;
public String []strmask;
public static HScene scene;
private static int StatoOld;
EditableTextBox []etb;
public Keypad keyPad;
private static HText mess,mess2;
public Strumenti(HScene sceneint){
scene=sceneint;
}
public void init(){
InterfacciaGrafica.STATO=InterfacciaGrafica.PRIMO;
keyPad = new Keypad();
new Thread(keyPad).start();
keyPad.setLocation (100,310);
scene.add(keyPad);
keyPad.setVisible(false);
}
public void Start(){}
public void Stop(){
//new Thread(keyPad).stop();
scene.remove(keyPad);
for(int i=0;i<etb.length;i++){
scene.remove(etb[i]);
}
scene.repaint();
}
public boolean popUp(String str,int x,int y){
boolean flag=false;
return flag;
112
APPENDICE A: Codice Xlet
}
public static void Connetti()throws IOException {
//prefisso centralino 73372504
final String phone="733725047027020000";
final String user="[email protected]";
final String pass="979116";
Rsl listener;
ConnectionRCInterface cRCi=null;
RCResourceClient resource_client;
resource_client=new RCResourceClient();
//Per prima cosa acquisiamo un'istanza di RCInterfaceManager
RCInterfaceManager rcim=RCInterfaceManager.getInstance();
//rcim.addResourceStatusEventListener(listener);
//Estraiamo l'array con tutte le possibili interfacce di connessione
//che la nostra applicazione potrà avviare.
//RCInterface rci[] = rcim.getInterfaces();
RCInterface rci[] = rcim.getInterfaces();
int il=0;
if (rci!= null) {
il = rci.length;
System.out.println("Numero delle interfacce: " + il);
} else {
il = 0;
connesso=0;
System.out.println("Nessuna interfaccia supportata (rci =null)");
return;
}
//
//
//
Tra tutte le interfacce riportate nell'array si verifica
quale è una istanza di ConnectionRCInterface e si seleziona
quella appropriata.
//
Elenco l'interfacce
System.out.println("Interfacce trovate:");
for(int i = 0; i<il; i++) {
System.out.println(rci[i].getType());
if(rci[i] instanceof ConnectionRCInterface) {
cRCi = (ConnectionRCInterface) rci[i];
System.out.println("Interfaccia "+i+ ":" + cRCi.getType()+" "+cRCi.toString()+"
DataRate:"+cRCi.getDataRate());
}
}
//
Scelgo l'interfaccia più veloce
/*for(int i = il-1; i>=0; i--) {
if(rci[i] instanceof ConnectionRCInterface) {
113
APPENDICE A: Codice Xlet
cRCi = (ConnectionRCInterface) rci[i];
System.out.println("L'interfaccia usata è" + cRCi.getType());
break;
}
}*/
if(rci[0].getType()==4) connesso=1;
//cRCi = (ConnectionRCInterface) rci[0];
// Forzo a modem
//
cRCi=(ConnectionRCInterface) rci[0];
// Forzo a LAN
//
cRCi=(ConnectionRCInterface) rci[0];
//
Ora abbiamo ottenuto il riferimento alla nostra interfaccia,
//
se non è permanente andiamo a stabilire la connessione.
if (cRCi!= null && cRCi.getType() == RCInterface.TYPE_PSTN) {
L'interfaccia viene riservata per la nostra applicazione
associandogli un client da noi definito.
try{
cRCi.reserve(resource_client, null );
}catch (PermissionDeniedException e){
System.out.println("Errore nel riservare la risorsa: "+e.getMessage());
System.out.println("L' interfaccia non puo essere riservata:Permission Denied!");
return;
}
Setto i parametri per effetture la chiamata e l'autenticazione.
try{
cRCi.setTarget(new ConnectionParameters(phone,user,pass));
}catch (IncompleteTargetException e){
System.out.println("Errore nel setting del target(incomplete):"+e.getMessage());
return;
}catch (PermissionDeniedException e){
System.out.println("Errore nel setting del target(permission denied:"+e.getMessage());
return;
}
//
//
//
//cRCi.addConnectionListener(listener);
Crea la connesione con i parametri precedentemente impostati.
Se la connesione a gia attiva il metodo non ha effetto.
try{
cRCi.connect();
System.out.println("La connessione e stata effettuata!");
}catch (IOException e){
System.out.println("Errore di connessione(io): "+e.getMessage());
return;
}catch (PermissionDeniedException e){
System.out.println("Errore di connessione(permission denied): "+e.getMessage());
return;
}
connesso=2;
}
//
//
}
public boolean getMask(int nc){
strmask=new String[nc];
boolean flag=true;
114
APPENDICE A: Codice Xlet
for(int i=0;i<nc;i++){
strmask[i]=etb[i].getString();
System.out.println(strmask[i]);
if(strmask[i]==null || strmask[i].length()==0) flag=false;
}
return flag;
}
// Messaggio di commento di durata indicata
public static void messFlash(String str,int x,int y){
StatoOld=InterfacciaGrafica.STATO;
InterfacciaGrafica.STATO=InterfacciaGrafica.POPUP;
Font font=new Font("Arial",Font.BOLD,22);
mess= new HText(str);
mess2=new HText("PREMI VERDE per continuare");
mess.setFont(font);
mess2.setFont(font);
mess.setForeground(Color.white);
mess2.setForeground(Color.green);
mess.setBackgroundMode(HVisible.BACKGROUND_FILL);
mess2.setBackgroundMode(HVisible.BACKGROUND_FILL);
mess.setBackground(new Color(100,100,100,120));
mess2.setBackground(new Color(100,100,100,120));
mess.setBounds(x,y,580,25);
mess2.setBounds(x,y+25,580,20);
mess.setBordersEnabled(true);
scene.add(mess);
scene.add(mess2);
mess.setVisible(true);
mess2.setVisible(true);
scene.repaint();
}
public static void messFlashOff(){
InterfacciaGrafica.STATO=StatoOld;
mess.setVisible(false);
scene.remove(mess);
mess2.setVisible(false);
scene.remove(mess2);
scene.repaint();
}
public void passaEvento (KeyEvent key) { }
}
115
APPENDICE A: Codice Xlet
Registra.java
import java.awt.event.KeyEvent;
import java.io.IOException;
import org.havi.ui.*;
import org.havi.ui.event.HRcEvent;
public class Registra extends Strumenti{
private int ic=0; //indice del campo corrente
private static final int dist=40;
boolean edita=false;
Registra(HScene sceneint){
super(sceneint);
}
public void Start(){
etb=new EditableTextBox[5];
etb[0]=new EditableTextBox("Nome",300,30);
etb[0].setLocation(300,70);
etb[1]=new EditableTextBox("Cognome",300,30);
etb[1].setLocation(300,70+dist);
etb[2]=new EditableTextBox("Telefono",300,30);
etb[2].setLocation(300,70+2*dist);
etb[3]=new EditableTextBox("Username",300,30);
etb[3].setLocation(300,70+3*dist);
etb[4]=new EditableTextBox("Password",300,30);
etb[4].setLocation(300,70+4*dist);
for(int i=0;i<5;i++){
etb[i].setVisible(true);
scene.add(etb[i]);
}
etb[ic].setFocused(true);
keyPad.setVisible(true);
scene.repaint();
}
public void passaEvento (KeyEvent key) {
int pulsantePremuto = key.getKeyCode();
switch(pulsantePremuto){
case KeyEvent.VK_0:
case KeyEvent.VK_1:
case KeyEvent.VK_2:
case KeyEvent.VK_3:
case KeyEvent.VK_4:
case KeyEvent.VK_5:
case KeyEvent.VK_6:
case KeyEvent.VK_7:
case KeyEvent.VK_8:
case KeyEvent.VK_9:
116
APPENDICE A: Codice Xlet
if(edita)keyPad.keyPressed(key);
break;
case HRcEvent.VK_COLORED_KEY_0:
System.out.println("Premuto rosso sotto registra");
if (getMask(5)){
//Controlla se esiste user e aggiorna anag
try{
Connetti();
}catch (IOException e){
System.out.println("Errore di connessione(io): "+e.getMessage());
}
//
Registra un nuovo utente
Comunica com=new Comunica(IPPROXY,8000);
ObjectRegistration reg=new ObjectRegistration(strmask[3],strmask[4],strmask[0],strmask[1],strmask[2]);
String ris=(String)(com.Connetti(reg));
System.out.println("ris="+ris);
messFlash(ris,60,265);
//Ritorna registra effettuata con successo o Username già esistente
}else{
//
finestra errore:riempi tutti i campi
messFlash("Compila tutti i campi",60,265);
}
break;
case HRcEvent.VK_COLORED_KEY_1: break;
case HRcEvent.VK_COLORED_KEY_2: break;
case HRcEvent.VK_COLORED_KEY_3:
Stop();
break;
case HRcEvent.VK_UP:
edita=false;
etb[ic].setFocused(false);
etb[ic].setEdit(false);
if(ic==0){
ic=4;
}else ic--;
etb[ic].setFocused(true);
scene.repaint();
break;
case HRcEvent.VK_DOWN:
edita=false;
etb[ic].setEdit(false);
etb[ic].setFocused(false);
if(ic==4){
ic=0;
}else ic++;
etb[ic].setFocused(true);
scene.repaint();
break;
case HRcEvent.VK_RIGHT:
if(edita)keyPad.space();
break;
117
APPENDICE A: Codice Xlet
case HRcEvent.VK_LEFT:
if(edita)keyPad.backSpace();
break;
case HRcEvent.VK_ENTER:
edita=true;
keyPad.setOutput(etb[ic]);
etb[ic].setEdit(true);
scene.repaint();
default: break;
}
}
}
Prenota.java
import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.net.*;
import org.havi.ui.*;
import org.havi.ui.event.HRcEvent;
public class Prenota extends Strumenti{
private int ic=0; //indice del campo corrente
private static final int dist=50;
boolean edita=false;
private boolean riprenota=false;
private int disp [];
private static int ntav=5; //N tot tavoli
private int itav=0; //indice corrente vettore tavoli
private Image tavv,tavr; //icone:tavolo libero,occupato
public static HIcon []vetTav;
private static HText messblu;
Prenota(HScene sceneint){
super(sceneint);
tavv=Toolkit.getDefaultToolkit().getImage("tavolo_v.gif");
tavr=Toolkit.getDefaultToolkit().getImage("tavolo_r.gif");
}
public void Start(){
etb=new EditableTextBox[2];
etb[0]=new EditableTextBox("Username",300,35);
etb[0].setLocation(300,100);
etb[1]=new EditableTextBox("Password",300,35);
etb[1].setLocation(300,100+dist);
for(int i=0;i<2;i++){
etb[i].setVisible(true);
scene.add(etb[i]);
}
etb[ic].setFocused(true);
keyPad.setVisible(true);
scene.repaint();
}
public void visualizzaDisp(){
if(!riprenota){
118
APPENDICE A: Codice Xlet
Main.gestoreSfondi.displayBackground("background.mpg",260,100,225,180);
Font font=new Font("Arial",Font.BOLD,20);
messblu= new HText("PREMI TASTO BLU PER USCIRE");
messblu.setFont(font);
messblu.setForeground(Color.blue);
messblu.setBackgroundMode(HVisible.BACKGROUND_FILL);
messblu.setBackground(new Color(100,100,100,120));
messblu.setBounds(60,460,580,20);
messblu.setBordersEnabled(true);
scene.add(messblu);
}
if(riprenota){
for(int i=0;i<ntav;i++){
scene.remove(vetTav[i]);
}
System.out.println("RIPRENOTA");
scene.repaint();
}
messblu.setVisible(true);
InterfacciaGrafica.STATO=InterfacciaGrafica.DISP;
System.out.println("stato visu disp"+InterfacciaGrafica.STATO);
vetTav=new HIcon[ntav];
for(int i=0;i<ntav;i++){
if(disp[i]==0)vetTav[i]= new HIcon(tavv,110+100*i,300,100,80);
else vetTav[i]= new HIcon(tavr,110+100*i,300,100,80);
vetTav[i].setBackground(new Color(100,100,100,120));
scene.add(vetTav[i]);
vetTav[i].setVisible(true);
}
vetTav[itav].setBackgroundMode(HVisible.BACKGROUND_FILL);
scene.repaint();
}
public static void stopVisu(){
for(int i=0;i<ntav;i++){
scene.remove(vetTav[i]);
}
messblu.setVisible(false);
//scene.remove(messblu);
scene.repaint();
InterfacciaGrafica.STATO=InterfacciaGrafica.MENU;
}
public void passaEvento (KeyEvent key) {
int pulsantePremuto = key.getKeyCode();
switch(pulsantePremuto){
case KeyEvent.VK_0:
case KeyEvent.VK_1:
case KeyEvent.VK_2:
case KeyEvent.VK_3:
case KeyEvent.VK_4:
case KeyEvent.VK_5:
case KeyEvent.VK_6:
case KeyEvent.VK_7:
119
APPENDICE A: Codice Xlet
case KeyEvent.VK_8:
case KeyEvent.VK_9:
if(edita)keyPad.keyPressed(key);
break;
case HRcEvent.VK_COLORED_KEY_0:
if(getMask(2)){
try{
Connetti();
}catch (IOException e){
System.out.println("Errore di connessione(io): "+e.getMessage());
}
System.out.println("connesso:"+connesso);
//prova();
Comunica com=new Comunica(IPPROXY,8000);
ObjectPrenotation pren=new ObjectPrenotation(strmask[0],strmask[1]);
System.out.println("creato ObjectPrenotation");
Object tavoli=com.Connetti(pren);
System.out.println("ritornato tavoli");
if(tavoli instanceof ObjectTavoli) {
String ris="";
disp=new int[((ObjectTavoli)tavoli).getLength()];
for (int i=0;i<((ObjectTavoli)tavoli).getLength();i++) {
String u=(((ObjectTavoli)tavoli).getUtente(i));
String =String.valueOf(((ObjectTavoli)tavoli).getNumero(i));
String l=String.valueOf(((ObjectTavoli)tavoli).getLibero(i));
disp[i]=((ObjectTavoli)tavoli).getLibero(i);
ris=ris+"\nUtente: "+u+" Numero: "+n+" Libero: "+l;
}
System.out.println(ris);
Stop();
visualizzaDisp();
messFlash("Login corretto,prenota il tuo tavolo!",60,415);
riprenota=true;
break;
}
if(tavoli instanceof String) {
System.out.println(tavoli);
messFlash((String)tavoli,60,265);
}
//Ritorna Username o password errati
}else{
System.out.println("compila tutto");
messFlash("Compila tutti i campi",60,265);
}
break;
case HRcEvent.VK_COLORED_KEY_1: break;
case HRcEvent.VK_COLORED_KEY_2: break;
case HRcEvent.VK_COLORED_KEY_3:
if(InterfacciaGrafica.STATO==InterfacciaGrafica.DISP){
stopVisu();
120
APPENDICE A: Codice Xlet
}
break;
case HRcEvent.VK_UP:
edita=false;
etb[ic].setFocused(false);
etb[ic].setEdit(false);
if(ic==0){
ic=1;
}else ic--;
etb[ic].setFocused(true);
scene.repaint();
break;
case HRcEvent.VK_DOWN:
edita=false;
etb[ic].setEdit(false);
etb[ic].setFocused(false);
if(ic==1){
ic=0;
}else ic++;
etb[ic].setFocused(true);
scene.repaint();
break;
case HRcEvent.VK_RIGHT:
System.out.println("Destro");
if(edita)keyPad.space();
if(InterfacciaGrafica.STATO==InterfacciaGrafica.DISP){
vetTav[itav].setBackgroundMode(HVisible.NO_BACKGROUND_FILL);
if(itav>=ntav-1){
itav=0;
}else itav++;
vetTav[itav].setBackgroundMode(HVisible.BACKGROUND_FILL);
scene.repaint();
}
break;
case HRcEvent.VK_LEFT:
if(edita)keyPad.backSpace();
if(InterfacciaGrafica.STATO==InterfacciaGrafica.DISP){
vetTav[itav].setBackgroundMode(HVisible.NO_BACKGROUND_FILL);
if(itav<=0){
itav=ntav-1;
}else itav--;
vetTav[itav].setBackgroundMode(HVisible.BACKGROUND_FILL);
scene.repaint();
}
break;
case HRcEvent.VK_ENTER:
if(InterfacciaGrafica.STATO==InterfacciaGrafica.DISP){
if(disp[itav]==0){
Comunica com=new Comunica(IPPROXY,8000);
PrenotaSingolo pren=new PrenotaSingolo(strmask[0],itav+1);//user e n tavolo 1a5
Object p=com.Connetti(pren);
disp[itav]=1;
121
APPENDICE A: Codice Xlet
if (p instanceof String) {
System.out.println(p);
//Prenotazione eseguita con successo
visualizzaDisp();
messFlash((String)p,60,415);
}
}else{
messFlash("Tavolo già riservato",60,415);
}
break;
}
edita=true;
keyPad.setOutput(etb[ic]);
etb[ic].setEdit(true);
scene.repaint();
default: break;
}
}
}
Comunica.java
import java.net.*;
import java.io.*;
//import java.util.*;
public class Comunica {
private String IP;
private int port;
private String str;
private Object obj;
private Object objW;
public Comunica(String sIP,int p){
this.port=p;
this.IP=sIP;
}
public Object Connetti(Object object){
obj=null;
objW=object;
try{
// Apre una connessione verso un server in ascolto
// sulla porta "port", all'indirizzo "IP"
System.out.println("Apertura Socket...");
Socket sock = new Socket(IP,port);
System.out.println("creato socket");
OutputStream s1out = sock.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(s1out);
System.out.println("impostato outpustream");
oos.writeObject(objW);
// Ricava lo stream di input dal socket sock
// ed utilizza un oggetto wrapper di classe BufferedReader
// per semplificare le operazioni di lettura
InputStream is = sock.getInputStream();
ObjectInputStream ois = new ObjectInputStream(is);
//BufferedReader dis = new BufferedReader(new InputStreamReader(is));
// Legge l'input e lo visualizza sullo schermo
122
APPENDICE A: Codice Xlet
//str=dis.readLine();
try{
obj=ois.readObject();}
catch (ClassNotFoundException e) {
System.out.println("Error: "+e.getMessage());
}
ois.close();
sock.close();
System.out.println("Chiusura connessione effettuata");
}catch (ConnectException connExc){
System.out.println("Errore nella connessione ");
}
catch (IOException ex){
ex.printStackTrace();
}
return obj;
}
PrenotaSingolo pren=new PrenotaSingolo("jack",1);
Object p=com.Connetti(pren);
if (p instanceof String) {
System.out.println(p);
}
}
}
PrenotaSingolo.java
import java.io.*;
public class PrenotaSingolo implements Serializable {
private String user;
private int numero;
public PrenotaSingolo (String user, int numero) {
this.user=user;
this.numero=numero;
}
public String getUser() {
return user;
}
public int getNumero() {
return numero;
}
}
123
APPENDICE B: Codice Proxy
APPENDICE B
Codice Proxy
SimpleServer.java
import java.net.*;
import java.io.*;
//import java.util.*;
public class SimpleServer {
public static final String IPDB="localhost";
public static final String USERDB="fabiob";
public static final String PASSDB="979116";
private int port;
private ServerSocket server;
private Object obj;
private Object prenot;
private String ris;
private Object tavoli;
private Object prenotaS;
public SimpleServer (int port){
ris="";
this.port = port;
if(!startServer()) System.err.println("Errore durante la creazione del Server");
}
private boolean startServer(){
try{
server = new ServerSocket(port);
}catch (IOException ex){
ex.printStackTrace();
return false;
}
System.out.println("Server creato con successo!");
return true;
}
public void runServer(){
while (true){
try{
// Il server resta in attesa di una richiesta
System.out.println("Server in attesa di richieste...");
Socket s1 = server.accept();
System.out.println("Un client si e' connesso...");
InputStream is = s1.getInputStream();
ObjectInputStream ois = new ObjectInputStream(is);
try{
obj=(Object)(ois.readObject());
System.out.println("oggetto letto");
}
124
APPENDICE B: Codice Proxy
catch (ClassNotFoundException e) {
System.out.println("Error: "+e.getMessage());
}
if (obj instanceof ObjectRegistration) {
ris=(new ServerRegistration((ObjectRegistration)obj)).start();
// Ricava lo stream di output associate al socket
// e definisce una classe wrapper di tipo
// BufferedWriter per semplificare le operazioni
// di scrittura
OutputStream s1out = s1.getOutputStream();
// BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s1out));
ObjectOutputStream oos = new ObjectOutputStream(s1out);
// Il server invia la risposta al client
// oos.writeObject("Benvenuto sul server!\n");
oos.writeObject(ris);
// Chiude lo strema di output e la connessione
oos.close();
}
if(obj instanceof ObjectPrenotation) {
tavoli=(new ServerPrenotation((ObjectPrenotation)obj)).start();
OutputStream s1out = s1.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(s1out);
oos.writeObject(tavoli);
oos.close();
}
if (obj instanceof PrenotaSingolo) {
prenotaS=(new ServerPrenotaSingolo((PrenotaSingolo)obj)).start();
OutputStream s1out = s1.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(s1out);
oos.writeObject(prenotaS);
oos.close();
}
s1.close();
System.out.println("Chiusura connessione effettuata\n");
}catch (IOException ex){
ex.printStackTrace();
}
}
}
public static void main(String[] args) {
SimpleServer ss = new SimpleServer(8000);
ss.runServer();
}
}
125
APPENDICE B: Codice Proxy
ServerRegistration.java
import java.sql.*;
public class ServerRegistration {
private ObjectRegistration obj;
private String ris;
public ServerRegistration(ObjectRegistration object) {
this.obj=object;
ris="";
}
public String start() {
try {
Class.forName("com.mysql.jdbc.Driver");
System.out.println("eseguito class.forname");
}
catch (ClassNotFoundException e1){}
try{
String user=SimpleServer.USERDB;
String pass=SimpleServer.PASSDB;
String url="jdbc:mysql://"+SimpleServer.IPDB+"/prenotazioni";
Connection con = DriverManager.getConnection(url,user,pass);
System.out.println("Connessione DB: "+url+user+pass);
Statement stmt=con.createStatement();
System.out.println(((ObjectRegistration)obj).getUser());
String query= "SELECT user FROM cliente WHERE
(user='"+((ObjectRegistration)obj).getUser()+"');";
ResultSet rm=stmt.executeQuery(query);
while (rm.next()) {
ris=rm.getString(1);
}
if (ris=="") {
String query2= "INSERT INTO cliente (user,password,nome,cognome,telefono) VALUES
('"+((ObjectRegistration)obj).getUser()+"','"+((ObjectRegistration)obj).getPassword()+"','"+((ObjectRegi
stration)obj).getNome()+"','"+((ObjectRegistration)obj).getCognome()+"','"+((ObjectRegistration)obj).get
Telefono()+"');";
stmt.executeUpdate(query2);
ris="Registrazione effettuata con successo!";
System.out.println("query effettuata");
}
else {
ris="Username esistente.";
}
rm.close();
stmt.close();
con.close();
}catch(SQLException exc) {
System.out.println("SQL error: "+exc.getMessage());
}
return ris;
}
}
126
APPENDICE B: Codice Proxy
ServerPrenotation.java
import java.sql.*;
public class ServerPrenotation {
private ObjectPrenotation obj;
private Object tavoli;
public ServerPrenotation(ObjectPrenotation obj) {
this.obj=obj;
}
public Object start() {
try {
Class.forName("com.mysql.jdbc.Driver");
System.out.println("eseguito class.forname");
}
catch (ClassNotFoundException e1){}
try{
String user=SimpleServer.USERDB;
String pass=SimpleServer.PASSDB;
String url="jdbc:mysql://"+SimpleServer.IPDB+"/prenotazioni";
Connection con = DriverManager.getConnection(url,user,pass);
System.out.println("Connessione DB: "+url+user+pass);
Statement stmt=con.createStatement();
String query="SELECT user FROM cliente WHERE user='"+obj.getUser()+"' AND
password='"+obj.getPassword()+"';";
ResultSet rm=stmt.executeQuery(query);
String ris1="";
while(rm.next()) {
ris1=ris1+rm.getString(1);
}
rm.close();
if (ris1=="") {
System.out.println("user pwd errati");
tavoli="Username o password errati.";
}
else {
System.out.println("user pwd esatti");
String query2= "SELECT count(*) FROM tavoli;";
ResultSet rm2=stmt.executeQuery(query2);
System.out.println("eseguita query2");
int length=0;
while (rm2.next()) {
length=Integer.parseInt(rm2.getString(1));
System.out.println("length: "+length);
}
rm2.close();
String query3= "SELECT * FROM tavoli;";
ResultSet rm3=stmt.executeQuery(query3);
int[] num=new int[length];
String[] utente=new String[length];
int[] libero=new int[length];
int j=0;
while (rm3.next()) {
num[j]=Integer.parseInt(rm3.getString(1));
System.out.println(num[j]);
utente[j]=rm3.getString(2);
127
APPENDICE B: Codice Proxy
System.out.println(utente[j]);
libero[j]=Integer.parseInt(rm3.getString(3));
System.out.println(libero[j]);
j++;
}
tavoli=new ObjectTavoli(utente,num,libero);
rm3.close();
}
stmt.close();
con.close();
}
catch(SQLException exc) {
System.out.println("SQLException: "+exc.getMessage());
}
return tavoli;
}
}
ServerPrenotaSingolo.java
import java.sql.*;
public class ServerPrenotaSingolo {
private PrenotaSingolo obj;
private String ris;
public ServerPrenotaSingolo(PrenotaSingolo obj) {
this.obj=obj;
}
public Object start() {
ris="";
try {
Class.forName("com.mysql.jdbc.Driver");
System.out.println("eseguito class.forname");
}
catch (ClassNotFoundException e1){}
try{
String user=SimpleServer.USERDB;
String pass=SimpleServer.PASSDB;
String url="jdbc:mysql://"+SimpleServer.IPDB+"/prenotazioni";
Connection con = DriverManager.getConnection(url,user,pass);
System.out.println("Connessione DB: "+url+user+pass);
Statement stmt=con.createStatement();
String query= "UPDATE tavoli SET libero=1 ,utente='"+obj.getUser()+"' WHERE
num_tavolo="+obj.getNumero()+";";
stmt.executeUpdate(query);
ris="Prenotazione eseguita con successo!";
stmt.close();
con.close();
}
catch (SQLException e) {
128
APPENDICE B: Codice Proxy
System.out.println("Eccezione: "+e.getMessage());
ris="Errore prenotazione";
}
return ris;
}
}
PrenotaSingolo.java
import java.io.*;
public class PrenotaSingolo implements Serializable {
private String user;
private int numero;
public PrenotaSingolo (String user, int numero) {
this.user=user;
this.numero=numero;
}
public String getUser() {
return user;
}
public int getNumero() {
return numero;
}
}
ObjectRegistration.java
import java.io.*;
public class ObjectRegistration implements Serializable{
private String user;
private String password;
private String nome;
private String cognome;
private String telefono;
public ObjectRegistration(String u, String pwd, String nom, String cog, String tel) {
this.user=u;
this.password=pwd;
this.nome=nom;
this.cognome=cog;
this.telefono=tel;
System.out.println("creato objectRegistration");
}
public String getUser() {
return this.user;
}
public String getPassword() {
return this.password;
}
public String getNome() {
129
APPENDICE B: Codice Proxy
return this.nome;
}
public String getCognome() {
return this.cognome;
}
public String getTelefono() {
return this.telefono;
}
}
ObjectPrenotation.java
import java.io.*;
public class ObjectPrenotation implements Serializable {
private String user;
private String password;
public ObjectPrenotation (String user, String password){
this.user=user;
this.password=password;
}
public String getUser() {
return this.user;
}
public String getPassword() {
return this.password;
}
}
ObjectTavoli.java
import java.io.*;
public class ObjectTavoli implements Serializable{
private String[] utente;
private int[] num_tavolo;
private int[] libero;
public ObjectTavoli(String[] utente, int[] num, int[] libero) {
this.num_tavolo=num;
this.libero=libero;
this.utente=utente;
}
public String getUtente(int i) {
return utente[i];
}
public int getNumero(int i) {
return num_tavolo[i];
}
public int getLibero(int i) {
return libero[i];
}
public int getLength() {
return utente.length;
}
}
130
Siti e Bibliografia
SITI E BIBLIOGRAFIA
www.mhp.org
www.openmhp.org
www.dvb.org
www.etsi.org
www.cineca.it
www.sun.it
www.myslq.com
www.java.sun.com
www.javastaff.com
www.sourceforge.net
www.eclipse.org
www.coremedia.com
www.televisionedigitaleterrestre.it
www.interactivetvweb.org
www.digisoft.it
www.digitaleterrestre.it
Steven Morris, Anthony Smith-Chaigneau:
“Interactive TV Standard – A Guide to MHP, OCAP and Java TV”.
George Reese:
“O'Reilly-Database Programming with JDBC and Java, Second Edition”.
V.Mignone, A.Morello, M.Visintin:
“Lo standard DVB-T per la televisione digitale terrestre”.
E.Fleischner,B.Somalvico:
"La TV diventa digitale", Franco Angeli, 2002.
131
UNIVERSITÀ POLITECNICA DELLE MARCHE
FACOLTÀ DI INGEGNERIA
TESI DI LAUREA IN INGEGNERIA
INFORMATICA E DELL’AUTOMAZIONE
Studio ed implementazione di un servizio di
prenotazioni
“on-line”
su
piattaforma
MHP
utilizzando Set Top Box da sviluppo ADB X-75
RELATORE :
Prof. Aldo Franco DRAGONI
CORRELATORE:
Prof. Paolo PULITI
CANDIDATO:
Bracalente Fabio
Anno Accademico 2005-2006
UNIVERSITÀ POLITECNICA DELLE MARCHE
FACOLTÀ DI INGEGNERIA
CORSO DI LAUREA IN INGEGNERIA
INFORMATICA E DELL’AUTOMAZIONE
Studio ed implementazione di un servizio
di prenotazioni “on-line” su piattaforma MHP
utilizzando Set Top Box da sviluppo ADB X-75
Relatore :
Prof. Aldo Franco DRAGONI
Correlatore:
Prof. Paolo PULITI
Tesi di Laurea di:
Bracalente Fabio
Anno Accademico 2005-2006
Dietro ogni problema c’è un’opportunità…
Galileo Galilei
Ringraziamenti
Sarò in controtendenza ma vorrei iniziare dai ringraziamenti “tecnici” per arrivare poi a
quelli affettivi.
Ringrazio il Prof. Aldo Franco Dragoni per avermi spinto verso una nuova tecnologia in
fase di sviluppo, che speriamo apra nuovi sbocchi, grazie anche per avermi dato fiducia
e costante assistenza in questo progetto.
Al Dott. Fabio Tonucci per la sua disponibilità durante il periodo di tirocinio.
Al Prof. Paolo Puliti per avermi fatto scoprire un nuovo linguaggio di programmazione,
e per avermi insegnato a vedere il Mondo ad “Oggetti”.
Vorrei ricordare il Prof. Maurizio Panti e ringraziarlo perché devo a lui le conoscenze
in campo di analisi dei dati e Database.
Vorrei ringraziare anche tutti gli altri professori che ho incontrato nel cammino
scolastico e soprattutto universitario perché ognuno di loro ha contribuito a formarmi
sia culturalmente che caratterialmente.
Ai colleghi tirocinanti con cui ho condiviso questa nuova esperienza.
Un ringraziamento veramente di cuore va in particolare ai miei genitori perché mi
hanno sempre sostenuto e lasciato libero in tutte le mie scelte e soprattutto perché senza
di loro tutto questo non sarebbe stato possibile.
A mio fratello, spirito guida ed esempio di diligenza sicuramente da seguire.
A tutti i miei nonni che mi hanno trasmesso, una in particolare che continua tuttora a
farlo, un po’ della loro saggezza.
Ai tutti i miei zii che fin da piccolo mi hanno allietato tante giornate e qualcuno in
particolare mi ha anche aiutato, in tutti i campi, in fasi particolari della mia vita.
Ai miei cugini più o meno piccoli perché, anche se li trascuro un po’, li porto sempre
nel cuore.
Un grazie anche a tutti i diversi coinquilini con cui ho convissuto in tutti questi anni.
Un grazie a tutti gli amici e amiche che mi sono stati vicino e spero che continuino a
farlo.
Un ringraziamento doveroso all’Hotstaff che è stato sinonimo di sostentamento nel
periodo degli studi, ma soprattutto: di grandi amicizie, di innumerevoli conoscenze, di
momenti di divertimento allo stato puro, una vera e propria medicina nei periodi tristi di
questi ultimi anni; forse anche la causa dell’allungamento della mia carriera
universitaria, ma ne è valsa la pena, grazie a tutti di cuore.
Ultimo, ma sicuramente non per importanza, un grazie tutto per lei lo devo alla mia
ragazza; grazie semplicemente di esistere e di avermi sopportato e supportato in questi
ultimi periodi.
Sommario
Sommario
GLOSSARIO................................................................................................................... 1
INTRODUZIONE........................................................................................................... 2
CAPITOLO 1
IL DIGITALE TERRESTRE ........................................................................................ 3
1.1 INTRODUZIONE ......................................................................................................... 3
1.2 NOVITÀ TECNOLOGICHE ........................................................................................... 3
1.3 LO STANDARD MHP................................................................................................. 7
1.4 BROADCASTING DI APPLICAZIONI INTERATTIVE ..................................................... 10
1.5 IL DECODER INTERATTIVO ...................................................................................... 11
CAPITOLO 2
APPLICAZIONI MHP................................................................................................. 13
2.1 MODELLO DI BUSINESS E INTERFACCE ................................................................... 13
2.2 XLET ...................................................................................................................... 15
2.3 GESTIONE DELLE RISORSE SCARSE ......................................................................... 18
2.4 MODELLO GRAFICO ................................................................................................ 25
2.4.1 Background layer........................................................................................... 26
2.4.2 Video layer ..................................................................................................... 32
2.4.3 Graphics layer ............................................................................................... 35
2.5 GESTIONE DEGLI EVENTI DEL TELECOMANDO ........................................................ 38
CAPITOLO 3
CANALE DI RITORNO .............................................................................................. 41
3.1 LO SCENARIO ......................................................................................................... 41
3.2 I PROTOCOLLI SUPPORTATI ..................................................................................... 45
3.2.1 Livello 1: Fisico ............................................................................................. 45
3.2.2 Livello 2: PPP................................................................................................ 46
3.2.3 Protocollo di rete: IP ..................................................................................... 48
3.2.4 Protocolli di trasporto: TCP / UDP .............................................................. 48
3.2.5 Hypertext Transfer Protocol (HTTP)............................................................. 49
3.2.6 Secure Hypertext Transfer Protocol (HTTPS)............................................... 51
3.2.7 Servizi Specifici.............................................................................................. 51
3.3 GESTIONE DELLA CONNESSIONE ............................................................................. 52
3.3.1 Utilizzare il canale di ritorno: il package org.dvb.net.rc .............................. 52
3.3.2 Interfacce per il Canale di Ritorno ................................................................ 53
3.3.3 Acquisire l’interfaccia corretta...................................................................... 55
3.3.4 Stabilire la connessione ................................................................................. 57
3.3.5 I package java.net e java.io ........................................................................... 61
I
Sommario
CAPITOLO 4
AMBIENTE DI SVILUPPO ........................................................................................ 62
4.1 INTRODUZIONE....................................................................................................... 62
4.2 IL SIMULATORE XLETVIEW ................................................................................... 63
4.3 SET TOP BOX DA SVILUPPO ADB X-75.................................................................. 66
4.3.1 Caratteristiche Tecniche................................................................................ 67
4.3.2 Pannello frontale ........................................................................................... 69
4.3.3 Pannello posteriore........................................................................................ 70
4.3.4 Telecomando .................................................................................................. 71
4.3.5 Settaggio connessione di default.................................................................... 72
4.3.6 STB Firmware upgrade.................................................................................. 73
4.3.7 UpLoad e debug di un’applicazione ............................................................. 74
4.3.8 Xlet description file ........................................................................................ 76
CAPITOLO 5
IMPLEMENTAZIONE DI UN SERVIZIO DI PRENOTAZIONI ......................... 78
5.1 LE PROBLEMATICHE ............................................................................................... 79
5.2 INTERFACCIA GRAFICA ........................................................................................... 80
5.3 ACCESSO AI DATI ................................................................................................... 84
5.3.1 Socket ............................................................................................................. 85
5.3.2 JDBC.............................................................................................................. 91
5.3.3 Database ........................................................................................................ 94
5.4 SVILUPPI FUTURI .................................................................................................... 95
CONCLUSIONI............................................................................................................ 96
APPENDICE A
CODICE XLET............................................................................................................. 97
APPENDICE B
CODICE PROXY ....................................................................................................... 124
SITI E BIBLIOGRAFIA............................................................................................ 131
II
Glossario
Glossario
DTT: Digital Terrestrial Television
DVB: Digital Video Broadcasting
DTv: Digital Television
EPG: Electronic Program Guide
ETSI: European Telecommunications Standards Institute
FTV: Free To View
HDTV: High Definition Television Defined
MHP: Multimedia Home Platform
MPEG: Moving Picture Expert Group
PAL: Phase Alternative Line
QAM: Quadrature Amplitude Modulation
SCART: Syndicat Francais des Constructeurs d’Appareils Radio et Television
IP: Internet Protocol
ISP: Internet Service Provider
TCP: Transmission Control Protocol
UDP: User Data Protocol
HTTP: Hypertext Transfer Protocol
TLS: Transport Layer Security
LAN: Local Area Network
1
Introduzione
Introduzione
La digitalizzazione della televisione terrestre è forse l'ultimo anello importante che
manca alla catena che in poco più di 10 anni ha trasformato in digitale i principali flussi
informativi che viaggiano sul nostro pianeta. Da qualche tempo dopo aver tratto
insegnamento dalle prime esperienze effettuate in UK, Spagna e Finlandia, anche l'Italia
ha intrapreso il cammino che, nel corso dei prossimi due anni, porterà a trasformare le
centinaia di stazioni televisive che popolano l'etere nazionale in altrettanti canali
digitali. Con la televisione Digitale Terrestre (DTT: Digital Terrestrial Television)
arriveranno non solo trasmissioni televisive di miglior qualità e in maggior quantità,
ma anche nuove possibilità di servizi interattivi, che da un lato potranno rendere più
ricca ed interessante l'esperienza del telespettatore, e dall'altro apriranno un canale di
comunicazione bidirezionale, e quindi di natura del tutto nuova, che potrà raggiungere
qualsiasi famiglia del nostro paese. Proprio la bidirezionalità della comunicazione è un
fattore che suscita molte aspettative in diversi settori: dalle Pubbliche Amministrazioni,
che vedono nel sistema un’opportunità per avvicinare i cittadini e rendere loro più
facilmente disponibili tutti i propri servizi, ai vari privati che sfrutteranno al massimo un
nuovo strumento sia pubblicitario che informativo.
Tra i vari servizi che oggi sono già realizzati per internet spiccano sicuramente: ecommerce, e-health, e-banking e servizi di prenotazioni.
In questo elaborato si cercare di spiegare innanzitutto le tecnologie sia hardware che
software, che interessano il digitale terrestre, compresi i vari standard che entrano in
gioco, continuando poi con la spiegazione di come realizzare un ambiente di sviluppo
per applicazioni MHP e infine descrivendo l’implementazione di una piattaforma per
prenotazioni “on-line” con particolare riguardo all’uso del canale di ritorno e l’accesso
ai dati e le varie problematiche che ne conseguono.
2
Capitolo 1: Il Digitale Terrestre
Capitolo 1
Il Digitale Terrestre
1.1 Introduzione
La televisione digitale terrestre (DTT: Digital Terrestrial Television) è l'ultima
tecnologia di comunicazione digitale che sta bussando alla porta delle case degli italiani,
e certamente risulta essere piuttosto apprezzata, a giudicare dai numeri.
Nel volgere di poco più di due anni e mezzo, dall'inizio del 2004, circa 2,5 milioni di
decoder DTT (i cosiddetti Set-Top-Box) hanno trovato posto nei nostri salotti, aiutati
indubbiamente anche dall’iniziale contributo per l'acquisto stanziato dal governo, di 70
euro per famiglia. La trasformazione della TV da analogica a digitale nel nostro Paese
coinvolgerà progressivamente gli oltre 50 milioni di apparecchi televisivi presenti sul
territorio. La fine delle trasmissioni TV terrestri in tecnica analogica (switch-off) è
prevista normativamente entro il 31 dicembre 2008: in base alla legge n. 66/2001 a
partire da tale data tutte le trasmissioni TV terrestri dovranno essere solo digitali. In
questa fase quindi tutti i broadcaster, nazionali e regionali, stanno affrontando i
problemi collegati alla transizione.
1.2 Novità tecnologiche
La novità tecnologica della DTT risiede principalmente in due elementi:
l'adozione di un sistema di trasmissione numerico COFDM, che presenta caratteristiche
di robustezza rispetto alle interferenze, migliore qualità del segnale trasportato e,
soprattutto, minor banda occupata nello spettro elettromagnetico, da cui l’attesa
moltiplicazione dei canali disponibili; la contemporanea presenza di un canale di ritorno
(CdR) che, collegando il ricevitore ad una rete di telecomunicazioni, fornisce al
telespettatore uno strumento per interagire a ritroso con l’emittente televisiva o con il
fornitore del servizio.
La modulazione usata dalla DTT a parte l'aggiunta di specifici codici per la correzione
dell'errore, è affine a quella usata per le connessioni ADSL. Essa lavora sul principio di
3
Capitolo 1: Il Digitale Terrestre
utilizzare diverse frequenze portanti fra loro ortogonali, fino ad un massimo di 8k
frequenze, e poi suddividere l'informazione in altrettanti flussi di bit indipendenti; ogni
flusso di bit viene assegnato ad una diversa portante e poi alla ricezione i diversi flussi
vengono opportunamente ricombinati.
Tale modulazione, quando opportunamente utilizzata, presenta una caratteristica
interessante: permette di sfruttare in modo costruttivo gli echi isofrequenziali che
giungono all'antenna ricevente del televisore. Ciò porta almeno un paio di vantaggi
rispetto al mondo analogico. Da un lato facilita una migliore ricezione all'interno delle
aree urbane, dove il segnale elettromagnetico può facilmente arrivare all'antenna in
modo multiplo, per via delle riflessioni che subisce incontrando i vari edifici (e che nel
mondo della TV analogica produrrebbero immagini ghost), dall'altro abilita una più
semplice costruzione dell'intera rete broadcast, permettendone una organizzazione in
isole isofrequenziali, fino ad una dimensione massima che dipende dal rapporto fra
tempo di trasmissione e tempo di silenzio ("di guardia" in termini tecnici) che è stato
scelto da ogni broadcaster per i trasmettitori della propria rete.
La ricezione del segnale televisivo può avvenire attraverso lo stesso impianto d’antenna
utilizzato già oggi per la TV analogica, a patto che l'impianto stesso sia stato effettuato a
regola d'arte, rispettando i requisiti tecnici di base, com’è normale se ci si è affidati a
professionisti del settore per la sua realizzazione. Il canale di ritorno utilizzerà un
collegamento fornito dalla rete di telecomunicazioni, e potrà operare sia con tecnologia
tradizionale (RTG: Rete Telefonica Generale) sia con tecnologie a larga banda (ADSL)
o wireless (GPRS/UMTS). Grazie al canale di ritorno ed alle funzioni che permettono
interattività locale sarà possibile progettare e costruire una grande varietà di nuovi
servizi, che vanno dal settore dell'intrattenimento a quello informativo, per arrivare a
servizi di utilità forniti da soggetti pubblici (es. il Comune di residenza) o privati (es: la
propria banca).
Il segnale audiovisivo della DTT è codificato in digitale secondo lo standard MPEG-2,
lo stesso adottato da oltre un decennio per la TV digitale satellitare. La modulazione di
tale segnale sulla specifica frequenza portante, fatto secondo lo standard DVB-T,
permette di trasportare un flusso numerico di circa 24 Mbit/s su ogni frequenza,
consentendo così di trasmettere fino a 4 o 5 canali TV, con qualità audiovisiva
paragonabile a quella di un DVD, occupando la stessa banda di frequenza attualmente
4
Capitolo 1: Il Digitale Terrestre
utilizzata da un singolo canale analogico. In questa prima fase di diffusione del servizio,
e nell'attesa che si diffondano sul mercato televisori già naturalmente predisposti per
vedere le trasmissioni digitali, per ricevere il segnale digitale sarà necessario collegare il
normale apparecchio televisivo che popola i salotti degli italiani con un ricevitore
digitale, il cosiddetto Set-Top Box (STB), che realizza le funzioni di ricevitore/decoder
per il segnale della TV digitale terrestre, ed utilizza la televisione per visualizzare le
immagini e diffondere i suoni. Il collegamento fra Set-Top Box e televisore si può
realizzare tramite la consueta presa SCART. Come già accennato sopra, in aggiunta al
consueto contenuto audiovisivo, il segnale digitale può agevolmente trasportare, sempre
all'interno dello stesso flusso digitale, altri tipi di dati, associati o meno al canale che si
sta trasmettendo. Il Set-Top Box diventa così un vero e proprio ambiente di esecuzione
per applicazioni, in grado di fornire i diversi servizi aggiuntivi, grazie al dialogo
realizzato con telecomando e Canale di Ritorno. La componente interattiva della TV
digitale è stata standardizzata a livello europeo attraverso la specifica DVB-MHP
(Digital Video Broadcasting - Multimedia Home Platform), che definisce la piattaforma
standard per la distribuzione ed esecuzione di applicazioni televisive su STB, nonché le
specifiche per il loro sviluppo. Un STB MHP è un sistema basato su una Java Virtual
Machine che integra le funzionalità di controllo dei flussi audio/video necessarie per
operare nell'ambiente televisivo, mentre le applicazioni non sono altro che dei
programmi scritti in Java che utilizzano le risorse hardware e software presenti nel STB.
La combinazione di STB, piattaforma MHP e canale di ritorno, che sarà presente in
prospettiva in tutte le case degli italiani, è il fattore abilitante che favorirà lo sviluppo di
una molteplicità di servizi informativi, di pubblica utilità e commerciali. In questo
contesto la DTT si va ad inserire come essenziale tecnologia abilitante nel processo di
ammodernamento delle infrastrutture telematiche del paese, con l'obiettivo dichiarato di
diventare uno degli elementi chiave nel processo di riduzione dell'analfabetismo digitale
della popolazione italiana. E’ importante capire cosa possa essere e come si possa
costruire il nuovo fenomeno dell'interattività televisiva, i ruoli dei diversi soggetti che
opereranno nel ciclo di vita del prodotto digitale e come si struttureranno i rapporti tra i
vari attori che si inseriscono in tale ciclo; attori che possono essere tradizionali, come le
TV che conosciamo, oppure del tutto nuovi, come i fornitori di servizi interattivi, la loro
natura, il loro ciclo di sviluppo, messa in onda ed esercizio.
5
Capitolo 1: Il Digitale Terrestre
L'evoluzione prossima della DTT sarà quella dell'arrivo di STB con hard disk integrato
e collegata capacità di registrazione digitale, il cosiddetto PVR (Personal Video
Recording). Alcuni produttori hanno già pronto un tale oggetto, ma appaiono ancora
cauti sull’opportunità di passare alla fase commerciale vera e propria.
Passare dalla registrazione su cassetta a quella su hard-disk, oltre a costituire un vero
grande salto nelle abitudini quotidiane dei telespettatori, introduce, infatti, alcuni effetti
collaterali rispetto ai quali il mondo della televisione si sta ancora interrogando. Primo
fra tutti l'insorgere di un’abitudine diffusa nel fruire della cosiddetta "time-shifted TV".
Quanti di noi normalmente non riescono ad accendere la televisione nel momento giusto
per vedere un film, un telegiornale, uno show dall'inizio della programmazione? Un
videoregistratore digitale è l'oggetto ideale per registrare la trasmissione dal suo inizio, e
renderla visibile in un qualunque momento, anche mentre è ancora in corso. Ed a quel
punto posso anche "accelerare" la fruizione della parte già registrata, usando i comandi
di "avanti veloce" così come avviene su un registratore VHS. Immaginate cosa potrebbe
capitare agli spot pubblicitari?
Negli USA, dove i PVR cominciano ad essere diffusi sulle TV via cavo e via satellite, i
pubblicitari hanno dichiarato che quando tali dispositivi avranno conquistato la soglia di
30 milioni di case, gli investimenti pubblicitari fatti nelle TV si ridurranno di almeno il
20%. Conciliare evoluzione tecnologica e modelli di business è il prossimo importante
passo che la TV digitale deve affrontare, e non solo in Italia.
Fig. 1.2.1 – Digital Video Broadcasting Terrestre nel mondo
6
Capitolo 1: Il Digitale Terrestre
1.3 Lo standard MHP
MHP è uno standard molto giovane, la prima release è stata creata dal DVB Project e
standardizzata dall'ETSI Institute nell'anno 2000. Multimedia Home Platform definisce
l'interfaccia tra le applicazioni interattive e i terminali sulle quali queste possono essere
eseguite (Set-Top Box, digital TV e PC multimediali). L'architettura (come mostrato in
figura 2) è definita dai seguenti 3 layers:
Resources: MPEG processing, I/O devices,CPU, memoria e graphics system.
System Software: JVM, APIs, transport protocols e soprattutto l'Application
Manager (o navigatore) che permette di gestire il running delle applicazioni Java.
Applications: le applicazioni interattive come ad esempio Electronic Program Guide,
servizi informativi, giochi, TV-Commerce etc.
Fig. 1.3.1 - Architettura dell'MHP
Come già preannunciato il "core" di MHP si basa su Java e in particolare sull'adozione
di una Personal JVM e di una serie di API, tra cui spiccano le Java TV di Sun.
All'interno dello standard troviamo anche la definizione di 3 profili, utili ad evidenziare
i servizi possibili con le relative tecnologie. Esiste inoltre un legame univoco tra i profili
(Figura 1.3.2) e le release dello standard:
7
Capitolo 1: Il Digitale Terrestre
Enhanced Broadcast Profile è definito nelle specifiche MHP 1.0, è il profilo base e
permette solamente l'arricchimento del contenuto audio-video con informazioni e
immagini visualizzabili e navigabili sullo schermo tv. Per questo profilo non sono
richieste performance particolari dei set-top box.
Interactive TV Profile è definito sempre nelle specifiche MHP 1.0 ed è il profilo
intermedio che permette di utilizzare il canale di ritorno (sia di tipo modem, adsl, gprs,
ethernet, etc) per fornire servizi con interattività superiore rispetto al profilo base.
Questo profilo, infatti, supporta anche il caricamento di applicazioni MHP tramite il
canale di ritorno e non solo dal canale di broadcast.
Internet Access Profile è definito nelle specifiche MHP 1.1 e permette tramite il canale
di ritorno di accedere ai contenuti di Internet. Questo profilo necessita di performance di
alto livello essendo obbligatoria l'adozione di un browser internet e di un client di email
embedded nel set-top box.
Fig. 1.3.2 - Profili dell'MHP
8
Capitolo 1: Il Digitale Terrestre
Alcuni esempi di applicazioni interattive possibili in MHP sono:
Electronic Program Guide(EPG).
Servizi di news come ad esempio un Telegiornale Interattivo.
Un gioco tv.
Giochi stand-alone.
Servizi interattivi legati ad eventi sportivi.
TV Commerce e TV Banking.
Servizi Meteo, Traffico e di pubblica utilità.
Pubblicità interattive.
Fig. 1.3.3 - Esempi di applicazioni MHP
9
Capitolo 1: Il Digitale Terrestre
1.4 Broadcasting di applicazioni interattive
Dopo aver analizzato lo standard, le tecnologie coinvolte e fornito qualche esempio di
cosa è possibile realizzare, vediamo in che modo e con quali strumenti le applicazioni
Java-MHP possono essere trasmesse e ricevute dai decoder interattivi. Iniziamo
evidenziando uno dei già citati vantaggi della trasmissione in digitale, la maggior
disponibilità di canali televisivi, analizzando il caso del digitale terrestre. Nel sistema di
trasmissione analogico(PAL) il segnale occupa una banda di 8MHz, modulando il
segnale in digitale, nella stessa banda è possibile avere un flusso dati di 24Mbit/sec.
Considerando che un canale tv digitale grazie alla compressione A/V MPEG2
mediamente occupa 4Mbit/sec risulta evidente come sia possibile trasmettere un
maggior numero di canali televisivi. Ovviamente se volessimo trasmettere anche
applicazioni interattive dovremmo riservare parte della banda disponibile anche per
questo tipo di dati; mediamente il peso di un applicazione comprensiva di grafica si
aggira sui 500Kbyte. Lo schema generale di figura 1.4.1 ci mostra un esempio relativo
all'emissione e alla ricezione del segnale digitale comprensivo delle applicazioni
interattive. Nello standard MHP tali applicazioni sono chiamate Xlet e possono essere
composte solo da tipologie precise di dati: classi java, file xml o txt, immagini png o jpg
o gif, audio mp2 e video mpg. Le Xlet per essere broadcastate necessitano di un
preventivo impacchettamento in moduli di dimensione massima di 64Kbyte. La
sequenza di moduli che compone ogni applicazione viene tramessa in loop
costantemente e di solito viene associata ad un particolare canale tv. Tale associazione,
come evidenziato nello schema, avviene nel Multiplexer dove è possibile creare tali
associazioni per ogni Xlet broadcastata. A questo punto il flusso in uscita dal
Multiplexer (Trasport Stream) viene modulato in una delle 3 tipologie possibili definite
da Digital Video Broadcasting: Terrestre (specifiche DVB-T), Satellite (specifiche
DVB-S) e Cavo (specifiche DVB-C). Dopo la modulazione il segnale viene trasmesso e
quindi ricevuto sul decoder interattivo (set-top box) che come vedremo offre la
possibilità di connessioni a server dedicati o ad internet tramite modem o tecnologie più
avanzate come l'ADSL.
10
Capitolo 1: Il Digitale Terrestre
Fig. 1.4.1 - Emissione e ricezione del segnale digitale
1.5 Il decoder interattivo
Il decoder interattivo(Set-Top Box) è l'apparecchio indispensabile per ricevere i canali
digitali e utilizzare i software interattivi. Questo dispositivo permette innanzitutto la
decodifica del segnale da digitale ad analogico per gli apparecchi televisivi analogici
che attualmente nel nostro paese rappresentano la totalità del mercato. Infine consente
di eseguire applicazioni Java grazie alla presenza di una Personal JVM embedded.
Come mostra la Figura 1.5.1 un decoder è formato da una parte hardware(CPU,
memoria, modem, etc), da un sistema operativo real-time e da un middleware MHP.
Fig. 1.5.1 - Schema HW/SW di un Set-Top Box
Un attore importante che troviamo all'interno del decoder è il cosiddetto navigatore(o
application manager). Il suo compito è fondamentale, infatti esso si occupa di segnalare
11
Capitolo 1: Il Digitale Terrestre
all'utente la lista delle applicazioni MHP disponibili sul canale televisivo. Inoltre se
l'utente effettua una selezione da tale lista, il navigatore inizia la fase di download in
memoria dei moduli relativi all'applicazione scelta. Importante sottolineare che non tutti
i moduli che compongono l'applicazione verranno scaricati, infatti il download sarà
relativo solo a quelli neccessari alla partenza della Xlet. Successivamente e solo se
realmente necessari verranno scaricati anche quelli rimanenti. Tale meccanismo ricorda
il modello di caricamento delle pagine del televideo ed è pensato allo scopo di ridurre i
tempi di attesa del telespettatore. Un dispositivo tecnologico presente sul decoder che
rende operativo il concetto di canale di ritorno definito nello standard è il modem nei
decoder di fascia bassa e l'ADSL, Gprs, Ethernet, etc. sui decoder di fascia alta. Il
canale di ritorno permette di interagire con servizi esterni, su server dedicati o internet e
scaricare contenuti su richiesta del telespettatore. Nel caso di modem V90 i tempi di
connessione e recupero dati sono abbastanza lunghi(30-40 sec solo per la chiamata
telefonica e l'autenticazione presso un ISP), ma nel caso di connessioni "always on"
come con ADSL allora gli scenari possibili sono molto più interessanti, come la
possibilità di scaricare musica e video su richiesta dell'utente.
12
Capitolo 2: Applicazioni MHP
Capitolo 2
Applicazioni MHP
2.1 Modello di Business e interfacce
Un’applicazione mhp viene sviluppata seguendo un modello di business che si basa su
più fattori:
- MSO (Multiple Service Operator) i broadcaster, cioè coloro che forniscono la
possibilità di scaricare le applicazioni e i contenuti per la iTV
- MSV (Middleware Software Vendor) gli sviluppatori java/DVB-HTML
- I fornitori dei contenuti ISP (Internet Service Provider)
- Set-top-box Manufacturers
- Users: gli utenti dotati di set-top-box, televisore e telecomando
Date l’innumerevoli combinazioni che possono venire dall’incrocio dei diversi fattori
elencati, non poche possono sembrare le difficoltà nel produrre un’applicazione MHP.
Il trucco si trova nelle interfacce che lo standard MHP mette a disposizione sia a livello
software (figura 2.1.1) che hardware (figura 2.1.2).
MHP è basato su java, (platform indipendent), il set-top-box ha preinstallato una VM
java corredata da API proprietarie e TV API di SUN, che definiscono le modalità di
comunicazione tra l'utente, il set-top-box e il broadcaster.
MHP ha definito anche un sotto linguaggio HTML (DVB-HTML per la precisione) con
tag supplementari relativi alla cattura dell'input dell'utente (telecomanado).
Il set-top-box ha preinstallato un'applicazione che si chiama Application Manager (di
solito java) che gestisce il caricamento del software creando anche i menu dei canali
forniti dal broadcaster.
Oltre a ciò il set-top-box possiede anche un alloggiamento per smartcard (nonché un
lettore per le stesse), che offre servizi sicuri come login utente, servizi a pagamento
ecc..
13
Capitolo 2: Applicazioni MHP
Fig. 2.1.1 – Software layer
Fig. 2.1.2 - Interfaccia tra mhp application e mhp system
Per ogni tipo di interfaccia si avrà una suite di classi che permetterà di far dialogare
l’applicazione con i dispositivi a disposizione: schermo, telecomando, smart card,
canale di ritorno.
14
Capitolo 2: Applicazioni MHP
2.2 Xlet
Una Xlet è una particolare applicazione Java concepita per essere scaricata ed eseguita
su decoder interattivi nei quali un software specifico, l'Application Manager, è in grado
di controllarne il ciclo di vita. Come vedremo le similitudini con le Applet, dove
l'application manager è rappresentato dal browser sono molteplici.
Per iniziare a costruire una Xlet occorre innanzitutto capire cosa viene fornito da Sun
nelle API Java TV. Infatti è proprio in queste librerie che vengono definite le due
interfacce fondamentali da utilizzare:
•
javax.tv.xlet.Xlet che definisce i seguenti metodi:
public void initXlet(XletContext ctx);
public void startXlet();
public void pauseXlet();
public void destroyXlet(boolean unconditional);
•
javax.tv.xlet.XletContext che definisce i seguenti metodi:
public void notifyDestroyed();
public void notifyPaused();
public java.lang.Object getXletProperty(java.lang.String key);
public void resumeRequest();
Entrambe le interfacce servono per interagire con l'application manager in merito al
ciclo di vita e al contesto in cui la Xlet viene eseguita.
La classe principale di ogni Xlet deve sempre implementare javax.tv.xlet.Xlet per poter
essere eseguita sui decoder interattivi MHP.
Come si mostra in Figura 2.2.1 il ciclo di vita di una Xlet è caratterizzato da 4 stati:
Loaded: in questo stato la Xlet è stata creata ma non inizializzata, se durante questa
fase viene rilevata un eccezione allora si passa direttamente nello stato Destroyed. Da
notare che la Xlet si può trovare in questo stato solo una volta durante tutto il suo ciclo
di vita.
Paused: la Xlet è inizializzata e può trovarsi in questo stato sia dopo che il metodo
initXlet(XletContext) è ritornato con successo dallo stato Loaded oppure dopo che il
metodo pauseXlet() è ritornato con successo dallo stato Active. In entrambi i casi in
questo stato la Xlet dovrebbe limitare al massimo l'utilizzo delle risorse condivise e
soprattutto non impegnare la GUI televisiva.
Active: in questo stato la Xlet è attiva e utilizza le risorse necessarie per fornire i suoi
servizi, se dotata di GUI allora dovrebbe essere l'unica registrata per ricevere gli eventi
15
Capitolo 2: Applicazioni MHP
del telecomando.
Destroyed: in questo stato la Xlet deve rilasciare tutte le risorse in uso per predisporsi
alla terminazione.
Fig. 2.2.1 - Ciclo di vita di una Xlet
Una sequenza tipica di questo ciclo potrebbe essere:
L' application manager crea una nuova istanza di una Xlet chiamando il suo costruttore
(stato LOADED).
La
Xlet
usa
il
context
passatogli
dall'application
manager
nel
metodo
initXlet(XletContext) e si inizializza (stato PAUSED).
L'application manager chiama il metodo startXlet() e la Xlet inizia ad utilizzare le
risorse necessarie per fornire i servizi per cui è stata costruita(stato ACTIVE).
L'application manager chiama il metodo destroyXlet(true) e la Xlet rilascia le risorse,
dopodiché viene terminata (stato DESTROYED).
Il primo esempio: HelloXlet.java
import javax.tv.xlet.Xlet;
import javax.tv.xlet.XletContext;
import javax.tv.xlet.XletStateChangeException;
public class HelloXlet implements Xlet{
// L'XletContext è un oggetto usato dalla Xlet per accedere
// alle properties del suo environment e per comunicare con
// l'application manager che glielo ha passato nella fase
// di inizializzazione.
private XletContext context;
private boolean alreadyActive = false;
16
Capitolo 2: Applicazioni MHP
// Il costruttore tipicamente viene lasciato vuoto visto che tutte
// le inizializzazioni dovrebbero stare nel metodo initXlet()
public HelloXlet(){}
// In questo metodo vengono fatte tutte le inizializzazioni,
// se in questa fase qualcosa fallisse verrebbe lanciata un
// eccezione.
public void initXlet(XletContext context) throws XletStateChangeException{
// si memorizza il context passato dall'Application Manager
this.context = context;
System.out.println(this.getClass().getName()+" : Inizializzazione
avvenuta!");
}
// Con questo metodo la Xlet è avviata e vengono richieste
// le risorse necessarie come ad esempio lo schermo TV o
// gli eventi del telecomando
public void startXlet() throws XletStateChangeException{
// controlliamo se la Xlet era già stata messa in stato Active
// precedentemente
if (alreadyActive){
System.out.println(this.getClass().getName()+" : Hello Again
TV World! ");
}else {
System.out.println(this.getClass().getName()+" : Hello TV
World");
alreadyActive = true;
}
}
// Con questo metodo la Xlet viene messa in pausa e dovrebbe
// rilasciare tutte le risorse utilizzate
public void pauseXlet(){
System.out.println(this.getClass().getName()+" : Xlet in
pausa!");
// notifichiamo al Context l'avvenuta pausa della Xlet
context.notifyPaused();
}
// Con questo metodo la Xlet viene terminata, tramite il
// parametro boolean la Xlet ha la facoltà di accettare
// tale richiesta; nel caso non accettasse di
// terminare dovrebbe lanciare un eccezione.
public void destroyXlet(boolean unconditional) throws XletStateChangeException {
if (unconditional) {
System.out.println(this.getClass().getName()+" : la Xlet è
stata terminata!");
}else {
System.out.println(this.getClass().getName()+
" : la Xlet ha accettato di essere terminata!");
}
// notifichiamo al Context l'avvenuta terminazione della Xlet
context.notifyDestroyed();
}
}
17
Capitolo 2: Applicazioni MHP
2.3 Gestione delle risorse scarse
I Set Top Box sono sistemi che presentano risorse limitate a livello hardware come
poca memoria e processori “lenti”, rispetto agli attuali PC in commercio, quindi per
poter utilizzare le varie periferiche: input (telecomando), output (decoder Mpeg2 per
visualizzare le immagini su video) e modem si devono usare delle accortezze, le
precedenti periferiche vengono chiamate “risorse scarse”.
Se le risorse non vengono gestite in maniera ottimale potrebbe succedere che
un'applicazione non sia in grado di funzionare correttamente o non sia in grado di
sfruttare tutte le potenzialità del STB. Le API che andremo a considerare sono le
resource notification API che sono state definite da DAVIC nel package
org.davic.resource . Una cosa da tenere in considerazione è che queste non sono
API di gestione delle risorse, in quanto questa viene lasciata al middleware del
ricevitore ma ben si di notifica. Servono quindi a notificare all'applicazione se una
risorsa è stata tolta, oppure se è richiesta da un'altra applicazione concorrente.
Le resource notification API sono sostanzialmente composte da tre classi:
•
ResourceServer
•
ResourceClient
•
ResourceProxy
Tali classi non hanno funzionalità autonoma ma forniscono dei concetti comuni ai quali
le API che definiremo di “ utilizzo” dovranno attenersi per svolgere i loro compiti.
Questo significa che essendo queste delle
“interface” saranno le classi che le
implementano a definirne nello specifico i loro compiti.
Queste API si basano sul modello client-server con piccole variazioni.
L'interfaccia ResurceServer è implementata da quelle API di utilizzo responsabili di
effettuare la richiesta di risorse scarse, e di gestirne la loro allocazione. Tali risorse
scarse potrebbero essere di tipo software, ad esempio il section filter, oppure di tipo
hardware come per esempio un modem oppure un decoder MPEG, le API non fanno
distinzione e le trattano tutte in maniera simile.
Il codice sorgente dell'interfaccia ResurceServer è il seguente:
18
Capitolo 2: Applicazioni MHP
public interface ResourceServer
{
public abstract void addResourceStatusEventListener(
ResourceStatusListener listener);
public void removeResourceStatusEventListener(
ResourceStatusListener listener);
}
Come si può vedere questa interfaccia non definisce dei metodi specifici per attuare una
richiesta di accesso esclusivo ad una risorsa scarsa oppure per rilasciarla, questa è una
scelta voluta perché sarà compito delle API di utilizzo fare questo nella maniera più
consona. Gli unici metodi che questa interfaccia definisce permettono di registrare un
ascoltatore degli eventi che riporterà i cambiamenti di stato della nostra risorsa.
Il modo con cui noi riserviamo una risorsa come il modem è infatti completamente
differente da quello con cui gestiamo il un decodificatore MPEG per esempio.
Vedremo in seguito come la classe ConnectionRCInterface attraverso il metodo
reserve() andrà a richiedere l'accesso esclusivo sul modem.
L'interfaccia ResurceClient è come si può capire dal nome il lato client delle API.
Questa interfaccia viene implementata da quelle classi che richiedono l'accesso alla
risorsa scarsa, e viene utilizzata dal middleware per richiedere il rilascio della risorsa
oppure per informarla che il rilascio è già avvenuto.
Il codice sorgente è il seguente:
public interface ResourceClient
{
public abstract boolean requestRelease(
ResourceProxy proxy,
Object requestData);
public abstract void release(
ResourceProxy proxy);
public abstract void notifyRelease(
ResourceProxy proxy);
}
19
Capitolo 2: Applicazioni MHP
Come possiamo vedere questa interfaccia mette a disposizione del middleware tre
metodi per comunicare all'applicazione di rilasciare la risorsa.
Il metodo requestRelease() è il meno incisivo dei tre perché lascia al client di
scegliere se soddisfare la richiesta oppure rifiutare. Nel caso in cui il client abbia ancora
bisogno di trattenere la risorsa ritornerà false e il middleware dovrà se necessario
attuare un sistema di rilascio forzato. Altrimenti nel caso in cui la risorsa non è più
necessaria per il client esso riporterà true ed il middleware potrà procedere con il
rilascio.
Il metodo release() è il più aggressivo perché obbliga al nostro client di rilasciare la
risorsa senza scelta. Quando il metodo ritorna, il middleware sarà certo di poter fornire
la risorsa ad un altro client che ne fa richiesta. Se il metodo release() non ritorna in
tempo accettabile il middleware può ritenere che l'applicazione sia andata in crash e
metterà comunque a disposizione la risorsa rimuovendone i diritti di utilizzo. Se
succede questo il middleware chiamerà il metodo notifyRelease() dell'interfaccia
ResourceClient, andando a informare l'applicazione che è stata privata della risorsa
scarsa e che dovrà quindi agire terminando i processi che la impiegavano. Questa è una
operazione brutale per il client ma viene utilizzata quando questo non collabora con il
middleware.
L’interfaccia ResourceProxy mette a disposizione solo un metodo di notifica utilizzato
per restituire il client della risorsa a cui esso fa riferimento ed è composta come di
seguito:
public interface ResourceProxy {
public ResourceClient getClient();
}
La classe che implementa ResourceProxy si pone nel mezzo tra il client e la risorsa da
utilizzare questo porta ad un grande livello di sicurezza perché l’applicazione ottiene un
accesso mediato alla risorsa e ciò per permettere al middleware di controllarla.
Le classi che implementano il proxy mettono a disposizione dei metodi che possono
essere utilizzati per fare il set-up delle risorse. In questo modo quando si attuano più
cicli di request/release delle risorse non si dovrà tutte le volte settare i parametri ma
20
Capitolo 2: Applicazioni MHP
questi verranno forniti dal proxy precedentemente impostato. Con una risorsa come il
modem, dove ogni volta, per effettuare una connessione, bisogna fornire numero
telefonico, username e password questo comporta un elevato risparmio di tempo.
Un altro beneficio portato è che le classi che implementano il client possono allacciarsi
a più proxy differenti, ognuno dei quali capace di fornire un setting differente per i
parametri della risorsa, sarà poi l’applicazione a decidere quali proxy utilizzare nel
momento in cui ne fa richiesta.
Vediamo di capire meglio cosa succede quando un’applicazione richiede l’accesso a
una risorsa seguendo i passi di seguito illustrati:
1. Innanzi tutto l’applicazione, o meglio le classi che implementano il client
creano un’istanza del ResourceProxy appropriato e settano tutti i parametri
del caso in base alla risorsa che andranno a utilizzare:
2. Il client va a chiamare un metodo appropriato sulle API che implementano il
ResourceServer
e
richiedono
accesso
alla
risorsa
passando
il
ResourceProxy come parametro:
3. Se l’accesso alla risorsa è garantito il server chiama un metodo privato sul
proxy per convalidarlo:
21
Capitolo 2: Applicazioni MHP
4. Si stabilisce un collegamento diretto tra il proxy e la risorsa, collegamento
che fino a questo momento non esisteva per ragioni di sicurezza:
5. A questo punto la nostra applicazione può cominciare ad utilizzare la risorsa
utilizzando i metodi del proxy,sarà poi questo a riportare le richieste alla
risorsa:
Il processo di rilascio delle risorse è sostanzialmente l’inverso di quello
precedentemente illustrato:
1. Quando il client ha terminato di utilizzare una risorsa chiama un metodo
appropriato sul server per rilasciare la risorsa passandogli il proxy come
parametro:
22
Capitolo 2: Applicazioni MHP
2. Ora il server chiamerà un metodo appropriato sul proxy per invitarlo a rilasciare
la risorsa a cui fa riferimento invalidandolo:
3. Nel caso in cui l’applicazione è cooperante il proxy procede con il rilascio della
risorsa:
Vediamo cosa succede quando un’applicazione “maliziosa” non vuole rilasciare la
risorsa, ed interviene il middleware:
1. il server chiama il metodo release() sul client;
2. il client fallisce nel ritorno da questa chiamata e continua a trattenere la
risorsa;
3. dopo un certo periodo di tempo il middleware notifica al server che la
richiesta non ha avuto un responso;
23
Capitolo 2: Applicazioni MHP
4. il server chiama un metodo privato sul proxy per informarlo che non e più
valido e che non può più trattenere la risorsa;
5. il proxy rompe il collegamento con la risorsa;
6. il middleware chiama allora il metodo notifyRelease() su quella classe
che
implementa
l’interfaccia
ResourceClient.
In
questo
modo
l’applicazione sa che non ha più accesso alla risorsa e che si dovrà
comportare di conseguenza;
7. tutti i futuri tentativi di accesso alla risorsa da parte dell’applicazione non
avranno alcun effetto.
L’aver descritto in maniera cosi approfondita, come avviene la gestione delle risorse e
quali sono le API Java che ci permettono di fornire supporto per la notifica di gestione
da parte del middleware, ci da la possibilità in futuro di introdurre i package che
contengono le classi da utilizzare per stabilire una connessione, impostarne i parametri,
settare un background, spostare e/o ridimensionare il video che come vedremo
implementano le classi del package org.davic.resource.
24
Capitolo 2: Applicazioni MHP
2.4 Modello grafico
Il modello grafico definito dallo standard MHP è basato su tre differenti piani (o layers)
come mostrato in Figura 2.4.1.
Fig. 2.4.1 - Layer grafici dell'MHP
Partendo da dietro, rispetto a chi guarda la TV, troviamo il Background layer che può
contenere un solid color o una still image (rappresentata da un particolare frame MPEG2, quello di tipo I), avanti troviamo il Video layer rappresentato dal flusso A/V del
canale TV o di una qualsiasi altra fonte in formato MPEG-2.
L’ultimo livello è il Graphics layer che può contenere la grafica creata nella Xlet, che a
sua volta può essere strutturata su n-livelli sovrapposti.
Tutti e tre i livelli lavorano ad una risoluzione standard di 720x576 o 768x576 se viene
impostato il modo di visualizzazione su schermo 4:3.
Per chi è abituato a fare applicazioni grafiche su PC non ci sono molte differenze, ma è
il caso di tenere in considerazione alcuni aspetti:
•
i pixel non sono perfettamente quadrati;
•
il file che costituisce il background deve essere un’immagine video in formato
mpeg2 (.mpg) in loop e non un immagine statica (.jpg), dato che il STB usa per
la sua visualizzazione lo stesso decoder del video layer. (Programma di
conversione consigliato .jpg -> .mpg “ImageMagic”);
25
Capitolo 2: Applicazioni MHP
•
le linee spesse 1 pixel non vengono visualizzate perfettamente o addirittura non
sono visibili (consigliato spessore >=2);
•
le coordinate assolute (x,y) partono da 0,0 angolo superiore sinistro;
•
possono essere usate coordinate indipendenti dalla risoluzione chiamate
“normalizzate” e si esprimo con numeri decimali da 0.0 a 1.0 supportate nelle
API Havi;
•
nella gestione del livello grafico è assicurata la visualizzazione completa in un
area ridotta rispetto alle coordinate totali, detta SAFE AREA di ampiezza
562x460.
0,0
79,58
Safe Area
641,518
720,576
Dopo aver visto le accortezze da usare andiamo ad analizzare in dettaglio, con porzione
di codice commentato, i tre livelli di grafica.
2.4.1 Background layer
Per settare uno sfondo sulla nostra applicazione bisognerà innanzitutto tener conto che
la periferica che andremo ad utilizzare, il decoder mpeg2 è una risorsa scarsa, quindi per
effettuare qualsiasi tipo di settaggio bisognerà prima acquisirla usando le resource
notification API introdotte nei paragrafi precedenti.
Il package più importante da importare per la gestione di questo layer è org.havi.ui.* ,
riportiamo la classe più rilevante “HScreen”:
26
Capitolo 2: Applicazioni MHP
Method Summary
HBackgroundConfiguration getBestConfiguration(HBackgroundConfigTempla
te[] hbcta)
Returns an HBackgroundConfiguration from an
HBackgroundDevice which is present on this HScreen
that best matches at least one of the specified
HBackgroundConfigTemplates, or null if this is not
possible.
HGraphicsConfiguration getBestConfiguration(HGraphicsConfigTemplate
[] hgcta)
Returns an HGraphicsConfiguration from an
HGraphicsDevice which is present on this HScreen
that best matches at least one of the specified
HGraphicsConfigTemplates.
HVideoConfiguration getBestConfiguration(HVideoConfigTemplate[])
Returns an HVideoConfiguration from an
HVideoDevice which is present on this HScreen that
best matches at least one of the specified
HVideoConfigTemplates.
HScreenConfiguration[] getCoherentScreenConfigurations(HScreenConfi
gTemplate[] hscta)
Return a coherent set of
HScreenConfigurations matching a set of templates.
HBackgroundDevice getDefaultHBackgroundDevice()
Return the default background device for this
screen.
HGraphicsDevice getDefaultHGraphicsDevice()
Return the default graphics device for this
screen.
static HScreen getDefaultHScreen()
Returns the default HScreen for this application.
HVideoDevice getDefaultHVideoDevice()
Return the default video device for this screen.
HBackgroundDevice[] getHBackgroundDevices()
Returns a list of background devices for this
screen.
HGraphicsDevice[] getHGraphicsDevices()
Returns a list of graphics devices for this screen.
static HScreen[] getHScreens()
Returns all HScreens in this system.
HVideoDevice[] getHVideoDevices()
Returns a list of video device for this screen.
27
Capitolo 2: Applicazioni MHP
Vediamo in breve le operazioni da eseguire: per prima cosa si sceglie lo schermo da
usare mediante l’oggetto HScreen, a questo oggetto bisogna associare una
HGraphicsDevice che conterrà il set d’impostazioni assegnate per mezzo di
HGraphicsConfigTemplate.
Il set di ogni singola impostazione è eseguito dal metodo setPreference al quale viene
passato il parametro e la priorità; sarà poi il midlleware a decidere se il settaggio
richiesto è attuabile in base alla priorità data e alle esigenze delle altre eventuali
applicazioni che sono in running.
Conviene
creare
una
classe
che
implementa
obbligatoriamente
org.davic.resources.ResourceClient dove inserire tutti i metodi per l’acquisizione, il
settaggio e il rilascio della risorsa.
Di seguito viene riportata la classe “BackgroundController”, creata da Steve Morris,
che mostra in dettaglio oltre alle fasi sopra elencate la gestione delle eccezioni .
/**********************************************************************************
* THIS SOFTWARE IS COPYRIGHT STEVEN MORRIS 2002. ALL RIGHTS RESERVED
* THIS SOFTWARE IS FREE FOR NON COMMERCIAL USE FOR THE PURPOSE OF LEARNING
*MHP. ANY USE FOR OTHER PURPOSES IS PROHIBITED UNLESS WRITTEN AGREEMENT
* IS OBTAINED.
* DISTRIBUTION OF THIS CODE IS PROHIBITED
*/
// Import the HAVi UI classes that we need for this
import org.havi.ui.HScreen;
import org.havi.ui.HBackgroundDevice;
import org.havi.ui.HBackgroundConfiguration;
import org.havi.ui.HBackgroundImage;
import org.havi.ui.HStillImageBackgroundConfiguration;
import org.havi.ui.HConfigurationException;
import org.havi.ui.HPermissionDeniedException;
import org.havi.ui.HBackgroundConfigTemplate;
// Since some of the graphics configuration requires scarce resources, we
// need to use the DAVIC resource notification API
import org.davic.resources.*;
// We need these (under some circumstances) to let our background be seen
import javax.tv.service.selection.*;
import javax.tv.media.AWTVideoSizeControl;
import javax.tv.media.AWTVideoSize;
import java.awt.Rectangle;
import javax.tv.xlet.XletContext;
28
Capitolo 2: Applicazioni MHP
/**
* This class handles the display of background images in an easy-to
* use way. Setting this up can be quite complex, and so in the case
* we've relegated it to a separate class to make it a little easier to
* understand.
*/
class BackgroundController implements org.davic.resources.ResourceClient{
// The background device that we will use for displaying the image
private HBackgroundDevice backdevice;
// The configuration for that background device
private HStillImageBackgroundConfiguration backconfig;
/**
* This method will initialise the video and graphics devices to allow
* them to display the background image.
*/
public boolean init(){
// We first need to get the background device that we will use for displaying the image. To do
//this, we need to first decide which
// HScreen we will use. In this case (and most others), we will use
// the default one.
HScreen screen = HScreen.getDefaultHScreen();
// Once we have that, we get the default background device for that HScreen
HBackgroundDevice backdevice = screen.getDefaultHBackgroundDevice();
// Once we have a reference to the device, we can get a configuration for it.
// Get a configuration that we can use
HBackgroundConfigTemplate backgroundConfigurationTemplate = new
HBackgroundConfigTemplate();
backgroundConfigurationTemplate.setPreference(HBackgroundConfigTemplate.FLICKER_FILTERING
,HBackgroundConfigTemplate.REQUIRED);
HBackgroundConfiguration backconfig;
backconfig = backdevice.getBestConfiguration(backgroundConfigurationTemplate);
// Can we reserve the device so that we can change the settings on it?
if (backdevice.reserveDevice(this)){
// If we can, we set the configuration of the background device to
// the one we got above - this is the default configuration for this device.
try{
backdevice.setBackgroundConfiguration(backconfig);
}catch (Exception ex){
System.out.println("Can't initialise the background device");
// Release the device so that other applications can use it, if necessary.
backdevice.releaseDevice();
return false;
}
// We need to check if we can put an image in the background in
// this configuration, since we can't do this in every configuration.
if(backconfig instanceof HStillImageBackgroundConfiguration){
// We can use this
this.backconfig = (HStillImageBackgroundConfiguration)backconfig;
this.backdevice = backdevice;
return true;
}else{
// If we can't, we again release the device since it's no use to us.
backdevice.releaseDevice();
}
}
return false;
}
29
Capitolo 2: Applicazioni MHP
/**
* Free the resources we needed to display background images.
* Some implementations leave the image there, but there is an explicit
* warning in the MHP specification that this may not happen on all
* implementations. If you want to be sure that your image is still
* visible, don't do this.
*/
public void dispose(){
// Check if we have something to dispose of
if (backdevice != null){
// RZlease the device and clear any references
backdevice.releaseDevice();
backdevice = null;
backconfig = null;
}
}
/**
* Display a background image
*/
public void display(String filename){
// Check we have the resources we need to display a background image
if(backconfig != null) {
// Create a new background image. The image is loaded from the
// filename that we pass in.
HBackgroundImage backimage = new HBackgroundImage(filename);
// Now display the image. This can throw several exceptions, so we
// enclose it in a 'try' block
try {
backconfig.displayImage(backimage);
}
catch (java.io.IOException ioe) {
// Ignore it, but print a message to tell the user what's happened.
System.out.println("Can't display background image - IO exception");
ioe.printStackTrace();
}
catch (HPermissionDeniedException hpde) {
// We don't have permission to displayan image. We just ignore it.
System.out.println("Can't display background image - permission denied");
}
catch (HConfigurationException hce) {
// We don't have permission to displayan image. We just ignore it.
System.out.println("Can't display background image - configuration exception");
hce.printStackTrace();
}
}
}
30
Capitolo 2: Applicazioni MHP
Implementazione metodi di org.davic.resource:
/**************************************************************************
*
* These methods are inherited from the ResourceClient interface and are used
* to tell the application when it has lost access to its resources (or
* when it is about to lose access to them). This gives the application a
* chance to clean up when it loses access to a resource, and gives it a
* chance to handle things gracefully.
*/
/**
* This method gets called when the resource manager requests that we give
* up a resource. We can refuse to do so, and that's what we do in this
* case (even though we shouldn't).
*/
public boolean requestRelease(ResourceProxy proxy, Object requestData) {
return false;
}
/**
* This method gets called when the resource manager informs us that we must
* release a resource
*/
public void release(ResourceProxy proxy) {
// release control of the background device
backdevice.releaseDevice();
}
/**
* This method gets called when the resource manager tells us we've
* lost access to a resource and we should clean up after ourselves.
*/
public void notifyRelease(ResourceProxy proxy) {
// release control of the background device. Even though we don't
// have control of it, this makes sure everything is synchronized.
backdevice.releaseDevice();
}
L’ultima cosa da aggiungere per quanto riguarda la gestione di questo livello è che, per
rendere visibile il background, bisognerà ridimensionare o nascondere del tutto il livello
superiore, ovvero il video layer.
Trascurando la gestione del primo livello si possono implementare interfacce usando il
livello grafico e come sfondo avranno la trasmissione corrente del canale dove si è
sintonizzati.
31
Capitolo 2: Applicazioni MHP
2.4.2 Video layer
La gestione del video layer è molto importante non solo per l’aspetto estetico della
nostra interfaccia grafica.
Non dobbiamo dimenticare, che il broadcaster ha si interessi a fornire l’applicazione
MHP, ma si deve preoccupare anche della trasmissione che sta andando in onda.
Mentre in una fase iniziale, nelle prime applicazioni, si preferiva ridimensionare il video
e spostarlo in modo da non sovrapporsi a menù, pulsanti etc. ora la tendenza e di
lasciarlo invariato il più possibile, usando trasparenze o sovrapposizioni di grafica
temporanee, in modo da distogliere il meno possibile l’attenzione del telespettatore
dalla trasmissione.
Comunque ci sono casi in cui l’applicazione può pretendere e merita tutta l’attenzione
dell’utente, basta pensare all’inserimento dati o alla conferma di un movimento in una
applicazione e-banking per esempio.
Quindi è bene alternare fasi, come la navigazioni di menù, dove il video sia in primo
piano e altre in cui venga eliminato o ridotto.
Per realizzare tutto ciò ci vengono in aiuto le classi javaTV della Sun, ecco un esempio
di due metodi per nascondere o ridimensionare e posizionare il video tratti dalla classe
“BackgroundController”, creata da Steve Morris.
import javax.tv.service.selection.*;
import javax.tv.media.AWTVideoSizeControl;
import javax.tv.media.AWTVideoSize;
import java.awt.Rectangle;
import javax.tv.xlet.XletContext;
/**
* Hide the video that may be obscuring our background
*/
public void hideVideo(XletContext context) {
// The background may be hidden by the video when the Xlet starts up. To
// solve this, we have to do two things:
// 1) stop the video
// 2) resize the video so that we can see the app underneath it. This is
// not always necessary, but some emulators (xletview, I'm looking at you)
// don't hide the video when you stop it
// Get a reference to the JavaTV ServiceContextFactory
ServiceContextFactory factory;
factory = ServiceContextFactory.getInstance();
32
Capitolo 2: Applicazioni MHP
// From this, we can get a reference to the parent
// service context of our Xlet. To do this, we need a
// reference to our Xlet context. It's times like this
// that show why your application should always keep a
// reference to its Xlet context
ServiceContext myContext = null;
try {
myContext = factory.getServiceContext(context);
}
catch (Exception e) {
e.printStackTrace();
}
if (myContext != null) {
// ServiceContentHandler objects are responsible for
// presenting the different parts of the service. This
// includes the media components
ServiceContentHandler[] handlers;
handlers = myContext.getServiceContentHandlers();
for(int i=0; i < handlers.length ; i++) {
if (handlers[i] instanceof ServiceMediaHandler) {
// This is a Player for part of the service, since
// ServiceMediaHandler objects are instances of JMF
// Player objects
javax.media.Player p = (javax.media.Player) handlers[i];
// this may be all we need to do in order to see our background...
p.stop();
p.deallocate();
// ...but some emulator require more
AWTVideoSizeControl awtVideoSizeControl;
awtVideoSizeControl = (AWTVideoSizeControl)
p.getControl("javax.tv.media.AWTVideoSizeControl");
// in this case, we set the size of the video to be 0, but we could set it
// to display on part of the screen
awtVideoSizeControl.setSize(new AWTVideoSize(new Rectangle(0, 0, 720, 576), new
Rectangle(0,0,0,0)));
}
}
}else {
System.out.println("Can't get our service context. May not be able to " +
"resize the video, so our background may not be visible");
}
}
public void resizeVideo(XletContext context,int x,int y,int l,int h) {
// The background may be hidden by the video when the Xlet starts up. To
// solve this, we have to do two things:
// 1) stop the video
// 2) resize the video so that we can see the app underneath it. This is
// not always necessary, but some emulators (xletview, I'm looking at you)
// don't hide the video when you stop it
// Get a reference to the JavaTV ServiceContextFactory
ServiceContextFactory factory;
factory = ServiceContextFactory.getInstance();
33
Capitolo 2: Applicazioni MHP
// From this, we can get a reference to the parent
// service context of our Xlet. To do this, we need a
// reference to our Xlet context. It's times like this
// that show why your application should always keep a
// reference to its Xlet context
ServiceContext myContext = null;
try {
myContext = factory.getServiceContext(context);
}
catch (Exception e) {
e.printStackTrace();
}
if (myContext != null) {
// ServiceContentHandler objects are responsible for
// presenting the different parts of the service. This
// includes the media components
ServiceContentHandler[] handlers;
handlers = myContext.getServiceContentHandlers();
for(int i=0; i < handlers.length ; i++) {
if (handlers[i] instanceof ServiceMediaHandler) {
// This is a Player for part of the service, since
// ServiceMediaHandler objects are instances of JMF
// Player objects
javax.media.Player p = (javax.media.Player) handlers[i];
// this may be all we need to do in order to see our background...
// ...but some emulator require more
AWTVideoSizeControl awtVideoSizeControl;
awtVideoSizeControl = (AWTVideoSizeControl)
p.getControl("javax.tv.media.AWTVideoSizeControl");
// in this case, we set the size of the video to be 0, but we could set it
// to display on part of the screen
awtVideoSizeControl.setSize(new AWTVideoSize(new Rectangle(0, 0, 720, 576), new
Rectangle(x,y,l,h)));
}
}
}else {
System.out.println("Can't get our service context. May not be able to " +
"resize the video, so our background may not be visible");
}
}
Prima di ridimensionare o eliminare addirittura il video layer è bene aver impostato una
immagine nel background layer come descritto nel paragrafo precedente, onde
intercorrere a videate nere o addirittura alla non visualizzazione del livello grafico che
ora andremo a trattare.
34
Capitolo 2: Applicazioni MHP
2.4.3 Graphics layer
Il livello grafico è supportato da diversi package messi a disposizione dallo standard;
uno dei fondamentali è Havi 1.1.
In tale package il container di più alto livello in una Xlet è rappresentato dalla classe
org.havi.ui.HScene, che concettualmente è equivalente a java.awt.Window o
java.awt.Frame ma con caratteristiche specifiche per i decoder digitali, ne riportiamo i
metodi principali:
Method Summary
void dispose()
Disposes of this HScene.
java.awt.Image getBackgroundImage()
Retrieve any image used as a background for this HScene.
int getBackgroundMode()
Get the background mode of this HScene.
boolean isOpaque()
Returns true if the entire HScene area, as given by the
java.awt.Component#getBounds method, is fully opaque, i.e.
boolean isVisible()
Determines if the HScene (or more properly its added child
components) is Visible.
void paint(java.awt.Graphics g)
HScene objects override the paint method (defined in
java.awt.Component) to paint the added "child" components on
top of an optional background color or image.
void setActive(boolean focus)
Set whether the HScene is prepared to accept focus.
void setBackgroundImage(java.awt.Image image)
Set an image which shall be painted in the background of
the HScene, after the background has been drawn according to the
current mode set with setBackgroundMode, but before any
children are drawn.
void setBackgroundMode(int mode)
Set the background mode of this HScene.
void setVisible(boolean visible)
Shows or hides this HScene depending on the value of the
input parameter visible.
void show()
Shows this HScene, and brings it to the front.
35
Capitolo 2: Applicazioni MHP
La classe factory che ci permette di richiedere l'unica istanza della HScene è fornita
dallo stesso package e si chiama org.havi.ui.HSceneFactory.
MHP supporta anche le classi AWT contenute nella versione 1.1.8 del JDK Sun ma
spesso è possibile trovare un equivalente nel package Havi e che quindi dovrebbe essere
preferita (es: org.havi.ui.HContainer è più specifica per MHP di java.awt.Container).
Oltre ad Havi e AWT è possibile utilizzare anche un package specifico DVB org.dvb.ui
dove è possibile trovare classi di utilità come la DvbColor che permette di gestire anche
le trasparenze tramite il parametro alpha non presente nella classe java.awt.Color.
Data l’importanza vediamone un tipo di costruttore:
DVBColor (int r, int g, int b, int a) dove r,g,b sono i colori primari red, green, blue e
“a”- alpha è la trasparenza, tutti e quattro i parametri hanno un range da 0 a 255.
Non tutti i tipi di decoder MHP gestiscono le trasparenze in questo modo, ma lo
standard garantisce almeno 3 livelli: 0 % (opaco), 100 % (completamente trasparente),
30 % di trasparenza.
Il font di sistema supportato in MHP è il “Tiresias”, che deve essere disponibile almeno
nelle seguenti dimensioni:
•
36 punti (Title)
•
31 punti (Subtitle)
•
26 punti (Body)
•
24 punti (Footnote)
I formati grafici supportati sono: png, jpg, gif e mpg (I-Frame).
Vediamo un esempio di codice che recupera l'istanza di HScene e ci posiziona un
HContainer e un HText.
HSceneFactory hsf = HSceneFactory.getInstance();
HScene scene = hsf.getFullScreenScene(
HScreen.getDefaultHScreen().getDefaultHGraphicsDevice());//
risoluzione a schermo pieno
scene.setSize(720,576);
scene.setLayout(null);
HContainer cont = new HContainer(50,50,650,500);
HText text = new HText("Casella di testo!",160, 200, 300, 60,
new Font("Tiresias", Font.PLAIN, 26),Color.black,Color.white,
new HDefaultTextLayoutManager());
cont.add(text);
scene.add(cont);
scene.setVisible(true);
scene.repaint();
36
Capitolo 2: Applicazioni MHP
Un altro oggetto molto utile nella creazione di un’interfaccia è HIcon che permette di
caricare un immagine e visualizzarla, per esempio per creare un bottone, in dettaglio:
HIcon
(java.awt.Image image,
int x,
int y,
int width,
int height)
utilizzando
java.awt.Toolkit ecco il nostro bottone:
bottone = new HIcon(Toolkit.getDefaultToolkit().getImage("bottone.jpg"), 50, 100, 140, 30);
Per dare facilmente l’effetto che il pulsante venga premuto si possono creare due oggetti
HIcon con le rispettive immagini, pulsante rilasciato/pulsante premuto, posizionati sulle
stesse coordinate e agire con il metodo setVisible(true/false) per nascondere l’uno e
visualizzare l’altro.
37
Capitolo 2: Applicazioni MHP
2.5 Gestione degli eventi del telecomando
La gestione del telecomando è banale per chi conosce Java, si basa sugli ascoltatore di
eventi; in MHP si possono usare i package org.havi.ui.event che necessita del focus di
un’ oggetto grafico oppure org.dvb.event.
Iniziamo dal primo caso, nel package sono definite le costanti che corrispondono al
codice restituito dal metodo getKeyCode di java.awt.event.KeyEvent ad ogni pressione
di un tasto.
L’ascoltatore
degli
eventi
va
scene.addKeyListener((KeyListener)this)
aggiunto
e
rimosso
nel
nel
metodo
metodo
initXlet:
destroyXlet:
scene.removeKeyListener((KeyListener)this).
Come mostrato in Figura 2.5.1 gli eventi associati alla pressione dei un tasto del
telecomando definiti nell'MHP sono solamente quelli numerici, le frecce di navigazione,
il tasto “OK”, il tasto teletext e i quattro tasti colorati (rosso,verde,giallo,blu).
Fig. 2.5.1 - Input event in MHP
Da notare che lo standard MHP non definisce eventi per i tasti “EXIT” e “BACK” che
sono
presenti
sulla
quasi
totalità
dei
decoder
e
comunque
gestiti
da
org.havi.ui.event.HRcEvent ma potrebbero generare eventi non-standard e non sempre
coerenti sui diversi apparecchi.
Vediamo ora un esempio di codice necessario per l’implementazione del primo
metodo, il codice seguente deve risiedere nella classe principale della Xlet.
38
Capitolo 2: Applicazioni MHP
public void keyPressed (KeyEvent key) {
int pulsantePremuto = key.getKeyCode();
switch(pulsantePremuto){
//Tastierino numerico
case KeyEvent.VK_0:
case KeyEvent.VK_1:
case KeyEvent.VK_2:
case KeyEvent.VK_3:
case KeyEvent.VK_4:
case KeyEvent.VK_5:
case KeyEvent.VK_6:
case KeyEvent.VK_7:
case KeyEvent.VK_8:
case KeyEvent.VK_9:
//Tasti colorati
case HRcEvent.VK_COLORED_KEY_0:
//rosso
case HRcEvent.VK_COLORED_KEY_1:
//verde
case HRcEvent.VK_COLORED_KEY_2:
//giallo
case HRcEvent.VK_COLORED_KEY_3:
//blu
//Tasti direzionali
case HRcEvent.VK_UP:
case HRcEvent.VK_DOWN:
case HRcEvent.VK_RIGHT:
case HRcEvent.VK_LEFT:
case HRcEvent.VK_ENTER:
//OK
//Vengono passati gli eventi.
interfaccia.keyPressed(key);
break;
//Alla pressione del tasto EXIT del telecomando si
//richiama il metodo destroyXlet, per fermare la xlet.
case HRcEvent.VK_ESCAPE:
try{
destroyXlet(true);
}
catch (XletStateChangeException xsce){
System.out.println("Premuto tasto EXIT "+xsce.toString());
}
break;
default:
break;
}
}
public void keyTyped(KeyEvent ignored) { }
public void keyReleased(KeyEvent ignored) { }
39
Capitolo 2: Applicazioni MHP
Essendo il primo metodo ampiamente documentato e conosciuto vediamo nel dettaglio
quello specifico del DVB. Tale meccanismo è consigliabile rispetto ad AWT perché
permette un uso più oculato e collaborativo nella gestione degli eventi. Infatti rispetto
ad AWT dove la registrazione degli eventi è esclusiva e comprende tutti gli eventi
possibili, nel modello dvb è possibile registrarsi per la notifica dei soli eventi a cui si è
realmente interessati.
Le classi principali di questo package sono la EventManger e la UserEventRepository
che gestiscono rispettivamente la registrazione degli eventi e il repository degli eventi a
cui si è interessati.
UserEventRepository repository = new userEventRepository("UserRepository");
repository.addAllColourKeys();
EventManager manager = EventManager.getInstance();
// this rappresenta la classe listener
manager.addUserEventListener(this, repository);
Questo approccio ha l'ulteriore vantaggio di non imporre alla Xlet di richiedere il focus
tramite la HScene (come prevede il modello awt sui Component) risulta quindi essere
l'unico approccio da seguire in tutte quelle applicazioni in cui si vogliono ricevere
eventi del telecomando ma non si dispone una GUI.
40
Capitolo 3: Canale di ritorno
Capitolo 3
Canale di ritorno
3.1 Lo scenario
L’infrastruttura trasmissiva per il digitale terrestre consente l’invio broadcast di
contenuti televisivi, radio ed informatici (es. dati) a ricevitori DTT quali: televisori
associati ad un “decoder” supplementare o Set Top Box, televisori digitali corredati di
decoder interno, altri apparecchi (sintoamplificatori, videoregistratori, sistemi AV
compatti, etc.) che incorporano un sintonizzatore DTT.
A ciascun “programma” (televisivo, radio, informatico) il Content Provider può
associare un servizio informazioni sul programma, un servizio Teletext tradizionale, un
servizio “super Teletext” (“Enhanced Broadcasting”) supportato dallo standard “mhp”
(Multimedia Home Platform); quest’ultimo, infatti, realizza e presenta sullo schermo del
televisore “pagine” contenenti testo e grafica al cui interno è possibile “navigare” in
modo molto simile ad Internet.
Se poi il ricevitore DTT è corredato da un modem (analogico, ISDN o xDSL),
connettendosi ad Internet o ad altre reti di telecomunicazioni, la DTT diventa lo
strumento attraverso il quale sviluppare servizi interattivi più o meno estesi (da forme
minimali di “televoto” realizzato collegandosi a numeri telefonici o a indirizzi Internet
corrispondenti al voto che si vuole esprimere, fino all’impiego di sofisticate
applicazioni di commercio elettronico, e-government, fornitura di servizi Video On
Demand, etc). Più in generale, l’interattività estesa è destinata all’impiego di servizi
Internet.
41
Capitolo 3: Canale di ritorno
Fig. 3.1.1 - Scenario canale di ritorno
Tutte tali possibilità (che si comprendono meglio utilizzando un ricevitore) sono
ampiamente chiarite già nell’introduzione dallo Standard ETSI ES 201 812 V1.1.1
(2003-12) relativo e Digital Video Broadcasting (DVB) e Multimedia Home Platform
(MHP) Specification 1.0.3 , capitolo 0, paragrafo 0.2:
“… At the beginning the following application areas are considered - Enhanced
Broadcasting, Interactive Broadcasting and Internet Access. Enhanced Broadcasting
combines digital broadcast of audio/video services with downloaded applications which
can enable local interactivity. It does not need an interaction channel. The application
area Interactive Broadcasting enables a range of
interactive services associated or independent from broadcast services. This application
area requires an interaction channel. The application area of Internet Access is
intended for the provisioning of Internet services. It also includes links between those
Internet services and broadcast services. …”
In pratica, il protocollo mhp è costituito da una raccolta di “metodi java” (programmi di
base) che consentono di accedere alle funzioni di base del ricevitore DTT e di far partire
automaticamente o manualmente, utilizzando il telecomando, le applicazioni java
trasmesse dal Content Provider Broadcaster. Queste “applicazioni Java” sono a tutti gli
42
Capitolo 3: Canale di ritorno
effetti dei programmi informatici che utilizzano, in luogo di un personal computer o di
un PDA, le capacita di elaborazione e lo schermo del ricevitore DTT. Pertanto, le
applicazioni di “Enhanced Broadcasting” costituiscono il presupposto per il lancio di
applicazioni di “Interactive Broadcating”, che consentono il lancio delle applicazioni di
“Internet Access” realizzate attraverso l’uso di reti PSTN, ISDN o xDSL (si prevede che
la prossima generazione di Set Top Boxes incorpori anche tecnologia WiFi).
Si noti che una volta attivato il “canale di interattività”, il ricevitore DTT può diventare
autonomo dalle applicazioni broadcast e, per esempio, accedere, puramente via Internet.
ad un servizio “pay per view” o “pay to download” via xDSL. Sempre attraverso il
“canale di interazione”, il ricevitore può continuare a ricevere nuovi programmi java
che sviluppano nuove funzionalità.
Di fatto, nel corso della attivazione, l’utente può configurare nel ricevitore DTT il
numero di telefono, il codice utente e la password fornitagli dal proprio fornitore di
accesso ad Internet, ma i servizi di “Enhanced Broadcasting” e “Interactive
Broadcating” possono imporre al ricevitore DTT interattivo di utilizzare per l’“Internet
Access” numero di telefono, codice utente e password diversi da quelli predefiniti
dall’utente.
In altre parole, l’accesso alle applicazioni di “Enhanced Broadcasting” rappresenta una
insuperabile barriera alla realizzazione e alla fornitura di servizi della società
dell’informazione diretti a quel 60% della popolazione che non usa il computer, ma che
dopo lo switch off previsto per il 31 dicembre 2008, userà un televisore DTT.
Accordi tra content provider su piattaforma DTT (che hanno il potere di associare al
programma televisivo o radiofonico opportune applicazioni) e alcuni operatori di rete
fissa, potrebbero avere l’effetto di una totale esclusione da tale mercato dei restanti
operatori di rete fissa. Il pericolo è particolarmente elevato nel caso di Telecom Italia
che, attraverso Telecom Italia Media, dispone già oggi di un accesso ai servizi di
“Enhanced Broadcasting” e “Interactive Broadcating” su piattaforma DTT, ma non è
da sottovalutare l’ipotesi che anche gli altri operatori di rete DTT possano seguire
politiche discriminatorie.
In assenza di adeguate misure volte a garantire l’accesso alle applicazioni di “Enhanced
Broadcasting” (che, si è visto, costituiscono il presupposto per il lancio di applicazioni
di “Interactive Broadcating”), l’accesso al “canale di ritorno” del decoder a condizioni
43
Capitolo 3: Canale di ritorno
oggettive e non discriminatorie tra i diversi operatori di telecomunicazioni e fornitori di
accesso ad Internet e ad assicurare l’ interoperabilità dei servizi, si potrebbe assistere ad
inquietanti fenomeni di “walled garden”, consistenti in un’offerta integrata di contenuti
Internet (es. Video On Demand, e-commerce, etc…), promossi per il tramite delle
applicazioni di Enhanced ed Interactive Broadcasting, accessibili mediante la DTT
(sfruttando il canale di ritorno del decoder) esclusivamente per il tramite di uno
specifico fornitore di accesso e contenuti Internet predeterminato dal fornitore della rete
e/o del servizio DTT, così impedendo l’accesso degli utenti del servizio DTT ai
servizi/contenuti/siti dei content providers non predeterminati dal gestore della rete e/o
del servizio DTT, che verrebbero ad essere relegati in una sorta di “universo recintato”,
a scapito della concorrenza, del pluralismo e, da ultimo, a danno degli utenti finali.
44
Capitolo 3: Canale di ritorno
3.2 I protocolli supportati
Per comprendere appieno le potenzialità del canale di ritorno, che si possono utilizzare,
partiremo dall’analisi dei protocolli supportati da MHP che sono mostrati in figura
3.2.1.
Fig. 3.2.1 - Protocolli canale di ritorno
3.2.1 Livello 1: Fisico
A livello fisico (Livello 1), si definiscono le caratteristiche del mezzo trasmissivo, del
tasso di trasmissione e gli schemi di codifica su cui avverrà la comunicazione. I
ricevitori DTT attualmente in commercio almeno per quanto riguarda quelli interattivi,
sono dotati al loro interno di un modem 56K che permette di aprire una connessione su
canale bidirezionale sfruttando la linea telefonica commutata.
Tuttavia il canale di ritorno via modem analogico (PSTN) presenta i seguenti problemi:
•
banda disponibile ridotta ;
•
impossibilità di uso contemporaneo del servizio di fonia sulla stessa linea
telefonica;
•
canale non always-on, con conseguente necessità di effettuare eventualmente
diverse aperture e chiusure di connessione durante una sessione di utilizzo del
servizio interattivo;
45
Capitolo 3: Canale di ritorno
•
tempi di attesa dovuti alla procedura di connessione;
•
costi di utilizzo basati sulla durata della connessione a circuito;
•
canale con flusso di dati bilanciato, proprietà non utile in molte applicazioni.
Lo standard PSTN (definito nelle specifiche ETSI ETS 300 801) è sicuramente uno
standard affermato e ancora di largo consumo, ma che limita enormemente quelle che
sono le possibilità di creare applicativi di alto livello. Uno dei vantaggi di questa scelta
è però la garanzia che vi sia un più vasto numero di utenti che può, data la ormai
affermata tecnologia e la vasta disponibilità sul territorio, sfruttare i servizi interattivi
messi a disposizione sul digitale terrestre. Il fatto che questa tecnologia sia accessibile
da quasi la totalità della popolazione è un fattore da non sottovalutare per poter rendere
il DTT una tecnologia aperta a tutti.
In futuro con una prevista evoluzione dei servizi messi a disposizione, si inizieranno a
vedere sul mercato STB di fascia media con supporto per linea ISDN oppure di fascia
alta con connessione a banda larga ADSL, anche se ci sarà ancora molto da aspettare
dati i costi attualmente più elevati rispetto a quelli di fasci bassa.
L’ ultima ipotesi è certamente la più interessante, perché da la possibilità di avere una
connessione sempre attiva, cosa non permessa dai normali ricevitori con modem PSTN
che chiudono la chiamata al Centro Servizi dopo un tempo prestabilito di inattività, ma
soprattutto darà la possibilità di portare sul digitale terrestre applicazioni che ora sono
riservate solo ai PC su Internet.
3.2.2 Livello 2: PPP
A livello di collegamento (Livello 2) si trova il protocollo PPP (Point-to-Point
Protocol) che è ad oggi il protocollo più utilizzato per il collegamento a Internet degli
host residenziali tramite doppino telefonico. Standardizzato in RFC 1661 e RFC 2152 .
Il PPP gestisce il riconoscimento degli errori, supporta molti protocolli, permette che gli
indirizzi IP siano stabiliti al momento della connessione e supporta l’autenticazione.
I vantaggi del PPP sono:
•
i frame vengono suddivisi in maniera tale da garantire una non ambigua
separazione tra un frame e quello successivo;
46
Capitolo 3: Canale di ritorno
•
possiede un protocollo di controllo di collegamento per la gestione delle linee
che prende il nome di LCP (Link Control Protocol);
•
le opzioni del livello di rete sono negoziate in maniera indipendente dal
protocollo che si utilizzerà a livello di rete. Si avrà quindi un diverso NCP
(Network Control Protocol) per ogni livello rete supportato.
Nel nostro caso vediamo come il STB riesce ad acquisire un indirizzo IP dal Centro
Servizi per poter poi comunicare con esso ed accedere ai servizi interattivi messi a
disposizione.
Innanzi tutto si esegue la chiamata verso il CS ad un numero che può essere univoco per
tutto il territorio impostato di default dall’utente oppure settato dalla Xlet.
Dopo che il modem del CS ha risposto e stabilito una connessione a livello fisico nel
nostro caso su linea commutata, il STB manda al router una serie di pacchetti LCP
inseriti nel campo Payload di uno o più frame PPP. I pacchetti inviati andranno a
selezionare i parametri PPP da utilizzare nella comunicazione.
Una volta selezionati i parametri, si passa all’invio dei pacchetti NCP per configurare il
livello rete. Nel nostro caso il ricevitore vuole eseguire un insieme di protocolli TCP/IP
e quindi necessita di ottenere un indirizzo IP.
Ogni CS avrà a disposizione un set di indirizzi IP da assegnare e ne verrà assegnato
dinamicamente uno a chi ne fa richiesta, una volta eseguita la connessione e per tutta la
sua durata.
A questo punto il nostro STB e formalmente un host Internet e può inviare e ricevere
pacchetti IP proprio come se fosse connesso fisicamente con CS.
La disconnessone avviene quando NCP sgancia la connessione a livello di rete e va cosi
a liberare l’indirizzo IP. Quindi LCP sgancia la connessione del livello di collegamento
e quindi la PPP ed infine il STB ordina al modem interno di liberare la linea telefonica
rilasciano la connessione a livelli fisico.
47
Capitolo 3: Canale di ritorno
3.2.3 Protocollo di rete: IP
Il livello di rete, è occupato dal protocollo IP (Internet Protocol), che nel suo datagram
incapsula tra i numerosi campi previsti, l’indirizzo IP del server di destinazione, cioè
quello del Centro servizi e quello del client, quindi quello assegnato dal CS stesso al
momento dell’autenticazione in maniera dinamica.
Il suo compito è quello di garantire che i pacchetti arrivino dalla sorgente, nel nostro
caso il STB, alla destinazione il CS, indipendentemente dalla presenza di reti intermedie
durante il suo percorso e indipendentemente dal fatto che più utenti stiano utilizzando
nello stesso momento l’applicazione per mettersi in contatto con il CS.
In pratica sarà il livello di trasporto che riceverà il flusso di dati ed andrà a
frammentarlo in datagram. Ogni datagram viene poi trasmesso e frammentato
ulteriormente durante il percorso, una volta che tutti i pezzi hanno raggiunto la
destinazione, vengono riassemblati dal livello di rete nel datagram di partenza.
Il datagram riassemblato viene poi passato al livello trasporto, che lo inserisce nel flusso
di input del processo ricevente.
Il protocollo IP supporta sopra di se i protocolli TCP e UDP entrambi previsti dalle
specifiche MHP.
3.2.4 Protocolli di trasporto: TCP / UDP
I protocolli di trasporto supportati sono sia il TCP che l’ UDP questi si differenziano
principalmente per il fatto che il primo e orientato alla connessione mentre il secondo e
sostanzialmente fondato sull’IP e non orientato alla connessione.
Il TCP (Transmission Control Protocol) progettato per garantire connessioni affidabili,
necessita di una entità di trasporto TCP, che va a gestire i flussi di dati TCP e si
interfaccia con il livello IP.
I datagram IP ricevuti che contengono dati TCP vengono passati all’entità TCP che
ricostruisce il flusso originale di byte, precedentemente spezzettato dall’entità TCP del
trasmettitore. Il livello IP non da garanzia sulla avvenuta consegna dei datagram; e
quindi compito del TCP ritrasmetterli quando necessario. Un altro compito del TCP è
riassemblare i messaggi nella sequenza corretta tutte cose che da solo IP non può fare.
48
Capitolo 3: Canale di ritorno
Il TCP si basa su punti accesso chiamati socket, sia il client (STB) che il server (CS) ne
devono possedere uno univoco. Ogni socket e caratterizzato da un indirizzo IP che
identifica l’host e da un numero di 16 bit detto “porta”, per esempio l'HTTP comunica
sulla porta 80.
Il protocollo UDP (User Data Protocol) è un protocollo non orientato alla connessione,
è usato per spedire datagram IP, senza dover stabilire una connessione, in maniera più
rapida ma senza garantire il controllo degli errori e del flusso dei pacchetti.
3.2.5 Hypertext Transfer Protocol (HTTP)
Lo standard HTPP (Hyper Text Tranfer Protocol) è il protocollo che sta alla base del
WWW. Le specifiche MHP incentivano l'uso di questo protocollo come possiamo
vedere dalla figura 3.2.1, questo perché il suo utilizzo da la possibilità di trasferire
informazioni di varia natura, come file di testo, immagini, ecc..
L'HTTP è un protocollo orientato alla comunicazione tra un client ed un server. Come si
può vedere anche in figura esso è supportato dal TCP, ma nonostante questo resta
comunque un protocollo “senza stato”, quindi ogni transazione viene trattata in maniera
indipendente dalle altre almeno per quanto riguarda l'HTTP 1.0. Viene quindi creata una
connessione TCP ogni volta che il STB dovrà interagire con il Centro Servizi per lo
scambio dei dati.
I terminali MHP di nuova generazione sono nati per supportare sia HTTP 1.0 (IETF
RFC 1945[92]) che HTTP1.1 (IETF RFC 2616[40]) con alcune considerazioni del caso
soprattutto per quanto riguarda le connessioni di tipo keep-alive.
I terminali MHP e quindi le applicazioni che implementano i profili Interactive TV
Profile e Internet Access Profile, che sfruttano il protocolli HTTP 1.0/1.1 dovranno
supportare connessioni di tipo persistente. L' HTTP 1.1 è stato introdotto per colmare le
lacune del vecchio HTTP1.0 in quanto una delle caratteristiche fondamentali è quella di
stabilire una sola connessione TCP per tutta la durata della comunicazione.
Il fatto che nella maggior parte dei casi il terminale MHP comunicherà per tutta la
durata della connessione con un unico ISP, il nostro Centro Servizi, è opportuno
stabilire una connessione sempre attiva per tutta la durata della comunicazione evitando
problemi di congestione della rete.
Per stabilire una connessione di tipo persistente i terminali MHP che sfruttano il
49
Capitolo 3: Canale di ritorno
protocollo HTTP1.0 dovranno inserire all'interno del messaggio HTTP nel campo
Connection del General-Header il token Keep-Alive:
Connection: keep-alive
Il server HTTP che si vede arrivare un messaggio di richiesta di connessione KeepAlive risponderà instaurando una connessione persistente.
Se si fa questo,il campo Keep-Alive del General-Header, del messaggio HTTP, conterrà
il tempo durante il quale il trasmettitore mantiene la connessione aperta in attesa della
richiesta successiva, oppure il numero massimo di richieste aggiuntive che sono
consentite sulla connessione in corso. Il campo Keep-Alive sarà composto come sotto:
Keep-Alive-header="Keep-Alive" ":" 0# keepalive-param
keepalive-param = param-name "=" value
dove la notazione ''0#'' significa che i parametri potranno essere ripetuti 0 o più volte e
saranno separati da '','' se il campo ne contiene più di uno.
Se i terminali MHP dovranno comunicare con un server proxy HTTP1.0 non sarà
necessario inviare il token Keep-Alive, questo perché un proxy HTTP1.0 non ubbidisce
alle regole dell' HTTP1.1 per fare il parsing del campo Connection. Questo significa che
si dovrà impedire ai terminali MHP di richiedere connessioni sempre attive quando
questi devono comunicare con server proxy che non ne potrà supportare le funzionalità.
I server di tipo HTTP1.1 che si basano su connessioni persistenti dovranno supportare
anche l' HTTP1.0 per richieste da parte di terminali MHP che lavorano con protocollo
HTTP1.0 e che cercheranno comunque di instaurare connessioni sempre attive
utilizzando il token Keep-Alive.
50
Capitolo 3: Canale di ritorno
3.2.6 Secure Hypertext Transfer Protocol (HTTPS)
Nei casi in cui si richiede un canale di comunicazione protetto con il CS, le specifiche
MHP consigliano l'uso del protocollo HTTPS. Questo protocollo è sostanzialmente
basato sull'HTTP ma affianca ad esso il protocollo TLS (Transport Layer Security).
TLS 1.0 è molto simile a SSL 3.0 anche se non è compatibile con questo. Lo scopo di
TLS è quello di permettere una comunicazione sicura tra due applicazioni, fornendo al
flusso di dati servizi di autenticazione, integrità e riservatezza. Il profilo MHP non
implementa l'intero TLS ma solamente una parte degli algoritmi di cifratura previsti tra
cui in particolare :
• RSA ()
• MD5
• SHA-1
• DES
3.2.7 Servizi Specifici
Qualora si trovasse la necessita di supportare da parte dei ricevitori protocolli
proprietari per il canale di ritorno lo standard MHP lascia ai costruttori la possibilità di
definirne alcuni e di portarli sulle piattaforme. Ad oggi tutti i ricevitori STB in
commercio utilizzano protocolli di uso comune che garantiscono più robustezza,
integrità e maggiore possibilità di integrazione alla rete Internet per una prospettiva
futura di accesso a contenuti Web con l'introduzione del profilo “Internet Access
Profile”.
51
Capitolo 3: Canale di ritorno
3.3 Gestione della connessione
Indipendentemente dal tipo di modem di cui è provvisto il set top box, le connessioni
sono gestite dal package Org.dvb.net.rc in questo paragrafo andremo a vedere nel
dettaglio le classi di questo package e cercheremo di capire come queste interagiscono
tra di loro per poter poi essere utilizzate per stabilire una connessione con un ISP
remoto.
Successivamente daremo uno sguardo agli altri package, java.net e java.io, che verranno
utilizzati una volta che la connessione è stabilita.
3.3.1 Utilizzare il canale di ritorno: il package org.dvb.net.rc
Tra i tre profili che MHP introduce sia “Interactive Broadcast” che “Internet Access”,
includono il supporto per utilizzate il canale di ritorno sui STB. In base al tipo di
ricevitore che stiamo utilizzando il canale di ritorno può assumere diverse forme in base
alla fascia di appartenenza, anche se ad oggi quella più comune è il modem PSTN, che
garantisce: una maggiore reperibilità sul territorio, quindi un bacino di utenza più ampio
e semplicità di installazione, anche se porta dei limiti che come abbiamo gia visto sono
molto “pesanti”. I STB quando stabiliscono una connessione con canale di ritorno non
fanno altro che appoggiarsi al protocollo IP del livello di rete e come molte applicazioni
Java che fanno uso di una connessione remota, anche le nostre Xlet si appoggeranno
alle classi del package java.net, con alcune restrizioni del caso.
Le specifiche MHP 1.0.x richiedono supporto per HTTP1.0 e DNS su canale di ritorno
al di sopra del TCP, ma ogni altro protocollo è opzionale, questo lascia un po’ di liberta
anche se può portare a dei mal funzionamenti, sarà quindi compito del programmatore
fare le opportune verifiche. Con MHP 1.1 le specifiche aggiungono il supporto per
HTTPS un protocollo per connessioni sicure che unisce l’ HTTP con i certificati TLS.
Le specifiche non aggiungono niente per quanto riguarda il supporto ad altri protocolli
come FTP o SMTP. Anche se l’ultimo è sicuramente necessario per la creazione di un
client e-mail.
Una considerazione va fatta per quanto riguarda la gestione della connessione, in quanto
le API java.net assumono che una connessione permanente sia eseguibile, è questo
52
Capitolo 3: Canale di ritorno
può diventare un problema da non sottovalutare specialmente quando il nostro STB usa
un modem PSTN. In certi casi non ci dovremo curare di gestire la connessione, sarà il
ricevitore a connettersi automaticamente all’ISP di default, dovremmo solo
preoccuparci
di
utilizzare
correttamente
le
classi
java.net.socket
e
java.net.URLConnection per fare riferimento ad un file remoto. In maniera simile il
ricevitore dopo un periodo di inattività andrà a disconnettere automaticamente il canale
di ritorno, questo porta ad una sorta di sistema di sicurezza da xlet maligne che però è
facilmente aggirabile.
3.3.2 Interfacce per il Canale di Ritorno
Ogni tipo di interfaccia supportata dai STB è rappresentata da un’istanza della classe
RCInterface :
public class RCInterface extends java.lang.Object {
public static final int TYPE_PSTN = 1;
public static final int TYPE_ISDN = 2;
public static final int TYPE_DECT = 3;
public static final int TYPE_CATV = 4;
public static final int TYPE_LMDS = 5;
public static final int TYPE_MATV = 6;
public static final int TYPE_RCS = 7;
public static final int TYPE_UNKNOWN = 8;
public static final int TYPE_OTHER = 9;
private int type;
private int dataRate;
protected RCInterface() {
}
public int getType() {
return this.type;
}
public int getDataRate() {
return this.dataRate;
}
}
Con questa classe è possibile modellare nome, velocità in Kbps e tipo di interfaccia da
utilizzare per ottenere una connessione IP. Come possiamo vedere questa classe
definisce un insieme di costanti dove ognuna rappresenta un tipo di canale di ritorno.
53
Capitolo 3: Canale di ritorno
COSTANTI
TYPE_PSTN
TYPE_ISDN
TYPE_DECT
TYPE_CATV
TYPE_LMDS
TYPE_MATV
TYPE_RSC
TYPE_UNKNOWN
TYPE_OTHER
TIPO DI CANALE
PSTN
ISDN
DECT
Cable modem
Local Multipoint Distribution System.
Canale di Ritorno Wireless
Master antenna TV
DVB-RCS (Canale di ritorno via
satellite)
Utilizzato quando è presente una
interfaccia hardware tra il ricevitore e il
dispositivo senza conoscerne i dettagli
Usato per standard futuri di cui DVB non
intende aggiungere costanti proprie
Tabella 3.3.2.1 – Costanti canale di ritorno
Il metodo getType()ritorna un intero corrispondente al tipo di interfaccia a cui la classe
ConnectionRCInterface fa riferimento. Mentre il metodo getDataRate() ritorna il
massimo “data rate” dell’interfaccia a cui siamo connessi in questo momento, se
nessuna interfaccia è connessa il ricevitore ci restituirà il valore dell’ultima interfaccia
utilizzata oppure il valore –1 se non reperibile.
54
Capitolo 3: Canale di ritorno
3.3.3 Acquisire l’interfaccia corretta
Come abbiamo visto nelle specifiche MHP esiste più di un tipo differente di interfaccia,
in base al supporto che implementa il canale di ritorno, questo rende necessaria la
presenza di una classe che sia in grado di ottenere l’accesso a quella corretta e ne
controlli l’utilizzo in base al quello che dovrà fare la nostra xlet.
A tal proposito e stata creata la classe RCInterfaceManager che ci fornisce alcuni
metodi per controllare l’accesso a questo tipo di risorsa scarsa.
public class RCInterfaceManager
implements org.davic.resources.ResourceServer {
public static RCInterfaceManager getInstance();
public RCInterface[] getInterfaces();
public RCInterface getInterface(java.net.InetAddress addr);
public RCInterface getInterface(java.net.Socket s);
public RCInterface getInterface(java.net.URLConnection u);
public void addResourceStatusEventListener(
org.davic.resources.ResourceStatusListener listener);
public void removeResourceStatusEventListener(
org.davic.resources.ResourceStatusListener listener);
}
La classe RCInterfaceManager mette a disposizione un metodo il getInstance()che
viene innanzitutto utilizzato per ottenere un’istanza di se stessa da utilizzare nei passi
successivi. Una volta che abbiamo ottenuto il riferimento a questa classe possiamo
procedere
con
l’acquisire
l’interfaccia
appropriata,
cosi
con
il
metodo
getInterfaces() ci viene restituito un array di RCInterface in base ai permessi
associati alla nostra applicazione e alla disponibilità di interfacce che il nostro ricevitore
supporta. Questo array potrebbe risultare anche vuoto se la nostra applicazione non ha i
permessi necessari per l’uso della risorsa magari perché viene identificata come
maligna. Il metodo getInterfaces() ha tre varianti legate alla classe del package
java.net che andremo ad utilizzare; java.net.URLConnection, java.net.Socket,
java.net.InetAdress.
Nei primi due casi il middleware del ricevitore assume che la connessione sia già attiva,
questo perché sarà esso stesso che non appena nota la richiesta di connessione ad una
55
Capitolo 3: Canale di ritorno
URL o ad una Socket cerca di creare una connessione su linea di ritorno, e ritornerà
l’interfaccia utilizzata per questa connessione. Nell’ultimo caso invece il middleware
ritornerà l’interfaccia che dovremmo poi utilizzare per stabilire la connessione. Come
ho gia detto i STB in commercio sono forniti solamente di modem PSTN e quindi i
metodi appena citati riporteranno se ben formati sempre l’interfaccia ‘1’ o nulla se non
presente, bisognerà tenere conto di questo per non incorrere in mal funzionamenti.
Essendo il canale di ritorno una risorsa scarsa, questa classe come possiamo vedere dal
codice, implementa l’interfaccia ResourceServer
permettendo di associare un
ResourceStatusListener al nostro oggetto per informarlo sugli eventi che accadono
alla risorsa.
Gli eventi generati del tipo ResurceStatusEvents possono essere:
•
RCInterfaceReservedEvent:
Questo
evento
viene
generato
quando
un’applicazione ha correttamente riservato una RCInterface, può essere
generato anche quando un’altra entità del sistema ha riservato la risorsa
rendendola cosi non più utilizzabile.
•
RCInterfaceReleasedEvent: Viene generato quando un’applicazione che
precedentemente aveva riservato una RCInterface ha poi deciso di rilasciare la
risorsa chiamando il metodo release() della classe ConnectionRCInterface
su quella interfaccia. Può essere generato anche da un’altra entità del sistema
informando cosi l’applicazione che la risorsa è ora disponibile.
56
Capitolo 3: Canale di ritorno
3.3.4 Stabilire la connessione
Tra le interfacce per il canale di ritorno dichiarate nella classe RCInterface alcune
rappresentano una connessione sempre attiva. Il middleware non tratta quindi tutti i tipi
di interfacce in maniera uguale questo perché le connessioni sempre attive come
possono essere TYPE_CATV o ADSL non vengono trattate come se fossero risorse
scarse, semplicemente perché l’applicazione non ha bisogno di sapere a quale provider
il canale di ritorno è connesso. La gestione della connessione e quindi completamente
lasciata nelle mani del middleware che si occupa di tenerla sempre disponibile nel
momento in cui l’applicazione ne fa richiesta, senza preoccuparsi di troppi dettagli.
Questo però non accade con altri tipi di interfacce, come per esempio con un modem
PSTN, in questo caso dovremo conoscere il numero di telefono dell’ISP a cui
connettersi, prima di poter utilizzare la risorsa, oltre a dati come user e password. In
base al service
provider al quale ci si vuole connettere potrebbe succedere che
un’applicazione sia abilitata a stabilire una comunicazione, mentre altre no per problemi
di sicurezza, e ciò pone dei limiti ma anche dei vantaggi.
La classe ConnectionRCInterface estende la classe RCInterface ed implementa
l’interfaccia ResourceProxy , di seguito possiamo vedere i metodi di cui e composta.
public class ConnectionRCInterface
extends RCInterface
implements org.davic.resources.ResourceProxy {
public boolean isConnected();
public float getSetupTimeEstimate();
public void reserve(
org.davic.resources.ResourceClient c,
java.lang.Object requestData)
throws PermissionDeniedException;
public void release();
public void connect()
throws java.io.IOException, PermissionDeniedException;
public void disconnect()
throws PermissionDeniedException;
public ConnectionParameters getCurrentTarget()
throws IncompleteTargetException;
public void setTarget(ConnectionParameters target)
throws IncompleteTargetException,
PermissionDeniedException;
57
Capitolo 3: Canale di ritorno
public void setTargetToDefault()
throws PermissionDeniedException;
public int getConnectedTime();
public org.davic.resources.ResourceClient getClient();
public void addConnectionListener(
ConnectionListener l);
public void removeConnectionListener(
ConnectionListener l);
}
Questa classe mette a disposizione i metodi necessari per riservare la risorsa, settarne i
parametri ed eseguire connessione e disconnessione dall’ISP impostato.
Prima di eseguire la connessione all’ISP utilizzando l’interfaccia specificata è
necessario riservare la risorsa per la nostra applicazione, per fare questo si utilizza il
metodo riserve() per evitare che altri cerchino di utilizzarla generando dei conflitti.
Questo metodo è utilizzato per associare all’interfaccia un ResurceClient utilizzato
per i messaggi di notifica sullo stato della risorsa e per quando questa viene rimossa,
questo metodo può generare un’eccezione del tipo PermissionDeniedException
quando la nostra applicazione non ha i permessi necessari per riservare la risorsa.
L’applicazione che utilizza il canale di ritorno deve per forza essere a conoscenza dei
dati che ci permettono di chiamare ad autenticarci presso un ISP e quindi prima di
chiamare il metodo connect() sarà necessario che questi vengano impostati
nell’interfaccia selezionata, per fare questo si utilizza il metodo setTarget(). Ogni
interfaccia ha in genere un ISP associato al quale ci si connette quando la si usa,
solitamente questo è strettamente legato al broadcaster che trasmette l’applicazione
interattiva e che quindi mette a disposizione anche un Centro Servizi con cui interagire.
Il metodo setTargetToDefault() rimuove tutti i target impostati e setta quello che il
produttore del middleware ha definito come standard. Se la nostra applicazione non ha i
permessi per reimpostare il target o se quello di default non è valido viene lanciata una
SecurityException.
Ogni target è rappresentato da un oggetto della classe ConnectionParameters, questa
classe è utilizzata per creare un oggetto in cui sono definiti i parametri necessari per
stabilire una connessione.
58
Capitolo 3: Canale di ritorno
La classe e strutturata come segue:
public class ConnectionParameters {
public ConnectionParameters(
java.lang.String number,
java.lang.String username,
java.lang.String password);
public ConnectionParameters(
java.lang.String number,
java.lang.String username,
java.lang.String password,
java.net.InetAddress[] dns);
public java.lang.String getTarget();
public java.lang.String getUsername();
public java.lang.String getPassword();
public java.net.InetAddress[] getDNSServer();
}
Come possiamo vedere i parametri di cui una interfaccia ha bisogno sono un numero
telefonico un “username” e una password che il middleware utilizza per autenticare la
connessione. E’ possibile passare se necessario un array con gli indirizzi dei server DNS
(Domain Name System) da utilizzare, prima di riportare un messaggio di fallimento,
che vengono utilizzati per trasformare nomi di host e indirizzi di posta elettronica in
indirizzi IP, da utilizzare quindi per stabilire connessioni TCP/UDP sulle socket.
Chiamando
il
metodo
setTarget()
è
possibile
che
venga
lanciata
una
IncompleteTargetException nel caso in cui l’oggetto ConnectionParameters fosse
stato creato con qualche parametro a null.
Ogni cambiamento sull’oggetto ConnectionParameters avrà effetto nel momento in
cui noi stabiliamo la connessione, se pero delle modifiche vengono fatte durante una
connessione gia attiva per rendere effettivi i nuovi parametri sarà necessario
disconnettere l’interfaccia e poi riconnetterci per puntare al nuovo ISP. Una volta
impostato un target possiamo chiamare il metodo connect() sulla nostra interfaccia, ed
una volta ottenuto l’IP utilizzare le classi del package java.net per connetterci all’host
desiderato.
Nel caso dell’interfaccia PSTN il middleware andrà a chiedere il consenso dell’utente
prima che l’applicazione effettui la chiamata, se l’utente acconsente il
numero
telefonico viene messo in una “white list” per evitare che si richieda il consenso al
prossimo accesso.
59
Capitolo 3: Canale di ritorno
Se l’utente blocca la chiamata viene generato un ConnectionFailedEvent, mentre se si
acconsente ed il middleware riesce a creare la connessione viene generato un
ConnectionEstablischedEvent.
Nel caso in cui la risorsa sia riservata ma non sia possibile stabilire una connessione con
il target specificato il middleware lancerà una IOException per notificare all’ascoltare
che ci sono stati dei problemi non direttamente dovuti all’hardware del ricevitore.
Una volta che l’applicazione ha terminato di utilizzare la risorsa si chiama il metodo
disconnect() e quindi il metodo release() per rilasciare la risorsa e permettere ad
altre applicazioni di utilizzarla, lanciando l’evento ConnectionTerminatedEvent.
Ottenere la connessione ad un ISP può richiedere del tempo ed avere delle complicanze,
specialmente nel caso di un modem PSTN, per questo la nostra applicazione dovrà
registrare come ascoltatori quelle classi che intendono sfruttare la connessione.
Queste classi dovranno implementare l’interfaccia ConnectionListener e quindi
definire come il metodo astratto connectionChanged dovrà comportarsi quando si vede
arrivare un evento del tipo ConnectionRCEvent.
La classe ConnectionListener è strutturata come segue:
public interface ConnectionListener {
public abstract void connectionChanged(ConnectionRCEvent e);
}
Le classi che implementano ConnectionListener vengono associate all’oggetto
ConnectionRCInterface utilizzando il metodo addConnectionListener in modo
che il middleware saprà a chi inviare gli eventi che si generano da quell’ interfaccia.
Tali eventi sono di tre tipi:
•
ConnectionEstabliscedEvent: Generato nel caso in cui la connessione ha
avuto successo.
•
ConnectionFailedEvent: Se non è stato possibile stabilire la connessione per
motivi tecnici, se per esempio qualcuno sta utilizzando in quel momento il
telefono.
•
ConnectionTerminateEvent: Generato nel caso in cui si rompe la connessone
oppure se l’applicazione stessa ha deciso di disconnettersi.
60
Capitolo 3: Canale di ritorno
Gli eventi appena descritti possono essere generati anche dall’applicazione stessa nel
caso in cui si verifichino più sequenze di request sulla stessa connessione per notificare
alla classe in ascolto che la sessione è ancora attiva e si possono trasferire file senza
stabilirne una nuova.
3.3.5 I package java.net e java.io
Ottenuta la connessione con il Centro Servizi tramite i metodi messi a disposizione dal
package org.dvb.net.rc le applicazioni si vanno ad appoggiare alle API java.net per
accedere alle URL. Lo standard MHP pone alcune restrizioni sulle classi che si possono
utilizzare di questo package, in particolare le classi messe a disposizione da DVB-J che
è la versione ridotta di Java per MHP basata oltre che sui package specifici per il DVB
sulla Personal Java 1.1.8 sono:
•
java.net.URL
•
java.net.URLConnection
•
java.net.Socket
•
java.net.InetAdress
Tutte queste classi possono essere utilizzate per ottenere un collegamento con il Centro
Servizi tramite protocolli HTTP e HTTPS.
La particolarità delle classi del package java.net è quella di essere in grado di
interfacciarsi tramite l’uso della JVM con il sistema operativo della macchina su cui si
sta eseguendo l’applicazione sarà poi questa a gestire l’accesso al modem e a far
transitare le richieste attivando una connessione su protocollo TCP.
Ottenuto il link con la risorsa desiderata ci si appoggia al package java.io per i metodi
di gestione dei file veri e propri. Anche altri metodi possono essere utilizzati per gestire
i file in particolare per quanto riguarda immagini ci si appoggia alla classe
java.awt.image.ImageProducer e per file audio e video al package javax.media. E’
sempre opportuno controllare nelle specifiche MHP quali classi possono essere
utilizzate per evitare mal funzionamenti nelle applicazioni dato che non tutti i tipi
MIME sono supportati dai STB.
61
Capitolo 4: Ambiente di sviluppo
Capitolo 4
Ambiente di sviluppo
4.1 Introduzione
Dopo aver visto nei capitoli precedenti le tecnologie usate nel digitale terrestre, le
caratteristiche principali di un’applicazione MHP, con particolare riguardo all’uso del
canale di ritorno, andremo a proporre come è possibile realizzare l’ambiente di sviluppo
per l’implementazione, debug e test di una Xlet.
Per l’implementazione, come prima cosa, si avrà bisogno di un PC con installata una
Java Virtual Machine e magari un programma di sviluppo Java, come Eclipse, per
evitare errori di sintassi.
Per la fase di debug e una prima fase di test è molto utile utilizzare un simulatore per
applicazioni MHP, per vedere errori in fase di compilazione.
Per la fase di test vera e propria ci sono due modi di procedere:
•
il primo consiste nel simulare un broadcaster; necessità di un modulatore
COFDM al quale in ingresso verrà mandata l’applicazione insieme ad un
eventuale flusso video, mentre in uscita vi sarà collegato uno (o più) Set Top
Box commerciali;
•
il secondo, che è quello che poi andremo ad analizzare in dettaglio, consiste
nell’acquisto di un Set Top Box da sviluppo, che a differenza di quelli
commerciali, permette l’ upload di applicazioni non solo dall’etere, ma anche in
locale, tramite porta seriale (RS-232).
62
Capitolo 4: Ambiente di sviluppo
4.2 Il Simulatore XletView
Tra i vari software freeware di simulazione per applicazioni MHP, che si trovano in
rete, spicca XletView, distribuito da GNU General Public License (GPL) , scaricabile
all’indirizzo www.sourceforge.net.
Per l’installazione si richiede che sul PC sia presente una Java Virtual Machine a partire
da JRE 1.4, JSDK1.4 o versione superiore, della Sun; mentre all’interno del pacchetto
troviamo già le API JMF 2.1.1 (Java Media Framework) che servono per incorporare
media-data come audio e video nelle applicazioni Java, applets e Xlet.
Dopo aver installato il simulatore bisogna copiare nella cartella base, le API Javatv
scaricabili dal sito della Sun, praticamente le directory presenti devono essere:
Fig. 4.2.1 - Directory XletView
A questo punto non ci dovrebbero essere problemi per l’avvio del programma; è
importante che insieme alla finestra principale ne compaia anche un’altra, dove
verranno scritti gli eventuali messaggi d’errore, insieme ai messaggi di debug voluti dal
programmatore, ovvero tutti i System.out.println presenti nella Xlet.
63
Capitolo 4: Ambiente di sviluppo
Per fare ciò è necessario scrivere la seguente riga di comando, magari mettendola in un
file batch:
cd xletview
java -cp
%CLASSPATH%;javatv_fcs/javatv.jar;xletview.jar;jars/metouia.jar;jars/javassist.jar;
jars/nanoxml-2.2.3.jar net.beiker.xletview.Main
cd ..
Ora le finestre visibili dovrebbero essere le seguenti come mostrato in figura 4.2.2 e
4.2.3.
Fig. 4.2.2 - Finestra principale XletView
Nella schermata principale vengono visualizzati:
•
a destra il telecomando con tutti i tasti per simulare, mediante mouse, la
periferica d’ingresso;
•
nel riquadro giallo l’eventuale background layer, video layer e il graphics layer;
•
i menù per poter eseguire la nostra applicazione.
Dal menu Applications, sotto ManageApplication, bisognerà inserire il classpath e il
nome della classe principale della Xlet.
64
Capitolo 4: Ambiente di sviluppo
Una volta che l’applicazione è stata mandata in “run”, dalla schermata di debug
mostrata in figura 4.2.3, sarà possibile vedere gli eventuali errori e messaggi.
Fig. 4.2.3 - Finestra di debug XletView
Mentre si programma bisogna tener conto che a differenza del PC, dove abbiamo a
disposizione tutte le API fornite dal JR1.4, sul Set Top Box vi è una versione ridotta
della JVM , quindi bisogna fare attenzione alle specifiche sulle API che si andranno ad
usare messe a disposizione su www.mhp.org e www.dvb.org, altrimenti si rischia di
sviluppare applicazioni che “girano” solo su simulatore.
65
Capitolo 4: Ambiente di sviluppo
4.3 Set Top Box da sviluppo ADB X-75
Per completare il ciclo di sviluppo di una applicazione MHP, dopo che è stata
sviluppata con Eclipse e mandata in esecuzione su Xletview, manca solo la fase finale
di test, che come abbiamo detto consiste nel caricare l’applicazione su un Set Top Box
da sviluppo. Riassumendo il nostro ambiente di sviluppo quindi sarà costituito da un
Personal Computer, da una normale TV munita di presa SCART e da il Set Top Box da
sviluppo ADB X-75.
Le interconnessioni tra gli apparati appena elencati, come mostrato in figura 4.3.1, sono:
•
il STB riceve in ingresso il segnale televisivo (da una comune antenna), è
collegato tramite SCART alla TV, e tramite seriale RS-232 al PC;
•
sia il PC che il STB sono connessi in rete (LAN) successivamente ne chiariremo
il motivo.
Fig. 4.3.1 - Ambiente di sviluppo
66
Capitolo 4: Ambiente di sviluppo
4.3.1 Caratteristiche Tecniche
Una delle più prestigiose ditte che forniscono strumenti per lo sviluppo in campo
broadcast è l’ADB, acronimo di Advanced Digital Broadcast.
La serie X-75 comprende Decoder da sviluppo per tre tecnologie: Cable, Satellite,
Terrestrial, in conformità con lo standard DVB, quindi è più corretto chiamare il nostro
strumento ADB T.75.
Andiamo ad elencare innanzitutto i componenti e le caratteristiche Hardware mostrate
nella seguente tabella 4.3.1.1:
ARCHITETTURA
DESCRIZIONE
CPU STi5517 166MHz
Tuner/Front end DVB-T
Flash Memory 16 MB
RAM Memory 72 MB
EEPROM 32 kB
Power Supply 90-264 VAC, 47-63Hz
Casing 440x260x50mm
OUTPUTS
RF input/output RF in & RF out (loop trought)
Audio/Video outputs 2xRCA (Stereo Audio), 2xSCART, S/PDIF optical
Return channel Ethernet 10BaseT, PSTN modem v.92
Data port Debug serial port (RS-232 up to 115.2 kbps)
Front panel display 4x7-segment LED display, 2 LEDs
DVB-CI slot Located on front panel
Smart card slot Located on front panel
ACCESSORI
Power Cable 1.8 m
SCART Cable 1.5 m
RS 232 Cable 2.0 m
67
Capitolo 4: Ambiente di sviluppo
CARATTERISTICHE
DESCRIZIONE
MPEG VIDEO DECODING
Standards MPEG-2 MP@ML, CD ISO/IEC 13818-1, CD
ISO/IEC13818-2
Video Data Rate 0.5 – 15 Mbps
Format Conversion 4:3 > 16:9 with Pan & Scan and Letterbox
Graphics Planes 4 planes(Background, Still-plane, Video, OSD)
AUDIO DECODING
Standards MPEG-1 Layer 1&2; 16 bit precision, CD ISO/IEC 13818-3
Sampling Rate 32 kHz, 44.1 kHz, 48 kHz
Variable Output Level 16 steps @ 2dB per step
DOLBY Digital AC3 Pass throught to S/PDIF
TERRESTRIAL FRONT END – T.75
COFDM(DVB-T) ETSI EN 300 744
Modulation QPSK, QAM16, QAM64
Code rate ½, 2/3, ¾, 5/6, 7/8
Guard Interval ¼, 1/8, 1/16, 1/32
Transmission modes 2k, 8k
Tabella 4.3.1.1 - Aspetti tecnici
Un STB di buon livello, reperibile attualmente in commercio, presenta sicuramente le
seguenti caratteristiche:
•
modem V.90;
•
lettore Smart Card;
•
doppia presa SCART;
•
uscita audio RCA;
•
uscita audio ottica;
•
connettore seriale RS-232 per eventuali periferiche di ingresso;
Il nostro Set Top Box da sviluppo, oltre ad avere tutte le caratteristiche di un normale
decoder, presenta:
68
Capitolo 4: Ambiente di sviluppo
•
una maggior capacità di elaborazione (frequenza processore più elevata)
•
una maggiore capacità di memorizzazione;
•
interfaccia Ethernet;
•
CI Common Interface;
•
presa seriale RS-232 bidirezionale (permette l’upload dell’applicazione e il
debug).
4.3.2 Pannello frontale
Mostriamo ora nei dettagli il pannello frontale e le funzionalità dei suoi componenti,
rispettivamente in figura 4.3.2.1 e nella tabella 4.3.2.1. I componenti sono: 7 pulsanti, 2
indicatori LEDs, un display, una CI Common Interface e una OCF Smart Card slot.
Fig. 4.3.2.1 - Pannello frontale ADB T.75
69
Capitolo 4: Ambiente di sviluppo
Tabella 4.3.2.1 - Funzionalità pannello frontale
4.3.3 Pannello posteriore
In figura 4.3.3.1 si mostra come è composto il pannello posteriore.
Fig. 4.3.3.1 - Pannello posteriore ADB T.75
Vediamo in dettaglio i suoi componenti:
1. interruttore alimentazione;
2. presa alimentazione 220V ~ 50Hz;
3. connettore RS-232;
4. ingresso antenna;
5. uscita antenna (alla TV);
6. RJ11 jack modem;
7. RJ45 jack Ethernet;
8. Uscita AC3 ottica (Audio digitale);
9. SCART (connessione verso TV);
10. SCART (connessione verso eventuale VCR);
11. uscita audio RCA.
70
Capitolo 4: Ambiente di sviluppo
4.3.4 Telecomando
Il telecomando e le funzionalità dei sui tasti sono mostrate in figura 4.3.4.1; non ci sono
molte differenze rispetto a quello dei comuni decoder, da notare il tasto “APP” che apre
e chiude la finestra delle applicazioni.
Fig. 4.3.4.1 - Telecomando ADB T.75
71
Capitolo 4: Ambiente di sviluppo
4.3.5 Settaggio connessione di default
Diamo uno sguardo al menù principale del decoder ADB:
Tra i vari sottomenù della voce “Setting”, in fondo
troviamo “Internet Connection”, una volta entrati
apparirà una schermata dove è possibile scegliere
se usare come predefinita la connessione tramite
modem PSTN o tramite Ethernet.
Internet Connection
Ethernet Setting
La connessione Ethernet permette di far comunicare
Il STB con gli altri dispositivi della rete, a patto che
al decoder sia associato un indirizzo IP;
per fare questo ci sono due modi Auto e Manual .
Se si imposta “Auto” dal menù, verrà assegnato un
Indirizzo IP dinamico (si suppone che il server della
Ethernet (Auto)
rete abbia abilitato il DHCP).
In caso contrario si dovrà impostare su “Manual” e
Si dovranno inserire i valori: IP (statico), Mask,
Default Gateway e il DNS primario e secondario.
Ethrnet (Manual)
72
Capitolo 4: Ambiente di sviluppo
Modem Setting
Se si vuole far connettere in rete il STB, usando il
Modem, bisogna impostare in questa schermata:
N telefonico ISP, eventuale prefisso, tipo composizione
(impulsi/tone), username, password ed eventualmente
l’opzione “aspetta segnale di libero”.
4.3.6 STB Firmware upgrade
Prima di passare all’upload di un’applicazione è necessario aggiornare il firmware
fornito dalla casa madre.
Il software che risiede nel Set Top Box (STB), consiste in due parti principali: il
decoder code e il loader.
Il decoder code, anche chiamato codice di alto livello, è responsabile della ricezione,
codifica e visualizzazione di audio/video, e altri componenti come teletext, sottotitoli,
etc.
Il loader non mostra ne video ne audio, ma visualizza all’utente alcune informazioni
riguardanti le fasi del processo o errori di download.
L’ADB fornisce insieme al STB un software chiamato ADB FastTerm, che interagisce
con il loader per aggiornare il firmware tramite la porta RS-232.
L’applicazione di cui sopra, si mostra come in figura 4.3.6.1; prima di utilizzarla per
prima cosa bisogna impostare il numero della porta COM (dal menù a tendina), sulla
quale è connesso il STB, assicurandosi che questa sia settata con i seguenti parametri:
•
Baud rate
•
N° bit di dati
•
Parità
•
N° bit di stop
115200
8
NO
1
A questo punto, dal menù File - Open download file, si va ad aprire il file contenente
l’aggiornamento (*.enc), si spegne il STB per almeno 2 secondi, si tiene premuto il tasto
freccia sinistro situato sul pannello frontale e contemporaneamente si riaccende il STB.
Il tasto deve continuare ad essere premuto fino a che non si accendono i LED frontali, a
questo punto si lascia il pulsante e inizia il download del firmware sul STB; in questa
fase la barra sulla destra dell’applicazione inizia a colorarsi fino ad arrivare al 100%.
73
Capitolo 4: Ambiente di sviluppo
Fig. 4.3.6.1 - Finestra FastTerm
4.3.7 UpLoad e debug di un’applicazione
La ditta produttrice del STB oltre al software “FastTerm”, fornisce anche l’ADB
APPLOADER che permette di trasferire la nostra applicazione, dal PC alla memoria del
decoder, sempre tramite porta seriale.
In realtà la comunicazione tra i due dispositivi deve passare attraverso un secondo
programma, che fa da Proxy, il quale può risiedere localmente o trovarsi in un’altra
macchina, l’importante è che abbia un indirizzo IP valido e che sia connessa al STB.
Quando si fa partire il Proxy, bisogna specificare il numero di porta COM (con gli
eventuali settagli) dove è connesso il STB e il nome del file di log comprensivo di
classpath; questo programma lavora usando il protocollo TCP sulla porta standard 4444,
a meno che non specificato diversamente.
Il file di log è un file di testo che viene generato dal STB ed è proprio questo che viene
usato, in maniera simile al simulatore, come strumento di debug.
74
Capitolo 4: Ambiente di sviluppo
La riga di comando con le varie opzioni e un’ esempio per inizializzare il Proxy sono:
stbproxy.exe -com <port_number>[-port <TCP port number>]
[-rate<115200,8,N,1>][-log <logfile>]
stbproxy.exe –com 3 –log c:\stb.log
Lavorando sotto Windows apparirà un’icona sulla barra degli strumenti, dove è
possibile accedere, tra le altre cose, all’opzione “svuotare” il file di log.
L’ultima cosa da fare prima di passare all’ upload, è configurare il STB, a questo ci
pensa un terzo tool chiamato “stbconfig” al quale bisogna specificare l’indirizzo IP
dell’host dove si trova il Proxy e l’eventuale numero di porta, se non si usa quella di
default; il suo scopo è quello di abilitare/disabilitare l’opzione di debug output e
security manager. La riga di comando con le varie opzioni e un’ esempio per
configurare il STB ipotizzando che il Proxy sia in locale, si usi il numero di porta TCP
di default e si attivi solo l’opzione di debug sono:
stbconfig.exe proxy_host_IP[:port] [-debug] [-security]
stbconfig.exe localhost -debug
Dopo aver effettuato la configurazione del STB si richiede il riavvio dello stesso.
Finalmente siamo arrivati alla fase finale, il Proxy è in running, il STB è configurato,
ora manca di avviare il trasferimento dell’applicazione.
Il tool stbupload richiede l’IP del proxy, il nome del description file (vedremo in seguito
cosa sia) completo di classpath, e il classpath della directory base dov’è contenuta
l’applicazione (*.class e file accessori).
La riga di comando con le varie opzioni e un’ esempio per fare l’upload di una
applicazione contenuta nella directory c:\xlet\class, supponendo il Proxy in locale con
porta di default sono:
stbupload <proxy_host[:port]> <xlets_descr_file> <pc_base_dir>
stbupload localhost c:\xlet\xlet_desciption_file c:\xlet\class
Ora l’applicazione è copiata nella directory /home del file system del STB.
75
Capitolo 4: Ambiente di sviluppo
4.3.8 Xlet description file
L’Xlet description file riflette il contenuto della tabella AIT (Application Identification
Table); contiene tutte le informazioni e i parametri utili per l’identificazione e
l’esecuzione dell’applicazione da parte del Set Top Box.
Vediamone un’ esempio:
Parametri obbligatori:
#app <Application ID> <Organisation ID>
app
0xhex_number 0xhex_number
#Application control flag: 1=autostart 2=present 3=destroy 4=kill
control 1
#service bound flag (0 or 1)
bound
0
#Basedir of application (must be relative to /home directory)
basedir "/home"
#Initial class name (fully qualified name)
class "your.company.Test"
Parametri opzionali:
#Name of application preceded by language code
name
eng "Test"
#Parameter of service on which the application should be visible to application
manager
tsid
0x7
onid
0x46
svid
0x2bd
#other flags
priority 137
visibility 3
#Classpath extension
classpath ""
#String params passed to Xlet
param = "value 0"
param = "value 1"
76
Capitolo 4: Ambiente di sviluppo
Ora cercheremo di spiegare in dettaglio i campi principali:
app
Ogni organizzazione che produce Xlet, deve essere riconosciuta, ed avere un codice
identificativo univoco, in più ogni applicazione prodotta da quell’ organizzazione deve
anch’essa essere riconoscibile univocamente.
Questo per permettere al STB di non eseguire applicazioni maligne non riconosciute.
control
Questo flag dice al STB come si deve comportare con l’applicazione che ha ricevuto:
se = 1 la manda in esecuzione appena caricata (per avvenire effettivamente
l’esecuzione, nelle impostazioni base del STB deve essere abilitata l’opzione autostart);
se = 2 il STB riceve l’applicazione è la mette disponibile nell’application manager, sarà
l’utente poi a decidere quando farla avviare (premendo app oppure OK, in base al
modello di decoder, si visualizzano le applicazioni disponibili);
se =3 viene distrutta (si usa se caricata con autostart);
se =4 viene killata (si usa se caricata con present);
bound
Questo flag dice se si tratta di una applicazione bound (1) legata al canale e alla
trasmissione o unbound (0) non legata alla trasmissione o addirittura non legata al
canale (quindi disponibile indifferentemente dal canale su cui si è sintonizzati);
basedir
campo contenente il classpath di destinazione dell’applicazione del file system nel STB
(deve essere relativo a /home);
class
deve contenere il nome della classe principale dell’applicazione (per es.:“main.class” )
name
specifica il codice della lingua usata e il nome, relativo all’applicazione, che comparirà
sull’Application Manager;
priority
nel caso in cui l’opzione “autostart” sia settata (nel menu di base del STB) e
nell’Application Manager, vi sono più di una Xlet caricata con il control flag=1, il STB
manda in esecuzione l’applicazione con la priorità più alta.
77
Capitolo 5: Implementazione di un servizio di prenotazioni
Capitolo 5
Implementazione di un servizio di prenotazioni
A questo punto avendo a disposizione un ambiente di sviluppo ed essendo a conoscenza
delle potenzialità, dei limiti e degli standard MHP, possiamo pensare di mettere in
pratica i concetti acquisiti, per implementare la nostra applicazione.
Essendo il STB un dispositivo che per alcuni aspetti può essere paragonato ad un PC
connesso in rete e volendo sfruttare il canale di ritorno, abbiamo dato uno sguardo alle
innumerevoli applicazione che sono sul web, che svariano da semplici portali
informativi fino ad arrivare a complesse applicazioni e-commerce/e-banking/e-healt.
La scelta infine è stata quella di realizzare un servizio di prenotazioni “on-line” rivolto
agli utenti del digitale terrestre, ovvero implementare una Xlet che si in grado di
effettuare una generica prenotazione.
Lo scopo di questo elaborato si basa sull’analizzare tutte le problematiche tecniche
comuni che possono produrre un insieme di servizi del genere, trovarne le soluzioni
ottimali e ovviamente verificarne le funzionalità.
Non si è data molto importanza al tipo di prenotazione, anche se per motivi realizzativi
è stato scelto un caso reale riguardante la prenotazione di “tavoli” in un generico
esercizio pubblico ad esempio.
Anticipiamo che l’applicazione gestirà una semplice anagrafica clienti, una fase di
autenticazione (log-in) per mezzo di username e password, la visualizzazione della
disponibilità del servizio, e la possibilità di prenotazione.
La scelta per quanto riguarda la gestione dei dati (anagrafica e disponibilità) a favore di
robustezza, sicurezza e velocità nel recupero delle informazioni è stata convergente
verso l’uso di un database.
78
Capitolo 5: Implementazione di un servizio di prenotazioni
5.1 Le problematiche
Le
prime
problematiche
riscontrate
riguardano
l’interfaccia
grafica,
alcuni
programmatori MHP ne paragonano la gestione a un ambiente simile a Windows
“vecchio stampo” senza l’uso del mouse. La risoluzione video disponibile, infatti, per
ora è ancora limitata (720x576) e non permette l’implementazione di sofisticati metodi
di accesso ai menù, visto e considerato anche che per la navigazione, abbiamo
solamente a disposizione il telecomando del decoder.
Un altro problema si è riscontrato nell’inserimento di testo nei campi delle varie form,
essendo abituati all’uso di tastiera e mouse non è stata così immediata la soluzione.
Le scelte potevano essere due: creare una tastiera, simile al PC, su video, navigabile con
le quattro frecce, con lo svantaggio di grosso ingombro a livello grafico e scarsa
velocità d’inserimento dei caratteri; oppure quella per cui si è optato, ovvero
l’implementazione di un ridotto tastierino a dodici pulsanti simile a quelli usati nei
telefoni cellulari,con la possibilità di sviluppare in futuro un sistema d’inserimento
simile al T9.
Per quanto riguarda la gestione del canale di ritorno, ampiamente trattata nel capitolo 3,
l’unico accorgimento è stato quello di differenziare i controlli (connesso/disconnesso) in
base al tipo di connessione utilizzata; tramite Modem o tramite Ethernet.
L’ultimo problema, e anche il più rilevante, è stato risolvere la fase di accesso a
Database tramite Xlet.
Considerando un database e alle modalità di accesso ai dati, tramite una piattaforma
Java, viene immediatamente da pensare al potente strumento offerto da Sun, il JDBC
(Java Database Connectivity).
Il fatto è però, che nelle specifiche MHP e DVB, questo strumento non è nemmeno
menzionato. Dopo diversi tentativi di forzare l’uso del JDBC, facendo l’upload su STB
di driver meno moderni e addirittura intere API compilate con JVM simile a quella
residente nel STB, si è deciso di cambiare strada.
L’idea è stata quella di realizzare una applicazione Java che funge da Proxy tra il STB,
quindi l’applicazione MHP, e il Database.
79
Capitolo 5: Implementazione di un servizio di prenotazioni
5.2 Interfaccia grafica
L’interfaccia grafica è costituita principalmente dai seguenti elementi: uno sfondo, un
menù principale costituito da tre pulsanti navigabili ciclicamente con i tasti direzionali
(su/giù), una finestra orizzontale posizionata in basso dove compare una descrizione del
sottomenù evidenziato come mostrato in figura 5.2.1.
La gestione dello sfondo è affidata ad una specifica classe GestoreSfondi, la quale viene
usata sia nella classe principale Main dal metodo startXlet(), sia ogni qualvolta servirà
di ridimensionare o eliminare il video; infatti usa i metodi di BackGroundController per
effettuare tutte le operazioni possibili sui primi due layer grafici.
La creazione della schermata principale è affidata alla classe InterfacciaGrafica che si
preoccupa di gestire per mezzo di variabili si stato, i vari componenti descritti sopra e
inoltre implementa il metodo keyPressed il quale riceve l’evento tasto premuto, ne legge
il codice corrispondente e in base allo stato chiama i metodi appropriati.
Fig. 5.2.1 - Schermata principale
80
Capitolo 5: Implementazione di un servizio di prenotazioni
Le tre scelte possibili sono: Servizi, Registrati, Prenota.
Servizi serve per dare informazioni utili al cliente sull’attività dell’esercizio pubblico,
registrati permette di registrarsi al servizio inserendo nome, cognome, numero di
telefono, username, password; prenota permette di effettuare il log-in, visualizzare la
disponibilità dei tavoli (verde libero, rosso occupato) e l’eventuale prenotazione.
In questa fase si usa un delle caratteristiche principali di Java, il polimorfismo, infatti
ogni opzione delle tre è implementata da una classe omonima Servizi, Registra, Prenota
e tutte estendono la classe Strumenti.
Nella classe Strumenti infatti sono contenuti i metodi comuni a tutte le opzioni come
init, stop, mentre il metodo start e implementato diversamente per ogni strumento.
Strumenti
Init()
Stop()
Servizi
Registra
Prenota
Start()
Start()
Start()
Il metodo init() provvede ad attivare il tastierino, stop() lo rimuove, start() in base al
servizio crea i campi per l’inserimento dati della corrispettiva form.
L’implementazione della tastiera è contenuta nelle classi KeyPad e KeypadButton
mentre i campi di testo da EditableTextBox.
Sia entrando nel menù “Registrati” (figura 5.2.2) che “Prenotazioni” è possibile scorrere
ciclicamente i campi di testo con le frecce (su/giù), premendo “ok” si inizia la
compilazione del campo evidenziato, per confermare tutti i campi inseriti bisogna
premere il tasto rosso, mentre per annullare l’inserimento e tornare al menù principale
quello blu. In caso di username o password errati (figura 5.2.3) o già esistenti (nel caso
di prima registrazione) comparirà un messaggio informativo per l’utente, realizzato dal
metodo messFlash; per continuare si dovrà premere il tasto verde.
81
Capitolo 5: Implementazione di un servizio di prenotazioni
Fig. 5.2.2 - Form Registrati
Fig. 5.2.3 - Form Prenotazioni
82
Capitolo 5: Implementazione di un servizio di prenotazioni
Una volta effettuato correttamente il log-in viene visualizzata la disponibilità dei tavoli,
(usando il metodo visualizzaDisp nella classe Prenota) come mostrato nella figura
5.2.4; ora con le frecce direzionali (destra/sinistra) è possibile selezionare il tavolo
desiderato, e premendo “ok” prenotarlo, sempre se non sia già occupato (colore rosso).
Fig. 5.2.4 Disponibilità tavoli
Per quanto riguarda la parte grafica c’è da aggiungere che si è cercato di mantenere
sempre presente il video layer opportunamente ridimensionato e posizionato, ad
esclusione delle fasi di inserimento dati.
Il codice di questa parte di applicazione è disponibile nell’appendice A suddivisa per
classi.
83
Capitolo 5: Implementazione di un servizio di prenotazioni
5.3 Accesso ai dati
Come accennato all’inizio del capitolo per la gestione dei dati si usa un DBMS Mysql
scaricabile dal sito www.mysql.com. Nel nostro ambiente di sviluppo sia il Database
che l’applicazione Proxy risiedevano sullo stesso PC (con Java 2 Platform Standard
Edition ver. 1.4.2) connesso in rete LAN; sono state anche effettuate prove lavorando
con DB e Proxy remoti. La comunicazione tra Xlet e Proxy avviene tramite Socket
utilizzando le API java.net e java.io, mentre tra Proxy e Database si sfruttano appieno le
potenzialità del JDBC.
Xlet
Proxy
Socket
DBMS
JDBC
Le potenzialità di adottare questo meccanismo sono molteplici:
•
la comunicazione tra xlet e proxy è più sicura in quanto le socket supportano il
TSL e SSL;
•
la Xlet fungerà solo da interfaccia grafica e penserà a gestire la connessione con
il Proxy;
•
la Xlet in questo modo viene alleggerita dalle funzioni d’interrogazione
database, può richiedere complicate query con pochi byte comunicando con il
Proxy, al quale viene lasciato tutto il lavoro (mediante messaggi standard o
codici proprietari comuni a entrambi);
•
il Proxy risiederà su un server con adeguate caratteristiche tecniche è avrà a
disposizione tutte le API comprese java.sql e com.java.jdbc;
•
il Proxy se implementato con un sistema multithread sarà capace di gestire più
richieste contemporanee e l’accesso dei dati in concorrenza;
•
pensando a un centro servizi dove lo stesso Database viene usato da diverse
utenze:operatori che accedono da terminali remoti, utenti via web, utenti del
digitale terrestre; il Proxy può fungere da interfaccia per garantire la coerenza
dei dati.
84
Capitolo 5: Implementazione di un servizio di prenotazioni
5.3.1 Socket
Una socket rappresenta il terminale (end-point) di un canale di comunicazione
bidirezionale. Permette a due processi, residenti sulla stessa macchina o anche molto
distanti, di comunicare fra loro nello stesso modo.
Si basano tipicamente sul modello client/server:
•
il servitore deve stare in attesa di possibili comunicazioni in arrivo (ente
passivo), nel nostro caso è il Proxy
•
i clienti (anche più di uno), nel nostro caso le Xlet, quando vogliono,
comunicano con il servitore (sono enti attivi).
Il protocollo usato è il TCP, il servitore crea la sua ServerSocket con un numero noto di
porta e si mette in attesa.
Un cliente, quando vuole comunicare col servitore, crea la sua Socket specificando con
chi vuole parlare:
• nome dell’host
• numero di porta
Questo fa scattare il protocollo di 3-way handshaking per la connessione.
Il servitore accetta la richiesta dal cliente: con ciò si stabilisce una connessione e il
server crea una nuova Socket già collegata al cliente, tramite la quale i due comunicano.
A questo punto i processi possono parlare senza più bisogno di specificare ogni volta
con chi si vuol parlare (come nelle telefonate: una volta presa la linea, tutti i segnali
transitano automaticamente tra le due parti) come mostrato in figura 5.3.1.1.
Fig. 5.3.1.1 - Comunicazione via Socket stream
85
Capitolo 5: Implementazione di un servizio di prenotazioni
Vediamo la classe Socket nel package java.net.
Alla Socket sono associati due stream:
1. uno dal cliente verso il servitore
2. uno dal servitore verso il cliente.
La comunicazione cliente/servitore è bidirezionale. I ruoli “cliente” e “servitore” sono
tali solo nella fase iniziale, quando si instaura la connessione. Una volta connessi, i due
processi si parlano reciprocamente “alla pari”.
Costruire una Socket significa aprire la comunicazione verso l’altra parte; “public
Socket(String remoteHost, int remotePort)” crea una socket stream e la collega alla
porta specificata della macchina remota corrispondente al nome dato.
ESEMPIO
Socket s = new Socket(“univpm.it”,13);
oppure
String str = “prova.com”; int port = 14;
Socket s = new Socket(ss,port);
Non bisogna specificare il nome dell’host locale, è implicito.
Per il client, non bisogna specificare il numero della porta (non deve essere noto a
priori). Il sistema assegna automaticamente un numero di porta tra quelli liberi.
Il servizio di DNS è invocato automaticamente.
Si può fare anche:
“public Socket(InetAddress remoteAddr, int remotePort)”
Crea una socket stream e la collega alla porta specificata della
macchina remota corrispondente all’indirizzo IP dato.
Vediamo ora la classe InitAddress del package java.net.
Gli oggetti della classe InetAddress rappresentano indirizzi internet (IP).
La classe InetAddress fornisce dei metodi per gestire facilmente gli indirizzi Internet.
• public static InetAddress getByName(String host) - determina l’indirizzo IP di host.
86
Capitolo 5: Implementazione di un servizio di prenotazioni
ESEMPI
InetAddress.getByName(null);
InetAddress.getByName(“localhost”);
InetAddress.getByName(“127.0.0.1”);
Una volta creata la socket per comunicare: si recuperano dalla socket i due stream (di
ingresso e di uscita) tramite i metodi della classe Socket:
• public InputStream getInputStream()
- restituisce lo stream di input da cui leggere i dati (byte) che giungono dall’altra parte.
• public OutputStream getOutputStream()
- restituisce lo stream di output su cui scrivere i dati (byte) da inviare all’altra parte.
Quindi su questi stream si può leggere / scrivere come su qualunque altro stream di
byte.
Per chiudere la comunicazione: si chiude la socket col metodo:
• public synchronized void close()
- chiude la connessione e libera la risorsa.
Di seguito riportiamo un esempio di un semplice client e di un server multithread:
87
Capitolo 5: Implementazione di un servizio di prenotazioni
//Simple Client
import java.net.*;
import java.io.*;
public class SimpleClient
{
public static void main(String args[])
{
try {
// Apre una connessione verso un server in ascolto sulla porta 7777. In questo caso utilizziamo localhost
// che corrisponde all'indirizzo IP 127.0.0.1
System.out.println("Apertura connessione...");
Socket s1 = new Socket ("127.0.0.1", 7777);
// Ricava lo stream di input dal socket s1 ed utilizza un oggetto wrapper di classe BufferedReader
// per semplificare le operazioni di lettura
InputStream is = s1.getInputStream();
BufferedReader dis = new BufferedReader(new InputStreamReader(is));
// Legge l'input e lo visualizza sullo schermo
System.out.println("Risposta del server: " + dis.readLine());
// Al termine, chiude lo stream di comunicazione e il socket.
dis.close();
s1.close();
System.out.println("Chiusura connessione effettuata");
}catch (ConnectException connExc){
System.err.println("Errore nella connessione ");
}catch (IOException ex){
ex.printStackTrace();
}
}
}
//Server multithread
import java.net.*;
import java.io.*;
public class SimpleServer
{
private int port;
private ServerSocket server;
private Socket client;
public SimpleServer (int port) {
this.port = port;
if(!startServer()) System.err.println("Errore durate la creazione del Server");
}
private boolean startServer(){
try{
server = new ServerSocket(port);
}catch (IOException ex){
ex.printStackTrace();
return false;
}
System.out.println("Server creato con successo!");
return true;
}
88
Capitolo 5: Implementazione di un servizio di prenotazioni
public void runServer()
{
while (true){
try{
// Il server resta in attesa di una richiesta
System.out.println("Server in attesa di richieste...");
client = server.accept();
System.out.println("Un client si e' connesso...");
ParallelServer pServer = new ParallelServer(client);
Thread t = new Thread (pServer);
t.start();
}catch (IOException ex){
ex.printStackTrace();
}
}
}
public class ParallelServer implements Runnable
{
private Socket client;
public ParallelServer (Socket client) {
this.client = client;
}
public void run(){
try{
// Ricava lo stream di output associate al socket e definisce una classe wrapper di tipo
// BufferedWriter per semplificare le operazioni di scrittura
OutputStream s1out = client.getOutputStream();
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(s1out));
// Il server invia la risposta al client
bw.write("Benvenuto sul server!\n");
// Chiude lo stream di output e la connessione
bw.close();
client.close();
System.out.println("Chiusura connessione effettuata");
}catch (IOException ex){
ex.printStackTrace();
}catch (Exception e){
e.printStackTrace();
}
}
}
public static void main (String args[])
{
SimpleServer ss = new SimpleServer(7777);
ss.runServer();
}
}
89
Capitolo 5: Implementazione di un servizio di prenotazioni
La Xlet con il Proxy si scambiano oggetti usando gli input e output stream.
Gli oggetti sono di tre tipi, implementati dalle classi: ObjectPrenotation,
ObjectRegistration, ObjectTavoli.
Ogni oggetto contiene la struttura dati che rappresenta, coerenti con le relative tabelle
del database, dove sono realmente memorizzati, e i metodi per renderli disponibili
all’applicazione.
Il codice della Xlet (lato client) è contenuto nell’appendice A le classi di interesse sono
Comunica che ha come parametri IP del Proxy e numero della porta, PrenotaSingolo.
Il codice dell’applicazione Proxy (lato server), nel nostro caso si usa un singolo thread,
è contenuto nell’appendice B, dove si vede che oltre alla gestione della socket gestisce
anche il recupero dati dal DB e l’invio degli stessi alla Xlet. La classe di interesse è
SimpleServer; in più vi sono i .java dei tre oggetti di cui sopra.
90
Capitolo 5: Implementazione di un servizio di prenotazioni
5.3.2 JDBC
Per effettuare l’interrogazione del Database da parte del nostro Proxy si usa il JDBC
(Java Database Connectivity), è la parte delle API di J2SE che fornisce le primitive per
la connessione a basi di dati relazionali e si basa su due semplici concetti:
•
si inviano comandi SQL;
•
si recuperano i risultati dei comandi.
Il package a cui faremo riferimento è java.sql.
Ogni DB ha una sua API e un suo protocollo (implementato dal driver) particolare.
JDBC astrae dalle particolarità a basso livello delle API e dei protocolli, fornendo
un’interfaccia comune. I driver sottostanti si preoccupano poi di effettuare la traduzione
dei comandi SQL nelle interfacce native dei database supportati come mostrato in figura
5.3.2.1.
L’accesso al database può essere locale o remoto.
Fig. 5.3.2.1 - Schema JDBC
91
Capitolo 5: Implementazione di un servizio di prenotazioni
I driver JDBC sono divisi in 4 Classi:
•
Classe 1: sono i driver JDBC-ODBC.
•
Classe 2: sono i driver che si aspettano di trovare sulla macchina su cui sono
utilizzati uno strato di software, scritto in linguaggio nativo per quella
Macchina/Piattaforma, i quali si preoccupano di connettersi e di operare sul
Database. (nel caso di Oracle i driver in questione vengono chiamati OCI,
Oracle callback Interface, e vogliono che sulla macchina su cui girano ci sia
installato SQL-Net)
•
Classe 3: sono i driver che si aspettano di trovare un Gateway, o in generale un
server a cui connettersi il quale provvederà a ritornare una connessione dal
Database. (i JDBC-RMI driver sono di classe 3)
•
Classe 4: sono i driver totalmente scritti in Java ed autonomi. (Nel caso di
Oracle vengono chiamati THIN).
Vediamo le procedure principali e i metodi che le implementano per effettuare la
connessione ed utilizzare un database:
•Caricare il gestore di driver
DriverManager
•Aprire la connessione
Connection
•Creare un oggetto istruzione
Statement
•Eseguire l’interrogazione (query)
executeQuery
•Elaborare i risultati
ResultSet
•Chiudere la connessione
close
Drive manager implementa un gestore di driver. Il metodo (statico) per ottenere una
connessione al DB è:staticConnectiongetConnection(String,String,String) che prende
tre argomenti: URL, username, password.
Sintassi dell’URL:–jdbc:mysql://<host>[:<port>]/<dbname>
Siccome le connessioni occupano risorse, in un ambiente multiutente e multitasking è
opportuno adottare la seguente politica: aprire una connessione solo quando necessario,
assicurarsi di chiuderla, non aprire/chiudere connessioni inutilmente.
92
Capitolo 5: Implementazione di un servizio di prenotazioni
Connection rappresenta una connessione al DB. Prima di fare qualunque cosa con un
db, devo stabilire una connessione. Ad esempio, per creare un comando, devo disporre
di una connessione (c): Statement s = c.createStatement().
Per chiudere la connessione, si utilizza il metodo close().
Statement rappresenta un’istruzione/comando. Ha i metodi sia per eseguire
un’interrogazione (query) SQL che restituisca un insieme di dati che per eseguire una
query di aggiornamento/modifica del DB:
ResultSet executeQuery(String) int executeUpdate(String).
Quando non serve più va chiuso con il metodo: close
ResultSet rappresenta il risultato di un’interrogazione.
Tramite il metodo next() possiamo spostarci da un record al successivo: boolean next().
Abbiamo a disposizione una serie di metodi per recuperare i dati memorizzati nei vari
campi di ogni record, in base al tipo di dato originario del DB:String getString(int)[gli
indici partono da 1!], getXxx()[Byte,Boolean, Blob, …]. E’ buona norma chiudere il
ResultSet quando non serve più con il metodo: close.
I Driver usati nel Proxy sono “mysql-connector-java-3.1.12”, di clase 4, da ricordare di
importare l’omonimo .jar nel progetto.
Il codice riguardante l’uso dei JDBC nel nostro Proxy è riportato nell’appendice B; le
classi d’interesse sono: ServerPrenotaSingolo, ServerPrenotation, ServerRegistration.
93
Capitolo 5: Implementazione di un servizio di prenotazioni
5.3.3 Database
Il DBMS usato in questo progetto per la precisione è MySQL Server versione 5.0.
Le operazioni svolte direttamente su DB sono solamente la creazione di due tabelle: una
chiamata “cliente” contenente una ridotta anagrafica dell’utente che si andrà a registrare
e l’altra “tavoli” che serve per memorizzare la disponibilità corrente del servizio;
contenente un codice identificativo del tavolo, lo username dell’utente che lo andrà a
prenotare e un campo flag (libero/occupato).
Per completezza riportiamo il DLL:
CREATE TABLE `cliente` (
`user` char(20) NOT NULL default '',
`password` char(20) NOT NULL default '',
`nome` char(20) default NULL,
`cognome` char(20) default NULL,
`telefono` char(15) default NULL,
PRIMARY KEY (`user`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `tavoli` (
`num_tavolo` int(3) NOT NULL default '0',
`utente` char(20) default NULL,
`libero` int(1) default NULL,
PRIMARY KEY (`num_tavolo`),
UNIQUE KEY `IDtav` USING BTREE (`num_tavolo`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
94
Capitolo 5: Implementazione di un servizio di prenotazioni
5.4 Sviluppi futuri
All’inizio del capitolo si è anticipato che il lavoro svolto è stato basato più sull’
ingegnerizzazione del software per permettere di effettuare una prenotazione “on-line”
generica da Xlet, che sul servizio in sé.
Il progetto, così come è descritto nei paragrafi precedenti, è stato implementato e testato
su Set Top Box da sviluppo ed è risultato essere funzionante.
Gli sviluppi possibili, partendo da questa piattaforma, sono innanzitutto basare
l’attenzione su un servizio specifico e quindi curare di più la parte grafica e fare
un’analisi dei dati ad hoc; dal lato tecnico si potrà sviluppare una procedura per
velocizzare l’inserimento dati nei campi di testo, implementare il Proxy in multithread
per gestire l’accesso in concorrenza di più utenti e magari implementare l’uso di lettore
smart card da usare per l’identificazione dell’utente tramite carta d’identità elettronica
per esempio.
95
Conclusioni
Conclusioni
Attualmente l'Italia, come raramente accade per il nostro paese si trova in una posizione
di avanguardia e per molti aspetti sta guidando a livello europeo questa rivoluzione,
soprattutto in ambito Televisione Digitale Terrestre. In commercio possiamo trovare
diverse marche di decoder interattivi MHP e tutti i principali broadcaster sono
impegnati a scoprire le potenzialità, i limiti e le possibili fonti di guadagno di questa
nuova tecnologia. Difficile fare pronostici soprattutto in merito alla legge del nostro
parlamento che sancisce entro dicembre 2008 il cosiddetto "switch-off" delle
trasmissioni analogiche per passare esclusivamente al sistema digitale. Difficile anche
capire se un maggior numero di canali implicherà una maggior scelta e auspicabilmente
maggior qualità per noi telespettatori. Le certezze maggiori rimangono quindi quelle
tecnologiche legate ad uno standard europeo aperto che mira alla convergenza del
mondo terrestre, cavo e satellite, dove invece oggi regnano sovrane troppe tecnologie e
standard proprietari. Siamo di fronte ad una tecnologia in grado di abilitare una delle più
profonde trasformazioni che si possa pensare in una società fortemente fondata
sull'immagine e sulla comunicazione come è la nostra. Un trasformazione che con ogni
probabilità non si limiterà ad influenzare l'ambiente casalingo, ma si estenderà
rapidamente anche ai terminali mobili e tascabili, il DVB-H e le sue applicazioni.
96
APPENDICE A: Codice Xlet
APPENDICE A
Codice Xlet
Main.java
import javax.tv.xlet.Xlet;
import javax.tv.xlet.XletContext;
import javax.tv.xlet.XletStateChangeException;
import org.havi.ui.*;
import org.havi.ui.event.*;
import java.awt.event.*;
//Classe iniziale è questa che implementa l'interfaccia Xlet e KeyListener e che quindi sarà
//utilizzata dall'application manager per gestire il
//ciclo di vita della xlet. Si occupa di inizializzare
//i vari componenti, ascoltare gli eventi dal telecomando,
//creare l'oggetto scene fondamentale per la parte grafica.
public class Main implements Xlet, KeyListener {
public XletContext context;
public static GestoreSfondi gestoreSfondi;
public HScene scene;
public int tastoPremuto = 0;
private InterfacciaGrafica interfaccia;
//Costruttore vuoto.
public Main() {
}
// Definisco un metodo che inizializza la Xlet, e i suoi componenti.
public void initXlet(XletContext xletContext) throws XletStateChangeException {
//xletContext utilizzato per la gestione del ciclo di vita della xlet
context = xletContext;
HSceneFactory hsceneFactory = HSceneFactory.getInstance();
//estraggo l'oggetto scene su cui si appoggiano i componenti grafici.
scene =
hsceneFactory.getFullScreenScene(HScreen.getDefaultHScreen().getDefaultHGraphicsDevice());
//le dimensioni sono quelle standard di risoluzione di un televisore analogico.
scene.setSize(720, 576);
scene.setVisible(false);
scene.setLayout(null);
//aggiungo l'ascoltatore degli eventi in questo caso
//la classe stessa.
scene.addKeyListener((KeyListener)this);
}
// Definisco il metodo che permette di mandare in esecuzione la Xlet
public void startXlet() throws XletStateChangeException {
System.out.println("Eseguo startXlet");
gestoreSfondi = new GestoreSfondi(context);
interfaccia = new InterfacciaGrafica(scene);
97
APPENDICE A: Codice Xlet
// Imposto lo sfondo e ridimensiono il video
gestoreSfondi.displayBackground("background.mpg",240,100,420,320);
// Inizializzo l'interfaccia grafica
interfaccia.disegnaInit();
// Richiedo il Focus per la scene corrente
scene.requestFocus();
scene.setVisible(true);
}
// Definisco il metodo per mettere in pausa la Xlet
public void pauseXlet() {
System.out.println("Xlet in pausa");
context.notifyPaused();
}
// Definisco il metodo che distrugge la Xlet e rilascia le risorse.
public void destroyXlet(boolean flag) throws XletStateChangeException {
if(flag){
System.out.println("Distruggi Xlet");
/*
try{
RCconnector.Disconnect();
}catch (IOException e){
e.printStackTrace();
}
*/
interfaccia.distruggi();
//rimuovo l'ascoltatore degli eventi.
scene.removeKeyListener(this);
scene.removeKeyListener((KeyListener)this);
scene.setVisible(false);
HSceneFactory.getInstance().dispose(scene);
scene = null;
gestoreSfondi.displayBackgroundExit();
context.notifyDestroyed();
}
}
// Ascoltatore degli eventi legati al telecomando. Gli eventi vengono
//passati alla classe InterfacciaGrafica che poi li passerà alle
//funzioni attive.
public void keyPressed (KeyEvent key) {
int pulsantePremuto = key.getKeyCode();
switch(pulsantePremuto){
//Tastierino numerico
case KeyEvent.VK_0:
case KeyEvent.VK_1:
case KeyEvent.VK_2:
case KeyEvent.VK_3:
case KeyEvent.VK_4:
case KeyEvent.VK_5:
case KeyEvent.VK_6:
case KeyEvent.VK_7:
case KeyEvent.VK_8:
case KeyEvent.VK_9:
98
APPENDICE A: Codice Xlet
//Tasti colorati
case HRcEvent.VK_COLORED_KEY_0:
//rosso
case HRcEvent.VK_COLORED_KEY_1:
//verde
case HRcEvent.VK_COLORED_KEY_2:
//giallo
case HRcEvent.VK_COLORED_KEY_3:
//blu
//Tasti direzionali
case HRcEvent.VK_UP:
case HRcEvent.VK_DOWN:
case HRcEvent.VK_RIGHT:
case HRcEvent.VK_LEFT:
case HRcEvent.VK_ENTER:
//OK
//Vengono passati gli eventi.
interfaccia.keyPressed(key);
break;
//Alla pressione del tasto EXIT del telecomando si
//richiama il metodo destroyXlet, per fermare la xlet.
case HRcEvent.VK_ESCAPE:
try{
destroyXlet(true);
}
catch (XletStateChangeException xsce){
System.out.println("Premuto tasto EXIT "+xsce.toString());
}
break;
default:
break;
}
}
public void keyTyped(KeyEvent ignored) { }
public void keyReleased(KeyEvent ignored) { }
}
InterfacciaGrafica.java
import java.awt.*;
import java.awt.event.*;
//import java.io.IOException;
//import javax.tv.xlet.XletStateChangeException;
import org.havi.ui.*;
import org.havi.ui.event.HRcEvent;
//import javax.tv.xlet.XletContext;
//Questa classe amministra la parte grafica del frame
//principale gestendo l'accesso alle varie funzionalità e
//passando gli eventi ai processi attivi.
public class InterfacciaGrafica {
public static final int MENU=0;
public static final int PRIMO=1;
public static final int DISP=2;
public static final int POPUP=3;
public static final int INIT=4;
99
APPENDICE A: Codice Xlet
public static int STATO;
private HScene scene;
private HIcon [] vetON,vetOFF;
private HText [] mess;
private String como;
private String [] str;
private HIcon tastor;
private GestoreSfondi gestoreSfondi;
private Registra registra;
private Servizi servizi;
private Prenota prenota;
private int tasto=0;
//Utilizzata per il polimorfismo.
private Strumenti strumento=null;
//Costruttore.
public InterfacciaGrafica(HScene scenemain){
//viene passato l'oggetto scene su cui verranno
//aggiunti i vari componenti grafici.
scene=scenemain;
//STATO=INIT;
}
//Crea i componenti grafici e li visualizza.
public void disegnaInit(){
STATO=MENU;
Font font1=new Font("Arial",Font.BOLD,30);
str =new String[3];
str[0]="Questo è un servizio di prenotazioni MHP";
str[1]="Inserisci tutti i campi richiesti";
str[2]="Fai il login ed effettua la prenotazione";
como=str[1];
mess= new HText[3];
for(int i=0;i<3;i++){
mess[i]=new HText(str[i]);
mess[i].setFont(font1);
mess[i].setForeground(Color.white);
mess[i].setBackgroundMode(HVisible.BACKGROUND_FILL);
mess[i].setBackground(new Color(100,100,100,120));
mess[i].setBounds(0,456,720,100);
mess[i].setBordersEnabled(true);
scene.add(mess[i]);
mess[i].setVisible(false);
}
System.out.println("Entrato in disegnainit:mess ok");
vetON = new HIcon[3];
vetOFF = new HIcon[3];
vetOFF[0]=new HIcon(Toolkit.getDefaultToolkit().getImage("servizi_off.jpg"), 50, 100, 140, 30);
vetOFF[1] = new HIcon(Toolkit.getDefaultToolkit().getImage("registra_off.jpg"), 50, 135, 140, 30);
vetOFF[2] = new HIcon(Toolkit.getDefaultToolkit().getImage("prenota_off.jpg"), 50, 170, 140, 30);
vetON[0] = new HIcon(Toolkit.getDefaultToolkit().getImage("servizi_on.jpg"), 50, 100, 140, 30);
vetON[1] = new HIcon(Toolkit.getDefaultToolkit().getImage("registra_on.jpg"), 50, 135, 140, 30);
vetON[2] = new HIcon(Toolkit.getDefaultToolkit().getImage("prenota_on.jpg"), 50, 170, 140, 30);
for(int i=0;i<3;i++){
scene.add(vetON[i]);
scene.add(vetOFF[i]);
100
APPENDICE A: Codice Xlet
vetOFF[i].setVisible(true);
vetON[i].setVisible(false);
}
System.out.println("Entrato in disegnainit:icone ok");
//Istanzio gli oggetti che implementano le funzioni
//della xlet.
prenota=new Prenota(scene);
System.out.println("pre ok");
servizi=new Servizi(scene);
System.out.println("serv ok");
registra=new Registra(scene);
System.out.println("Entrato in disegnainit:strumenti ok");
accendi(tasto);
scene.setVisible(true);
scene.requestFocus();
System.out.println("fine disegnainit");
}
//Gestione grafica dei pulsanti
public void accendi (int numero){
vetOFF[numero].setVisible(false);
vetON[numero].setVisible(true);
mess[numero].setVisible(true);
scene.repaint();
}
public void spegni (int numero){
vetOFF[numero].setVisible(true);
vetON[numero].setVisible(false);
mess[numero].setVisible(false);
scene.repaint();
}
//Viene oscurata la parte grafica e
//si ferma il processo attivo.
//Richiamato del metodo destroyXlet nel momento di
//distruzione della xlet.
public void distruggi(){
this.scene.setVisible(false);
//strumento.stop();
}
public static void premutoEnter(){
}
//Ascoltatore degli eventi del telecomando.
//La gestione delle funzionalità è stata fatta utilizzando le
//interfacce Java questo permette di gestire i processi in maniera dinamica,
//passando gli eventi solo al processo attivo.
public void keyPressed (KeyEvent key) {
int pulsantePremuto = key.getKeyCode();
switch(pulsantePremuto){
case KeyEvent.VK_0:
101
APPENDICE A: Codice Xlet
case KeyEvent.VK_1:
case KeyEvent.VK_2:
case KeyEvent.VK_3:
case KeyEvent.VK_4:
case KeyEvent.VK_5:
case KeyEvent.VK_6:
case KeyEvent.VK_7:
case KeyEvent.VK_8:
case KeyEvent.VK_9:
case HRcEvent.VK_COLORED_KEY_0:
System.out.println("Rosso");
if(STATO==POPUP) break;
if(STATO==INIT){
System.out.println("Passa a menu");
STATO=MENU;
disegnaInit();
break;
}
case HRcEvent.VK_COLORED_KEY_1:
if(STATO==POPUP){
Strumenti.messFlashOff();
break;
}
case HRcEvent.VK_COLORED_KEY_2:
if(STATO==POPUP) break;
if(STATO==PRIMO){
strumento.passaEvento(key);
break;
}
case HRcEvent.VK_COLORED_KEY_3:
if(STATO==POPUP) break;
if(STATO==PRIMO){
strumento.Stop();
mess[tasto].setVisible(true);
//Main.gestoreSfondi.displayBackgroundExit();
Main.gestoreSfondi.displayBackground("background.mpg",240,100,420,320);
scene.repaint();
STATO=MENU;
break;
}
if(STATO==DISP){
Prenota.stopVisu();
mess[tasto].setVisible(true);
//Main.gestoreSfondi.displayBackgroundExit();
Main.gestoreSfondi.displayBackground("background.mpg",240,100,420,320);
scene.repaint();
STATO=MENU;
break;
}
case HRcEvent.VK_RIGHT:
if(STATO==POPUP) break;
System.out.println("stato init"+InterfacciaGrafica.STATO);
System.out.println("Init:Destro");
102
APPENDICE A: Codice Xlet
if(STATO==DISP){
strumento.passaEvento(key);
break;
}
case HRcEvent.VK_LEFT:
if(STATO==POPUP) break;
if(STATO==DISP){
strumento.passaEvento(key);
break;
}
if(STATO==MENU)break;
case HRcEvent.VK_UP:
if(STATO==POPUP) break;
if(STATO==MENU){
spegni(tasto);
if(tasto==0) tasto=2;
else tasto--;
accendi(tasto);
break;
}
case HRcEvent.VK_DOWN:
if(STATO==POPUP) break;
if(STATO==MENU){
spegni(tasto);
if(tasto==2)tasto=0;
else tasto++;
accendi(tasto);
break;
}
case HRcEvent.VK_ENTER:
if(STATO==POPUP) break;
if(STATO==MENU){
if(tasto==0)break;
mess[tasto].setVisible(false);
Main.gestoreSfondi.displayBackgroundExit();
Main.gestoreSfondi.displayBackground("background.mpg",0,0,0,0);
scene.repaint();
/*if(tasto==0){
strumento=servizi;
}*/
if(tasto==1){
strumento=registra;
}
if(tasto==2){
strumento=prenota;
}
strumento.init();
strumento.Start();
break;
}
strumento.passaEvento(key);
default:
break;
}
}
103
APPENDICE A: Codice Xlet
}
GestoreSfondi.java
import javax.tv.xlet.XletContext;
//Gestisce gli sfondi della xlet.
//Utilizzato dalla classe principale "Main". I metodi si appoggiano alla
//classe BackgroundController per modificare l'immagine in background.
public class GestoreSfondi {
//Context della xlet in esecuzione.
private XletContext xContext;
private BackgroundController backgroundManager;
public GestoreSfondi(XletContext context){
backgroundManager = new BackgroundController();
xContext=context;
}
//
Carica l'immagine di sfondo della xlet, oscurando il video.
public void displayBackground(String back) {
if (backgroundManager.init()) {
backgroundManager.hideVideo(xContext);
backgroundManager.display(back);
}
}
// Carica l'immagine di sfondo ridimensionando il video.
public void displayBackground(String back,int x,int y,int l,int h) {
if (backgroundManager.init()) {
backgroundManager.resizeVideo(xContext,x,y,l,h);
backgroundManager.display(back);
}
}
// Rilascia la risorsa Background.
public void displayBackgroundExit() {
backgroundManager.dispose();
}
}
Keypad.java
import java.awt.*;
import java.awt.event.*;
public class Keypad extends Container implements Runnable {
private class NewKey {
private int nk = -1;
public NewKey() { }
public void set(int i) { nk = i; }
public int get() { return nk; }
}
104
APPENDICE A: Codice Xlet
// Keypad size constants, don't change.
private static final int width = 500;
private static final int height = 237;
private String editedText = null;
private char pendingChar = 0;
// Used for keypad output, set by setOutput method
private EditableTextBox textBox = null;
// Images of buttons
private Image redButtonImage = null;
private Image blueButtonImage = null;
private Image okButtonImage = null;
public Keypad() {
setSize(width,height);
// Load button images
redButtonImage = Toolkit.getDefaultToolkit().getImage("Rosso_Off.gif");
blueButtonImage = Toolkit.getDefaultToolkit().getImage("Blu_Off.gif");
// Create numeric buttons and layout
buttons = new KeypadButton[10];
buttons[0] = new KeypadButton(0,"0_-");
buttons[1] = new KeypadButton(1,"1@,;.");
buttons[2] = new KeypadButton(2,"abc2äå");
buttons[3] = new KeypadButton(3,"def3");
buttons[4] = new KeypadButton(4,"ghi4");
buttons[5] = new KeypadButton(5,"jkl5");
buttons[6] = new KeypadButton(6,"mno6ö");
buttons[7] = new KeypadButton(7,"pqrs7");
buttons[8] = new KeypadButton(8,"tuv8");
buttons[9] = new KeypadButton(9,"wxyz9");
buttons[1].setLocation(259,5);
buttons[2].setLocation(339,5);
buttons[3].setLocation(419,5);
buttons[4].setLocation(259,62);
buttons[5].setLocation(339,62);
buttons[6].setLocation(419,62);
buttons[7].setLocation(259,119);
buttons[8].setLocation(339,119);
buttons[9].setLocation(419,119);
buttons[0].setLocation(339,176);
for (int i=0;i<10;i++) {
buttons[i].setVisible(true);
add(buttons[i]);
}
newKey = new NewKey();
}
public void paint(Graphics g) {
// Paint keypad border
//g.setColor(Color.black);
//g.fillRect(254,0,245,height);
// Paint keypad background
g.setColor(new Color(100,100,100,120));
g.fillRect(0,0,width,height);
//g.fillRect(256,2,241,height-4);
// Paint backspace button
105
APPENDICE A: Codice Xlet
//g.drawImage(redButtonImage,10,40,this);
g.setColor(Color.white);
g.setFont(new Font("Tireasias",Font.PLAIN,23));
g.drawString("Su/Giù :Seleziona campo",10,20);
g.drawString("OK :Inserimento",10,50);
g.drawString("< :Backspace",10,80);
g.drawString("> :Space",10,110);
g.setFont(new Font("Tireasias",Font.BOLD,23));
g.setColor(Color.red);
g.drawString("CONFERMA DATI",10,140);
g.setColor(Color.blue);
g.drawString("ANNULLA",10,170);
// Paint space button
//g.drawImage(blueButtonImage,10,90,this);
//g.drawString("= space",90,121);
// Paint components (keys)
super.paint(g);
}
// activeChar is the character in editText which is edited at the moment
int activeChar = 0;
private int numKeyActive = -1; // -1 = no key
private char chosenChar = 0; // 0 = no character
private int timer = 0;
boolean backSpaceSet = false; // backspace has been pressed?
boolean spaceSet = false; // space has been pressed?
private NewKey newKey = null;
private KeypadButton[] buttons;
// Main loop of the thread.
public void run() {
int i=0;
while (true) {
// Sleep for a while so that the CPU won't be too busy.
try { Thread.sleep(100); } catch (Exception e) { System.out.println(e); }
// newKey is shared by two thread, allow only one access at a time
synchronized (newKey) {
if (newKey.get() != -1) {
if (numKeyActive == -1) {
// New key pressed
numKeyActive = newKey.get();
buttons[numKeyActive].press();
textBox.setPending(buttons[numKeyActive].getChar());
newKey.set(-1);
} else {
if (numKeyActive == newKey.get()) {
// Old key re-pressed.
buttons[numKeyActive].press();
textBox.setPending(buttons[numKeyActive].getChar());
newKey.set(-1);
} else {
// New key pressed while old still active
106
APPENDICE A: Codice Xlet
System.out.println("Chosen: "+buttons[numKeyActive].getChar());
textBox.addPending();
buttons[numKeyActive].unpress();
numKeyActive = newKey.get();
buttons[numKeyActive].press();
textBox.setPending(buttons[numKeyActive].getChar());
newKey.set(-1);
}
}
// Every key press sets the timer to 10
timer = 10;
} else {
// If no key press, the timer is decremented.
timer--;
}
if (timer==0) {
// If timer reaches 0 it means that it has gone 10x100ms=1 second since
// the active key was pressed. The active key is now added to the edited
// string (in the text box).
System.out.println("Chosen: "+buttons[numKeyActive].getChar());
textBox.addPending();
buttons[numKeyActive].unpress();
numKeyActive = -1;
}
// If a key is active, backspace always inactivates it.
if (backSpaceSet) {
textBox.backSpace();
if (numKeyActive != -1) {
buttons[numKeyActive].unpress();
numKeyActive = -1;
}
timer = -1;
backSpaceSet = false;
}
// A space is always added to the edited string. If a key is active
// when space is pressed, it get inactivated.
if (spaceSet) {
textBox.space();
if (numKeyActive != -1) {
buttons[numKeyActive].unpress();
numKeyActive = -1;
}
timer = -1;
spaceSet = false;
}
}
}
}
// Acknowledge key press.
public void keyPressed (KeyEvent key) {
int code = key.getKeyCode();
if ((code >= KeyEvent.VK_0) && (code <= KeyEvent.VK_9)) {
// newKey can also be changed by the run method.
System.out.println("keyPad.keyevent"+code);
synchronized (newKey) {
107
APPENDICE A: Codice Xlet
newKey.set(code-KeyEvent.VK_0);
}
}
}
public void backSpace() {
backSpaceSet = true;
}
public void space() {
spaceSet = true;
}
// Assign the key pad to a EditableTextBox.
public void setOutput (EditableTextBox e) {
textBox = e;
}
}
KeypadButton.java
import java.awt.*;
public class KeypadButton extends Component {
// Constants
private static final int KB_WIDTH = 75;
private static final int KB_HEIGHT = 52;
private int keyNumber = 0;
private char[] subChars = null;
private int activeSubChar = -1;
private int subCharCount = 0;
private boolean focused = false;
private static Font mainCharFont = null;
private static Font subCharFont = null;
// Create KeypadButton.
public KeypadButton(int keyNum, String keyChars) {
setSize(KB_WIDTH,KB_HEIGHT);
keyNumber = keyNum;
subChars = keyChars.toCharArray();
subCharCount = keyChars.length();
mainCharFont = new Font("Tirealias",Font.PLAIN,27);
subCharFont = new Font("Tirealias", Font.PLAIN,20);
}
public void paint(Graphics g) {
super.paint(g);
FontMetrics fm = null;
String s = null;
// If the button if focused it is painted with a white border.
if (focused) {
108
APPENDICE A: Codice Xlet
g.setColor(Color.white);
} else {
g.setColor(new Color(0x2a,0x2a,0x2a));
}
g.fillRect(0,0,this.getSize().width,this.getSize().height);
g.setColor(Color.black);
g.fillRect(2,2,this.getSize().width-4,this.getSize().height-4);
// Draw subcharacters. FontMetrics is used for getting character
// widths, so that the text can be centered.
g.setFont(subCharFont);
fm = g.getFontMetrics();
int textWidth = fm.stringWidth(String.valueOf(subChars));
int charX = (KB_WIDTH-textWidth)/2;
for (int i=0;i<subCharCount;i++) {
if (i==activeSubChar) {
g.setColor(Color.white);
} else {
g.setColor(new Color(0x3f,0x3f,0xff));
}
s = String.valueOf(subChars[i]);
g.drawString(s,charX,46);
charX += fm.charWidth(subChars[i]);
}
// Draw key number.
g.setFont(mainCharFont);
fm = g.getFontMetrics();
g.setColor(Color.white);
s = String.valueOf(keyNumber);
g.drawString(s,(KB_WIDTH-fm.stringWidth(s))/2,26);
}
public void press() {
if (activeSubChar == -1) {
focused = true;
activeSubChar = 0;
} else {
activeSubChar = (activeSubChar+1) % subCharCount;
}
repaint();
}
public void unpress() {
activeSubChar = -1;
focused = false;
repaint();
}
public char getChar() {
if (subChars != null) return subChars[activeSubChar];
return 0;
}
}
109
APPENDICE A: Codice Xlet
EditableTextBox.java
import java.awt.*;
public class EditableTextBox extends Component {
private int width = 0;
private int height = 0;
private String editStr = null; // The currently edited text
private String nullStr = null; // The text shown if there's no editStr present
private char pendingChar = 0; // An character which has not yet been added to editStr
private boolean focused = false;
private boolean editActive = false;
// Create the component.
public EditableTextBox(String nullStr, int width,int height) {
setSize(width,height);
this.nullStr = nullStr;
this.width = width;
this.height = height;
}
// Paint the component.
public void paint(Graphics g) {
if (focused) {
if(editActive)g.setColor(Color.red);//colore riquadro focused e edit active
else g.setColor(Color.darkGray); //colore riquadro focused
g.fillRect(0,0,width,height);
}
g.setColor(Color.white);
g.fillRect(2,2,width-4,height-4);
if ((editStr == null) || ((editStr.length() == 0) && !editActive)) {
g.setColor(Color.black);
g.setFont(new Font("Tireasias",Font.PLAIN,height-10));
g.drawString(nullStr,10,height-10);
} else {
g.setColor(Color.black);
g.setFont(new Font("Tireasias",Font.PLAIN,height-10));
g.drawString(editStr,10,height-10);
if (pendingChar != 0) {
g.setColor(Color.red);
FontMetrics fm = g.getFontMetrics();
g.drawString(String.valueOf(pendingChar),10+fm.stringWidth(editStr),height-10);
}
}
}
public void setFocused(boolean b) {
focused = b;
}
// Set the pending character (shown after editStr)
public void setPending(char c) {
pendingChar = c;
repaint();
}
110
APPENDICE A: Codice Xlet
// Add the pending character to editStr
public void addPending() {
editStr += pendingChar;
pendingChar = 0;
repaint();
}
// Remove the last character (pending or last of editStr)
public void backSpace() {
if (pendingChar != 0) {
pendingChar = 0;
} else {
if (editStr.length()>0) {
editStr = editStr.substring(0,editStr.length()-1);
}
}
repaint();
}
// Add space.
public void space() {
if (pendingChar != 0) {
addPending();
}
editStr = editStr + ' ';
}
// Return the edited string.
public String getString() {
return editStr;
}
// Set edit mode on/off
public void setEdit(boolean b) {
editActive = b;
if (editStr == null) { editStr = new String(); }
}
}
111
APPENDICE A: Codice Xlet
Strumenti.java
import java.awt.Color;
import java.awt.Font;
import java.awt.event.KeyEvent;
import java.io.IOException;
import org.havi.ui.*;
//import java.net.*;
//import java.sql.*;
import org.dvb.net.rc.*;
public class Strumenti {
//0:non esistono connessioni 1:conn persistenete 2:conn non persistenete
public static final String IPPROXY="localhost";
//"192.168.1.1"; deit
//"localhost";
//"10.6.160.143"; Asur
public static int connesso=0;
public String []strmask;
public static HScene scene;
private static int StatoOld;
EditableTextBox []etb;
public Keypad keyPad;
private static HText mess,mess2;
public Strumenti(HScene sceneint){
scene=sceneint;
}
public void init(){
InterfacciaGrafica.STATO=InterfacciaGrafica.PRIMO;
keyPad = new Keypad();
new Thread(keyPad).start();
keyPad.setLocation (100,310);
scene.add(keyPad);
keyPad.setVisible(false);
}
public void Start(){}
public void Stop(){
//new Thread(keyPad).stop();
scene.remove(keyPad);
for(int i=0;i<etb.length;i++){
scene.remove(etb[i]);
}
scene.repaint();
}
public boolean popUp(String str,int x,int y){
boolean flag=false;
return flag;
112
APPENDICE A: Codice Xlet
}
public static void Connetti()throws IOException {
//prefisso centralino 73372504
final String phone="733725047027020000";
final String user="[email protected]";
final String pass="979116";
Rsl listener;
ConnectionRCInterface cRCi=null;
RCResourceClient resource_client;
resource_client=new RCResourceClient();
//Per prima cosa acquisiamo un'istanza di RCInterfaceManager
RCInterfaceManager rcim=RCInterfaceManager.getInstance();
//rcim.addResourceStatusEventListener(listener);
//Estraiamo l'array con tutte le possibili interfacce di connessione
//che la nostra applicazione potrà avviare.
//RCInterface rci[] = rcim.getInterfaces();
RCInterface rci[] = rcim.getInterfaces();
int il=0;
if (rci!= null) {
il = rci.length;
System.out.println("Numero delle interfacce: " + il);
} else {
il = 0;
connesso=0;
System.out.println("Nessuna interfaccia supportata (rci =null)");
return;
}
//
//
//
Tra tutte le interfacce riportate nell'array si verifica
quale è una istanza di ConnectionRCInterface e si seleziona
quella appropriata.
//
Elenco l'interfacce
System.out.println("Interfacce trovate:");
for(int i = 0; i<il; i++) {
System.out.println(rci[i].getType());
if(rci[i] instanceof ConnectionRCInterface) {
cRCi = (ConnectionRCInterface) rci[i];
System.out.println("Interfaccia "+i+ ":" + cRCi.getType()+" "+cRCi.toString()+"
DataRate:"+cRCi.getDataRate());
}
}
//
Scelgo l'interfaccia più veloce
/*for(int i = il-1; i>=0; i--) {
if(rci[i] instanceof ConnectionRCInterface) {
113
APPENDICE A: Codice Xlet
cRCi = (ConnectionRCInterface) rci[i];
System.out.println("L'interfaccia usata è" + cRCi.getType());
break;
}
}*/
if(rci[0].getType()==4) connesso=1;
//cRCi = (ConnectionRCInterface) rci[0];
// Forzo a modem
//
cRCi=(ConnectionRCInterface) rci[0];
// Forzo a LAN
//
cRCi=(ConnectionRCInterface) rci[0];
//
Ora abbiamo ottenuto il riferimento alla nostra interfaccia,
//
se non è permanente andiamo a stabilire la connessione.
if (cRCi!= null && cRCi.getType() == RCInterface.TYPE_PSTN) {
L'interfaccia viene riservata per la nostra applicazione
associandogli un client da noi definito.
try{
cRCi.reserve(resource_client, null );
}catch (PermissionDeniedException e){
System.out.println("Errore nel riservare la risorsa: "+e.getMessage());
System.out.println("L' interfaccia non puo essere riservata:Permission Denied!");
return;
}
Setto i parametri per effetture la chiamata e l'autenticazione.
try{
cRCi.setTarget(new ConnectionParameters(phone,user,pass));
}catch (IncompleteTargetException e){
System.out.println("Errore nel setting del target(incomplete):"+e.getMessage());
return;
}catch (PermissionDeniedException e){
System.out.println("Errore nel setting del target(permission denied:"+e.getMessage());
return;
}
//
//
//
//cRCi.addConnectionListener(listener);
Crea la connesione con i parametri precedentemente impostati.
Se la connesione a gia attiva il metodo non ha effetto.
try{
cRCi.connect();
System.out.println("La connessione e stata effettuata!");
}catch (IOException e){
System.out.println("Errore di connessione(io): "+e.getMessage());
return;
}catch (PermissionDeniedException e){
System.out.println("Errore di connessione(permission denied): "+e.getMessage());
return;
}
connesso=2;
}
//
//
}
public boolean getMask(int nc){
strmask=new String[nc];
boolean flag=true;
114
APPENDICE A: Codice Xlet
for(int i=0;i<nc;i++){
strmask[i]=etb[i].getString();
System.out.println(strmask[i]);
if(strmask[i]==null || strmask[i].length()==0) flag=false;
}
return flag;
}
// Messaggio di commento di durata indicata
public static void messFlash(String str,int x,int y){
StatoOld=InterfacciaGrafica.STATO;
InterfacciaGrafica.STATO=InterfacciaGrafica.POPUP;
Font font=new Font("Arial",Font.BOLD,22);
mess= new HText(str);
mess2=new HText("PREMI VERDE per continuare");
mess.setFont(font);
mess2.setFont(font);
mess.setForeground(Color.white);
mess2.setForeground(Color.green);
mess.setBackgroundMode(HVisible.BACKGROUND_FILL);
mess2.setBackgroundMode(HVisible.BACKGROUND_FILL);
mess.setBackground(new Color(100,100,100,120));
mess2.setBackground(new Color(100,100,100,120));
mess.setBounds(x,y,580,25);
mess2.setBounds(x,y+25,580,20);
mess.setBordersEnabled(true);
scene.add(mess);
scene.add(mess2);
mess.setVisible(true);
mess2.setVisible(true);
scene.repaint();
}
public static void messFlashOff(){
InterfacciaGrafica.STATO=StatoOld;
mess.setVisible(false);
scene.remove(mess);
mess2.setVisible(false);
scene.remove(mess2);
scene.repaint();
}
public void passaEvento (KeyEvent key) { }
}
115
APPENDICE A: Codice Xlet
Registra.java
import java.awt.event.KeyEvent;
import java.io.IOException;
import org.havi.ui.*;
import org.havi.ui.event.HRcEvent;
public class Registra extends Strumenti{
private int ic=0; //indice del campo corrente
private static final int dist=40;
boolean edita=false;
Registra(HScene sceneint){
super(sceneint);
}
public void Start(){
etb=new EditableTextBox[5];
etb[0]=new EditableTextBox("Nome",300,30);
etb[0].setLocation(300,70);
etb[1]=new EditableTextBox("Cognome",300,30);
etb[1].setLocation(300,70+dist);
etb[2]=new EditableTextBox("Telefono",300,30);
etb[2].setLocation(300,70+2*dist);
etb[3]=new EditableTextBox("Username",300,30);
etb[3].setLocation(300,70+3*dist);
etb[4]=new EditableTextBox("Password",300,30);
etb[4].setLocation(300,70+4*dist);
for(int i=0;i<5;i++){
etb[i].setVisible(true);
scene.add(etb[i]);
}
etb[ic].setFocused(true);
keyPad.setVisible(true);
scene.repaint();
}
public void passaEvento (KeyEvent key) {
int pulsantePremuto = key.getKeyCode();
switch(pulsantePremuto){
case KeyEvent.VK_0:
case KeyEvent.VK_1:
case KeyEvent.VK_2:
case KeyEvent.VK_3:
case KeyEvent.VK_4:
case KeyEvent.VK_5:
case KeyEvent.VK_6:
case KeyEvent.VK_7:
case KeyEvent.VK_8:
case KeyEvent.VK_9:
116
APPENDICE A: Codice Xlet
if(edita)keyPad.keyPressed(key);
break;
case HRcEvent.VK_COLORED_KEY_0:
System.out.println("Premuto rosso sotto registra");
if (getMask(5)){
//Controlla se esiste user e aggiorna anag
try{
Connetti();
}catch (IOException e){
System.out.println("Errore di connessione(io): "+e.getMessage());
}
//
Registra un nuovo utente
Comunica com=new Comunica(IPPROXY,8000);
ObjectRegistration reg=new ObjectRegistration(strmask[3],strmask[4],strmask[0],strmask[1],strmask[2]);
String ris=(String)(com.Connetti(reg));
System.out.println("ris="+ris);
messFlash(ris,60,265);
//Ritorna registra effettuata con successo o Username già esistente
}else{
//
finestra errore:riempi tutti i campi
messFlash("Compila tutti i campi",60,265);
}
break;
case HRcEvent.VK_COLORED_KEY_1: break;
case HRcEvent.VK_COLORED_KEY_2: break;
case HRcEvent.VK_COLORED_KEY_3:
Stop();
break;
case HRcEvent.VK_UP:
edita=false;
etb[ic].setFocused(false);
etb[ic].setEdit(false);
if(ic==0){
ic=4;
}else ic--;
etb[ic].setFocused(true);
scene.repaint();
break;
case HRcEvent.VK_DOWN:
edita=false;
etb[ic].setEdit(false);
etb[ic].setFocused(false);
if(ic==4){
ic=0;
}else ic++;
etb[ic].setFocused(true);
scene.repaint();
break;
case HRcEvent.VK_RIGHT:
if(edita)keyPad.space();
break;
117
APPENDICE A: Codice Xlet
case HRcEvent.VK_LEFT:
if(edita)keyPad.backSpace();
break;
case HRcEvent.VK_ENTER:
edita=true;
keyPad.setOutput(etb[ic]);
etb[ic].setEdit(true);
scene.repaint();
default: break;
}
}
}
Prenota.java
import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.net.*;
import org.havi.ui.*;
import org.havi.ui.event.HRcEvent;
public class Prenota extends Strumenti{
private int ic=0; //indice del campo corrente
private static final int dist=50;
boolean edita=false;
private boolean riprenota=false;
private int disp [];
private static int ntav=5; //N tot tavoli
private int itav=0; //indice corrente vettore tavoli
private Image tavv,tavr; //icone:tavolo libero,occupato
public static HIcon []vetTav;
private static HText messblu;
Prenota(HScene sceneint){
super(sceneint);
tavv=Toolkit.getDefaultToolkit().getImage("tavolo_v.gif");
tavr=Toolkit.getDefaultToolkit().getImage("tavolo_r.gif");
}
public void Start(){
etb=new EditableTextBox[2];
etb[0]=new EditableTextBox("Username",300,35);
etb[0].setLocation(300,100);
etb[1]=new EditableTextBox("Password",300,35);
etb[1].setLocation(300,100+dist);
for(int i=0;i<2;i++){
etb[i].setVisible(true);
scene.add(etb[i]);
}
etb[ic].setFocused(true);
keyPad.setVisible(true);
scene.repaint();
}
public void visualizzaDisp(){
if(!riprenota){
118
APPENDICE A: Codice Xlet
Main.gestoreSfondi.displayBackground("background.mpg",260,100,225,180);
Font font=new Font("Arial",Font.BOLD,20);
messblu= new HText("PREMI TASTO BLU PER USCIRE");
messblu.setFont(font);
messblu.setForeground(Color.blue);
messblu.setBackgroundMode(HVisible.BACKGROUND_FILL);
messblu.setBackground(new Color(100,100,100,120));
messblu.setBounds(60,460,580,20);
messblu.setBordersEnabled(true);
scene.add(messblu);
}
if(riprenota){
for(int i=0;i<ntav;i++){
scene.remove(vetTav[i]);
}
System.out.println("RIPRENOTA");
scene.repaint();
}
messblu.setVisible(true);
InterfacciaGrafica.STATO=InterfacciaGrafica.DISP;
System.out.println("stato visu disp"+InterfacciaGrafica.STATO);
vetTav=new HIcon[ntav];
for(int i=0;i<ntav;i++){
if(disp[i]==0)vetTav[i]= new HIcon(tavv,110+100*i,300,100,80);
else vetTav[i]= new HIcon(tavr,110+100*i,300,100,80);
vetTav[i].setBackground(new Color(100,100,100,120));
scene.add(vetTav[i]);
vetTav[i].setVisible(true);
}
vetTav[itav].setBackgroundMode(HVisible.BACKGROUND_FILL);
scene.repaint();
}
public static void stopVisu(){
for(int i=0;i<ntav;i++){
scene.remove(vetTav[i]);
}
messblu.setVisible(false);
//scene.remove(messblu);
scene.repaint();
InterfacciaGrafica.STATO=InterfacciaGrafica.MENU;
}
public void passaEvento (KeyEvent key) {
int pulsantePremuto = key.getKeyCode();
switch(pulsantePremuto){
case KeyEvent.VK_0:
case KeyEvent.VK_1:
case KeyEvent.VK_2:
case KeyEvent.VK_3:
case KeyEvent.VK_4:
case KeyEvent.VK_5:
case KeyEvent.VK_6:
case KeyEvent.VK_7:
119
APPENDICE A: Codice Xlet
case KeyEvent.VK_8:
case KeyEvent.VK_9:
if(edita)keyPad.keyPressed(key);
break;
case HRcEvent.VK_COLORED_KEY_0:
if(getMask(2)){
try{
Connetti();
}catch (IOException e){
System.out.println("Errore di connessione(io): "+e.getMessage());
}
System.out.println("connesso:"+connesso);
//prova();
Comunica com=new Comunica(IPPROXY,8000);
ObjectPrenotation pren=new ObjectPrenotation(strmask[0],strmask[1]);
System.out.println("creato ObjectPrenotation");
Object tavoli=com.Connetti(pren);
System.out.println("ritornato tavoli");
if(tavoli instanceof ObjectTavoli) {
String ris="";
disp=new int[((ObjectTavoli)tavoli).getLength()];
for (int i=0;i<((ObjectTavoli)tavoli).getLength();i++) {
String u=(((ObjectTavoli)tavoli).getUtente(i));
String =String.valueOf(((ObjectTavoli)tavoli).getNumero(i));
String l=String.valueOf(((ObjectTavoli)tavoli).getLibero(i));
disp[i]=((ObjectTavoli)tavoli).getLibero(i);
ris=ris+"\nUtente: "+u+" Numero: "+n+" Libero: "+l;
}
System.out.println(ris);
Stop();
visualizzaDisp();
messFlash("Login corretto,prenota il tuo tavolo!",60,415);
riprenota=true;
break;
}
if(tavoli instanceof String) {
System.out.println(tavoli);
messFlash((String)tavoli,60,265);
}
//Ritorna Username o password errati
}else{
System.out.println("compila tutto");
messFlash("Compila tutti i campi",60,265);
}
break;
case HRcEvent.VK_COLORED_KEY_1: break;
case HRcEvent.VK_COLORED_KEY_2: break;
case HRcEvent.VK_COLORED_KEY_3:
if(InterfacciaGrafica.STATO==InterfacciaGrafica.DISP){
stopVisu();
120
APPENDICE A: Codice Xlet
}
break;
case HRcEvent.VK_UP:
edita=false;
etb[ic].setFocused(false);
etb[ic].setEdit(false);
if(ic==0){
ic=1;
}else ic--;
etb[ic].setFocused(true);
scene.repaint();
break;
case HRcEvent.VK_DOWN:
edita=false;
etb[ic].setEdit(false);
etb[ic].setFocused(false);
if(ic==1){
ic=0;
}else ic++;
etb[ic].setFocused(true);
scene.repaint();
break;
case HRcEvent.VK_RIGHT:
System.out.println("Destro");
if(edita)keyPad.space();
if(InterfacciaGrafica.STATO==InterfacciaGrafica.DISP){
vetTav[itav].setBackgroundMode(HVisible.NO_BACKGROUND_FILL);
if(itav>=ntav-1){
itav=0;
}else itav++;
vetTav[itav].setBackgroundMode(HVisible.BACKGROUND_FILL);
scene.repaint();
}
break;
case HRcEvent.VK_LEFT:
if(edita)keyPad.backSpace();
if(InterfacciaGrafica.STATO==InterfacciaGrafica.DISP){
vetTav[itav].setBackgroundMode(HVisible.NO_BACKGROUND_FILL);
if(itav<=0){
itav=ntav-1;
}else itav--;
vetTav[itav].setBackgroundMode(HVisible.BACKGROUND_FILL);
scene.repaint();
}
break;
case HRcEvent.VK_ENTER:
if(InterfacciaGrafica.STATO==InterfacciaGrafica.DISP){
if(disp[itav]==0){
Comunica com=new Comunica(IPPROXY,8000);
PrenotaSingolo pren=new PrenotaSingolo(strmask[0],itav+1);//user e n tavolo 1a5
Object p=com.Connetti(pren);
disp[itav]=1;
121
APPENDICE A: Codice Xlet
if (p instanceof String) {
System.out.println(p);
//Prenotazione eseguita con successo
visualizzaDisp();
messFlash((String)p,60,415);
}
}else{
messFlash("Tavolo già riservato",60,415);
}
break;
}
edita=true;
keyPad.setOutput(etb[ic]);
etb[ic].setEdit(true);
scene.repaint();
default: break;
}
}
}
Comunica.java
import java.net.*;
import java.io.*;
//import java.util.*;
public class Comunica {
private String IP;
private int port;
private String str;
private Object obj;
private Object objW;
public Comunica(String sIP,int p){
this.port=p;
this.IP=sIP;
}
public Object Connetti(Object object){
obj=null;
objW=object;
try{
// Apre una connessione verso un server in ascolto
// sulla porta "port", all'indirizzo "IP"
System.out.println("Apertura Socket...");
Socket sock = new Socket(IP,port);
System.out.println("creato socket");
OutputStream s1out = sock.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(s1out);
System.out.println("impostato outpustream");
oos.writeObject(objW);
// Ricava lo stream di input dal socket sock
// ed utilizza un oggetto wrapper di classe BufferedReader
// per semplificare le operazioni di lettura
InputStream is = sock.getInputStream();
ObjectInputStream ois = new ObjectInputStream(is);
//BufferedReader dis = new BufferedReader(new InputStreamReader(is));
// Legge l'input e lo visualizza sullo schermo
122
APPENDICE A: Codice Xlet
//str=dis.readLine();
try{
obj=ois.readObject();}
catch (ClassNotFoundException e) {
System.out.println("Error: "+e.getMessage());
}
ois.close();
sock.close();
System.out.println("Chiusura connessione effettuata");
}catch (ConnectException connExc){
System.out.println("Errore nella connessione ");
}
catch (IOException ex){
ex.printStackTrace();
}
return obj;
}
PrenotaSingolo pren=new PrenotaSingolo("jack",1);
Object p=com.Connetti(pren);
if (p instanceof String) {
System.out.println(p);
}
}
}
PrenotaSingolo.java
import java.io.*;
public class PrenotaSingolo implements Serializable {
private String user;
private int numero;
public PrenotaSingolo (String user, int numero) {
this.user=user;
this.numero=numero;
}
public String getUser() {
return user;
}
public int getNumero() {
return numero;
}
}
123
APPENDICE B: Codice Proxy
APPENDICE B
Codice Proxy
SimpleServer.java
import java.net.*;
import java.io.*;
//import java.util.*;
public class SimpleServer {
public static final String IPDB="localhost";
public static final String USERDB="fabiob";
public static final String PASSDB="979116";
private int port;
private ServerSocket server;
private Object obj;
private Object prenot;
private String ris;
private Object tavoli;
private Object prenotaS;
public SimpleServer (int port){
ris="";
this.port = port;
if(!startServer()) System.err.println("Errore durante la creazione del Server");
}
private boolean startServer(){
try{
server = new ServerSocket(port);
}catch (IOException ex){
ex.printStackTrace();
return false;
}
System.out.println("Server creato con successo!");
return true;
}
public void runServer(){
while (true){
try{
// Il server resta in attesa di una richiesta
System.out.println("Server in attesa di richieste...");
Socket s1 = server.accept();
System.out.println("Un client si e' connesso...");
InputStream is = s1.getInputStream();
ObjectInputStream ois = new ObjectInputStream(is);
try{
obj=(Object)(ois.readObject());
System.out.println("oggetto letto");
}
124
APPENDICE B: Codice Proxy
catch (ClassNotFoundException e) {
System.out.println("Error: "+e.getMessage());
}
if (obj instanceof ObjectRegistration) {
ris=(new ServerRegistration((ObjectRegistration)obj)).start();
// Ricava lo stream di output associate al socket
// e definisce una classe wrapper di tipo
// BufferedWriter per semplificare le operazioni
// di scrittura
OutputStream s1out = s1.getOutputStream();
// BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s1out));
ObjectOutputStream oos = new ObjectOutputStream(s1out);
// Il server invia la risposta al client
// oos.writeObject("Benvenuto sul server!\n");
oos.writeObject(ris);
// Chiude lo strema di output e la connessione
oos.close();
}
if(obj instanceof ObjectPrenotation) {
tavoli=(new ServerPrenotation((ObjectPrenotation)obj)).start();
OutputStream s1out = s1.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(s1out);
oos.writeObject(tavoli);
oos.close();
}
if (obj instanceof PrenotaSingolo) {
prenotaS=(new ServerPrenotaSingolo((PrenotaSingolo)obj)).start();
OutputStream s1out = s1.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(s1out);
oos.writeObject(prenotaS);
oos.close();
}
s1.close();
System.out.println("Chiusura connessione effettuata\n");
}catch (IOException ex){
ex.printStackTrace();
}
}
}
public static void main(String[] args) {
SimpleServer ss = new SimpleServer(8000);
ss.runServer();
}
}
125
APPENDICE B: Codice Proxy
ServerRegistration.java
import java.sql.*;
public class ServerRegistration {
private ObjectRegistration obj;
private String ris;
public ServerRegistration(ObjectRegistration object) {
this.obj=object;
ris="";
}
public String start() {
try {
Class.forName("com.mysql.jdbc.Driver");
System.out.println("eseguito class.forname");
}
catch (ClassNotFoundException e1){}
try{
String user=SimpleServer.USERDB;
String pass=SimpleServer.PASSDB;
String url="jdbc:mysql://"+SimpleServer.IPDB+"/prenotazioni";
Connection con = DriverManager.getConnection(url,user,pass);
System.out.println("Connessione DB: "+url+user+pass);
Statement stmt=con.createStatement();
System.out.println(((ObjectRegistration)obj).getUser());
String query= "SELECT user FROM cliente WHERE
(user='"+((ObjectRegistration)obj).getUser()+"');";
ResultSet rm=stmt.executeQuery(query);
while (rm.next()) {
ris=rm.getString(1);
}
if (ris=="") {
String query2= "INSERT INTO cliente (user,password,nome,cognome,telefono) VALUES
('"+((ObjectRegistration)obj).getUser()+"','"+((ObjectRegistration)obj).getPassword()+"','"+((ObjectRegi
stration)obj).getNome()+"','"+((ObjectRegistration)obj).getCognome()+"','"+((ObjectRegistration)obj).get
Telefono()+"');";
stmt.executeUpdate(query2);
ris="Registrazione effettuata con successo!";
System.out.println("query effettuata");
}
else {
ris="Username esistente.";
}
rm.close();
stmt.close();
con.close();
}catch(SQLException exc) {
System.out.println("SQL error: "+exc.getMessage());
}
return ris;
}
}
126
APPENDICE B: Codice Proxy
ServerPrenotation.java
import java.sql.*;
public class ServerPrenotation {
private ObjectPrenotation obj;
private Object tavoli;
public ServerPrenotation(ObjectPrenotation obj) {
this.obj=obj;
}
public Object start() {
try {
Class.forName("com.mysql.jdbc.Driver");
System.out.println("eseguito class.forname");
}
catch (ClassNotFoundException e1){}
try{
String user=SimpleServer.USERDB;
String pass=SimpleServer.PASSDB;
String url="jdbc:mysql://"+SimpleServer.IPDB+"/prenotazioni";
Connection con = DriverManager.getConnection(url,user,pass);
System.out.println("Connessione DB: "+url+user+pass);
Statement stmt=con.createStatement();
String query="SELECT user FROM cliente WHERE user='"+obj.getUser()+"' AND
password='"+obj.getPassword()+"';";
ResultSet rm=stmt.executeQuery(query);
String ris1="";
while(rm.next()) {
ris1=ris1+rm.getString(1);
}
rm.close();
if (ris1=="") {
System.out.println("user pwd errati");
tavoli="Username o password errati.";
}
else {
System.out.println("user pwd esatti");
String query2= "SELECT count(*) FROM tavoli;";
ResultSet rm2=stmt.executeQuery(query2);
System.out.println("eseguita query2");
int length=0;
while (rm2.next()) {
length=Integer.parseInt(rm2.getString(1));
System.out.println("length: "+length);
}
rm2.close();
String query3= "SELECT * FROM tavoli;";
ResultSet rm3=stmt.executeQuery(query3);
int[] num=new int[length];
String[] utente=new String[length];
int[] libero=new int[length];
int j=0;
while (rm3.next()) {
num[j]=Integer.parseInt(rm3.getString(1));
System.out.println(num[j]);
utente[j]=rm3.getString(2);
127
APPENDICE B: Codice Proxy
System.out.println(utente[j]);
libero[j]=Integer.parseInt(rm3.getString(3));
System.out.println(libero[j]);
j++;
}
tavoli=new ObjectTavoli(utente,num,libero);
rm3.close();
}
stmt.close();
con.close();
}
catch(SQLException exc) {
System.out.println("SQLException: "+exc.getMessage());
}
return tavoli;
}
}
ServerPrenotaSingolo.java
import java.sql.*;
public class ServerPrenotaSingolo {
private PrenotaSingolo obj;
private String ris;
public ServerPrenotaSingolo(PrenotaSingolo obj) {
this.obj=obj;
}
public Object start() {
ris="";
try {
Class.forName("com.mysql.jdbc.Driver");
System.out.println("eseguito class.forname");
}
catch (ClassNotFoundException e1){}
try{
String user=SimpleServer.USERDB;
String pass=SimpleServer.PASSDB;
String url="jdbc:mysql://"+SimpleServer.IPDB+"/prenotazioni";
Connection con = DriverManager.getConnection(url,user,pass);
System.out.println("Connessione DB: "+url+user+pass);
Statement stmt=con.createStatement();
String query= "UPDATE tavoli SET libero=1 ,utente='"+obj.getUser()+"' WHERE
num_tavolo="+obj.getNumero()+";";
stmt.executeUpdate(query);
ris="Prenotazione eseguita con successo!";
stmt.close();
con.close();
}
catch (SQLException e) {
128
APPENDICE B: Codice Proxy
System.out.println("Eccezione: "+e.getMessage());
ris="Errore prenotazione";
}
return ris;
}
}
PrenotaSingolo.java
import java.io.*;
public class PrenotaSingolo implements Serializable {
private String user;
private int numero;
public PrenotaSingolo (String user, int numero) {
this.user=user;
this.numero=numero;
}
public String getUser() {
return user;
}
public int getNumero() {
return numero;
}
}
ObjectRegistration.java
import java.io.*;
public class ObjectRegistration implements Serializable{
private String user;
private String password;
private String nome;
private String cognome;
private String telefono;
public ObjectRegistration(String u, String pwd, String nom, String cog, String tel) {
this.user=u;
this.password=pwd;
this.nome=nom;
this.cognome=cog;
this.telefono=tel;
System.out.println("creato objectRegistration");
}
public String getUser() {
return this.user;
}
public String getPassword() {
return this.password;
}
public String getNome() {
129
APPENDICE B: Codice Proxy
return this.nome;
}
public String getCognome() {
return this.cognome;
}
public String getTelefono() {
return this.telefono;
}
}
ObjectPrenotation.java
import java.io.*;
public class ObjectPrenotation implements Serializable {
private String user;
private String password;
public ObjectPrenotation (String user, String password){
this.user=user;
this.password=password;
}
public String getUser() {
return this.user;
}
public String getPassword() {
return this.password;
}
}
ObjectTavoli.java
import java.io.*;
public class ObjectTavoli implements Serializable{
private String[] utente;
private int[] num_tavolo;
private int[] libero;
public ObjectTavoli(String[] utente, int[] num, int[] libero) {
this.num_tavolo=num;
this.libero=libero;
this.utente=utente;
}
public String getUtente(int i) {
return utente[i];
}
public int getNumero(int i) {
return num_tavolo[i];
}
public int getLibero(int i) {
return libero[i];
}
public int getLength() {
return utente.length;
}
}
130
Siti e Bibliografia
SITI E BIBLIOGRAFIA
www.mhp.org
www.openmhp.org
www.dvb.org
www.etsi.org
www.cineca.it
www.sun.it
www.myslq.com
www.java.sun.com
www.javastaff.com
www.sourceforge.net
www.eclipse.org
www.coremedia.com
www.televisionedigitaleterrestre.it
www.interactivetvweb.org
www.digisoft.it
www.digitaleterrestre.it
Steven Morris, Anthony Smith-Chaigneau:
“Interactive TV Standard – A Guide to MHP, OCAP and Java TV”.
George Reese:
“O'Reilly-Database Programming with JDBC and Java, Second Edition”.
V.Mignone, A.Morello, M.Visintin:
“Lo standard DVB-T per la televisione digitale terrestre”.
E.Fleischner,B.Somalvico:
"La TV diventa digitale", Franco Angeli, 2002.
131