Creazione di un download manager in Java



Documenti analoghi
Tipi primitivi. Ad esempio, il codice seguente dichiara una variabile di tipo intero, le assegna il valore 5 e stampa a schermo il suo contenuto:

NOTE OPERATIVE. Prodotto Inaz Download Manager. Release 1.3.0

La gestione dell input/output da tastiera La gestione dell input/output da file La gestione delle eccezioni

Finestra.java. static String nomicolonne[] = {"ind.","cognome","nome","telefono"," "}; //nomi delle colonne della tabella

Corso sul linguaggio Java

MODELLO CLIENT/SERVER. Gianluca Daino Dipartimento di Ingegneria dell Informazione Università degli Studi di Siena

MANUALE UTENTE Fiscali Free

Che cos'è un modulo? pulsanti di opzione caselle di controllo caselle di riepilogo

DENUNCE EDILCONNECT GUIDA COMPILAZIONE

Java Applet. Linguaggi Corso M-Z - Laurea in Ingegneria Informatica A.A

Hub-PA Versione Manuale utente

2.5. L'indirizzo IP identifica il computer di origine, il numero di porta invece identifica il processo di origine.

Software Servizi Web UOGA

ALTRO. v (Aprile 2015)

Corso basi di dati Installazione e gestione di PWS

L interfaccia grafica in Java

11/02/2015 MANUALE DI INSTALLAZIONE DELL APPLICAZIONE DESKTOP TELEMATICO VERSIONE 1.0

Il sofware è inoltre completato da una funzione di calendario che consente di impostare in modo semplice ed intuitivo i vari appuntamenti.

10.1. Un indirizzo IP viene rappresentato in Java come un'istanza della classe InetAddress.

Registratori di Cassa

appunti delle lezioni Architetture client/server: applicazioni client

Consiglio regionale della Toscana. Regole per il corretto funzionamento della posta elettronica

1) GESTIONE DELLE POSTAZIONI REMOTE

1. Le macro in Access 2000/2003

ALTRO. v (Maggio 2015)

Manuale per la configurazione di AziendaSoft in rete

Programmazione Java: Interfacce grafiche (GUI)

GUIDA DI INSTALLAZIONE E PRIMA CONFIGURAZIONE DI EDILCONNECT PER I CONSULENTI

MANUALE D USO DELL E-COMMERCE. Versione avanzata

Invio SMS. DM Board ICS Invio SMS

Il seguente Syllabus è relativo al Modulo 7, Reti informatiche, e fornisce i fondamenti per il test di tipo pratico relativo a questo modulo

Per scrivere una procedura che non deve restituire nessun valore e deve solo contenere le informazioni per le modalità delle porte e controlli

Grafico della parabola

per immagini guida avanzata Organizzazione e controllo dei dati Geometra Luigi Amato Guida Avanzata per immagini excel

Manuale di KSystemLog. Nicolas Ternisien

Soluzione dell esercizio del 2 Febbraio 2004

Installazione del software Fiery per Windows e Macintosh

A destra è delimitata dalla barra di scorrimento verticale, mentre in basso troviamo una riga complessa.

FOXWave Gestione gare ARDF IZ1FAL Secco Marco Sezione ARI BIELLA

Luca Mari, Sistemi informativi applicati (reti di calcolatori) appunti delle lezioni. Architetture client/server: applicazioni server

GUIDA UTENTE MONEY TRANSFER MANAGER

Telematica II 17. Esercitazione/Laboratorio 6

MANUALE D'USO DEL PROGRAMMA IMMOBIPHONE

1 ACCESSO AL 3 2 CARICAMENTO DELLE RICHIESTE/PRESTAZIONI MONITORAGGIO DELLE RICHIESTE DOWNLOAD ESITI...

Istruzioni di installazione di IBM SPSS Modeler Text Analytics (licenza per sito)

Java threads (2) Programmazione Concorrente

Appunti di Informatica 1

Lo scenario: la definizione di Internet

Aggiornamenti Sistema Addendum per l utente

GUARINI PATRIMONIO CULTURALE VERSIONE MSDE/SQL SERVER MANUALE DI INSTALLAZIONE GUARINI PATRIMONIO CULTURALE VERSIONE MSDE/SQL SERVER

Centro Acquisti per la Pubblica Amministrazione EmPULIA. Linee guida per gli Enti Aderenti. Procedure Negoziate: Richiesta di Preventivo. Versione 2.

Database Manager Guida utente DMAN-IT-01/09/10

LA GESTIONE DELLE VISITE CLIENTI VIA WEB

Manuale di istruzioni sulle maschere per il calcolo del punteggio e del voto (unico) degli studenti che sostengono la Prova nazionale 2011

Portale tirocini. Manuale utente Per la gestione del Progetto Formativo

Corso Eclipse. Prerequisiti. 1 Introduzione

Funzioni in C. Violetta Lonati

Corso di Informatica (Programmazione) Lezione 6 (31 ottobre 2008)

Modulo 3 - Elaborazione Testi 3.5 Stampa unione

(Esercizi Tratti da Temi d esame degli ordinamenti precedenti)

Sistema Banca dati e Repertorio dei dispositivi medici Notifiche multiple di DM simili

Programmazione Orientata agli Oggetti in Linguaggio Java

SOMMARIO... 3 INTRODUZIONE...

Capitolo 3 Guida operativa del programma TQ Sistema

Introduzione alla programmazione in C

EXCEL PER WINDOWS95. sfruttare le potenzialità di calcolo dei personal computer. Essi si basano su un area di lavoro, detta foglio di lavoro,

Procedura SMS. Manuale Utente

METODI per effettuare previsioni con analisi di tipo WHAT-IF

Manuale d'uso del Connection Manager

Prova di informatica & Laboratorio di Informatica di Base

GESTIONE DEI PROCESSI

Manuale Utente MyFastPage

Manuale NetSupport v Liceo G. Cotta Marco Bolzon

Console di Amministrazione Centralizzata Guida Rapida

Programmazione a Oggetti Lezione 10. Ereditarieta

Compilatore risorse display grafico LCD serie IEC-line

TERMINALE. Creazione e gestione di una postazione terminale di Eureka

L amministratore di dominio

Introduzione. Installare EMAS Logo Generator

Il calendario di Windows Vista

SendMedMalattia v Manuale d uso

Network Services Location Manager. Guida per amministratori di rete

Manuale Amministratore Legalmail Enterprise. Manuale ad uso degli Amministratori del Servizio Legalmail Enterprise

Con il termine Sistema operativo si fa riferimento all insieme dei moduli software di un sistema di elaborazione dati dedicati alla sua gestione.

SAPIENZA Università di Roma Facoltà di Ingegneria dell Informazione, Informatica e Statistica

Il tuo manuale d'uso. LEXMARK X502N

Esercitazione n 4. Obiettivi

per immagini guida avanzata Uso delle tabelle e dei grafici Pivot Geometra Luigi Amato Guida Avanzata per immagini excel

Gestione Risorse Umane Web

Installazione di GFI WebMonitor

Esame di Informatica CHE COS È UN FOGLIO ELETTRONICO CHE COS È UN FOGLIO ELETTRONICO CHE COS È UN FOGLIO ELETTRONICO. Facoltà di Scienze Motorie

Chat. Si ha un server in ascolto sulla porta Quando un client richiede la connessione, il server risponde con: Connessione accettata.

Tabelle di riferimento Pulsanti Inserire documento Predisposizione doc Approvazione Doc Numerazione Doc Pubblicazione Albo Webservice

Protocolli applicativi: FTP

GUIDA ALLA CONFIGURAZIONE E ALL UTILIZZO

Gestione dell account AdWords di Google Guida pratica

GUIDA ALL UTILIZZO DEL PORTALE DELLA RETE DEI COMUNI OGLIO PO

5.3 TABELLE RECORD Inserire, eliminare record in una tabella Aggiungere record Eliminare record

GUIDA UTENTE WEB PROFILES

Guida Software GestioneSpiaggia.it

Transcript:

34 Creazione di un download manager in Java Se ci si è collegati a Internet con una connessione telefonica, probabilmente si è sperimentata l interruzione di un download, con la necessità di ricominciare da zero. Le disconnessioni della linea o i crash del computer possono interrompere un download. Tuttavia, anche con una connessione ad alta velocità si possono verificare interruzioni della trasmissione. Come minimo, il riavvio di un download può far perdere tempo ed essere un esperienza frustrante. Un fatto a volte sottovalutato è che molti download interrotti si possono riprendere. Ciò consente di riavviare il download dal punto in cui si è interrotto invece di iniziare di nuovo. Nel presente capitolo si sviluppa uno strumento dal nome Download Manager che gestisce i download da Internet e semplicemente riprende i download interrotti. Consente inoltre di fermare e riprendere un download e di gestire download multipli contemporaneamente. Al centro dell utilità del Download Manager si trova la capacità di sfruttare lo scaricamento di parti specifiche di un file. In uno scenario di download classico, un intero file viene scaricato dall inizio alla fine. Se la trasmissione del file viene interrotta per qualche motivo, si perde l avanzamento verso il completamento del download. Il Download Manager, tuttavia, può riprendere dal punto in cui si è verificata un interruzione e scaricare solo la parte restante del file. Non tutti i download vengono creati allo stesso modo e alcuni non possono semplicemente essere ripresi. I dettagli sui file che non possono essere ripresi sono spiegati nel prossimo paragrafo. Non solo il Download Manager è un utility utile, ma illustra anche in modo eccellente la potenza e la concisione delle API incorporate di Java, in particolare per l interfacciamento a Internet. Poiché Internet è stata una delle forze dietro la

40 Capitolo 34 creazione di Java, non sorprende che le capacità di rete di Java siano insuperabili. Per esempio, il tentativo di creare il Download Manager in un altro linguaggio, quale C++, comporterebbe molti più problemi e lavoro. 34.1 I download da Internet Per capire e apprezzare il Download Manager è necessario capire come funzionano i download da Internet. Nella loro forma più semplice i download da Internet sono semplicemente transazioni client/server. Il client, il browser, richiede di scaricare un file da un server su Internet. Il server risponde inviando il file richiesto al browser. Affinché i client possano comunicare con i server, devono avere un protocollo definito. I protocolli più utilizzati per scaricare file sono File Transfer Protocol (FTP, protocollo di trasferimento di file) e Hypertext Transfer Protocol (HTTP, protocollo di trasferimento di ipertesti). Solitamente FTP è associato allo scambio di file tra computer, mentre HTTP è associato specificatamente al trasferimento di pagine web e dei relativi file (grafiche, suoni e così via). Nel tempo, con la crescita di popolarità del World Wide Web, HTTP è diventato il protocollo dominante per lo scaricamento di file da Internet, ma FTP non è completamente estinto. Per motivi di spazio il Download Manager sviluppato nel presente capitolo supporta solo download HTTP. Tuttavia l aggiunta di supporto per FTP è un ottimo esercizio per l estensione del codice. I download HTTP ha due forme: riprendibile (HTTP 1.1) e non riprendibile (HTTP 1.0). La differenza tra le due forme sta nel modo in cui è possibile richiedere i file dai server. Con il vecchio HTTP 1.0, un client può richiedere solo che un server invii un file, mentre con HTTP 1.1 un client può richiedere che un server invii un file completo o solo una parte specifica di un file. Si tratta della caratteristica su cui è creato il Download Manager. 34.2 Una panoramica sul Download Manager Il Download Manager utilizza una semplice ma efficace interfaccia GUI realizzata con le librerie Swing di Java. La finestra del Download Manager è mostrata nella Figura 34.1. L utilizzo di Swing fornisce all interfaccia un aspetto moderno. La GUI mantiene un elenco di download gestiti. Ogni download nell elenco indica l URL, le dimensioni del file in byte, l avanzamento come percentuale verso il completamento e lo stato corrente. I download si possono trovare in uno degli stati indicati di seguito: Downloading (in scaricamento), Paused (in pausa), Complete (completo), Error (errore) o Cancelled (annullato). La GUI dispone inoltre di controlli per aggiungere download all elenco e per modificare lo stato di ogni download nell elenco. Quando si seleziona un download nell elenco, secondo lo stato corrente può essere messo fermato, ripreso, annullato o eliminato dall elenco. Il Download Manager è suddiviso in classi per la separazione naturale dei componenti funzionali: Download, DownloadsTableModel, ProgressRenderer e DownloadManager. La classe DownloadManager è responsabile dell interfaccia GUI e utilizza le classi DownloadsTableModel e ProgressRenderer per la visua-

Creazione di un download manager in Java 41 lizzazione dell elenco di download correnti. La classe Download rappresenta un download gestito ed è responsabile dello scaricamento di un file. Nei paragrafi di seguito si esamina in dettaglio ogni classe, evidenziandone il funzionamento e spiegandone il rapporto con le altre. Figura 34.1 L interfaccia GUI del Download Manager. 34.3 La classe Download La classe Download è il cavallo da tiro del Download Manager. Lo scopo primario è scaricare un file e salvarne i contenuti nel disco. Ogni volta che si aggiunge un nuovo download al Download Manager, viene creato un nuovo oggetto Download per gestirlo. Il Download Manager è in grado di scaricare più file contemporaneamente. Per farlo è necessario che ogni download sia eseguito in modo indipendente. È inoltre necessario che ogni singolo download gestisca il proprio stato in modo che possa essere riflesso nella GUI. Ciò si ottiene con la classe Download. Il codice completo per Download è mostrato di seguito. Si noti che estende Observable e implementa Runnable. Ogni parte si esamina in dettaglio nei paragrafi successivi. PARTE IV import java.io.*; import java.net.*; import java.util.*; // Questa classe scarica un file da un URL. class Download extends Observable implements Runnable { // Dimensioni max del buffer di download. private static final int MAX_BUFFER_SIZE = 1024; // Questi sono i nomi degli stati.

42 Capitolo 34 public static final String STATUSES[] = { Downloading, Paused, Complete, Cancelled, Error ; // Questi sono i codici degli stati. public static final int DOWNLOADING = 0; public static final int PAUSED = 1; public static final int COMPLETE = 2; public static final int CANCELLED = 3; public static final int ERROR = 4; private URL url; // URL di download private int size; // dimensioni del download in byte private int downloaded; // numero di byte scaricati private int status; // stato corrente del download // Costruttore per Download. public Download(URL url) { this.url = url; size = -1; downloaded = 0; status = DOWNLOADING; // Inizia il download. download(); // Otiene l URL del download. public String geturl() { return url.tostring(); // Otiene le dimensioni del download. public int getsize() { return size; // Ottiene l avanzamento del download. public float getprogress() { return ((float) downloaded / size) * 100; // Ottiene lo stato del download. public int getstatus() { return status; // Ferma il download. public void pause() { status = PAUSED; statechanged();

Creazione di un download manager in Java 43 // Riprende il download. public void resume() { status = DOWNLOADING; statechanged(); download(); // Annulla il download. public void cancel() { status = CANCELLED; statechanged(); // Contrassegna il download come con errore. private void error() { status = ERROR; statechanged(); // Avvia o riprende il download. private void download() { Thread thread = new Thread(this); thread.start(); // Ottiene la parte di nome di file dell URL. private String getfilename(url url) { String filename = url.getfile(); return filename.substring(filename.lastindexof( / ) + 1); // Scarica il file. public void run() { RandomAccessFile file = null; InputStream stream = null; PARTE IV try { // Apre connessione verso l URL. HttpURLConnection connection = (HttpURLConnection) url.openconnection(); // Specifica la parte di file da scaricare. connection.setrequestproperty( Range, bytes= + downloaded + - ); // Si connette al server. connection.connect(); // Verifica che il codice di risposta sia nell intervallo 200.

44 Capitolo 34 if (connection.getresponsecode() / 100!= 2) { error(); // Controlla la validità della lunghezza dei contenuti. int contentlength = connection.getcontentlength(); if (contentlength < 1) { error(); /* Imposta le dimensioni per il download se non sono già impostate. */ if (size == -1) { size = contentlength; statechanged(); // Apre il file e ne cerca la fine. file = new RandomAccessFile(getFileName(url), rw ); file.seek(downloaded); stream = connection.getinputstream(); while (status == DOWNLOADING) { /* Dimensiona il buffer secondo la quantità di file restata da scaricare. */ byte buffer[]; if (size - downloaded > MAX_BUFFER_SIZE) { buffer = new byte[max_buffer_size]; else { buffer = new byte[size - downloaded]; // Legge dal server nel buffer. int read = stream.read(buffer); if (read == -1) break; // Scrive il buffer nel file. file.write(buffer, 0, read); downloaded += read; statechanged(); /* Cambia lo stato in completo se questo punto è stato raggiunto perché è finito il download. */ if (status == DOWNLOADING) { status = COMPLETE;

Creazione di un download manager in Java 45 statechanged(); catch (Exception e) { error(); finally { // Chiude il file. if (file!= null) { try { file.close(); catch (Exception e) { // Chiude la connessine al server. if (stream!= null) { try { stream.close(); catch (Exception e) { // Segnala agli osservatori che lo stato del download è cambiato. private void statechanged() { setchanged(); notifyobservers(); Le variabili di Download Download inizia dichiarando numerose variabili static final che specificano le diverse costanti utilizzate dalla classe, quindi sono dichiarate quattro variabili di istanza. La variabile url contiene l URL Internet per il file da scaricare, la variabile size contiene le dimensioni del file da scaricare in byte; la variabile downloaded contiene il numero di byte scaricati e la variabile status indica lo stato corrente del download. PARTE IV Il costruttore di Download Al costruttore di Download si passa un riferimento all URL da scaricare sotto forma di un oggetto URL, assegnato alla variabile di istanza url. Si impostano quindi le variabili di istanza restanti sui relativi stati iniziali e si chiama il metodo download(). Si noti che size è impostata su -1 a indicare che non sono ancora presenti le dimensioni. Il metodo download() Il metodo download() crea un nuovo oggetto Thread, passando ad esso un riferimento all istanza di Download chiamante. Come detto in precedenza, è necessario che ogni download sia eseguito in modo indipendente. Affinché la classe

46 Capitolo 34 Download agisca da sola, deve essere eseguita nel proprio thread. Java dispone di un eccellente supporto incorporato per i thread e ne facilita l utilizzo. Per utilizzare i thread, la classe Download implementa semplicemente l interfaccia Runnable ridefinendo il metodo run(). Quando il metodo download() ha creato una nuova istanza di Thread, passando al costruttore la classe Download Runnable, chiama il metodo start() del thread. La chiamata al metodo start() fa in modo che sia eseguito il metodo run() dell istanza Runnable (della classe Download). Il metodo run() Quando viene eseguito il metodo run(), inizia il download effettivo. A causa delle dimensioni e dell importanza di tale metodo, viene esaminato in dettaglio. Il metodo run() inizia con le righe mostrate di seguito: RandomAccessFile file = null; InputStream stream = null; try { // Apre connessione verso l URL. HttpURLConnection connection = (HttpURLConnection) url.openconnection(); run() imposta le variabili per il flusso di rete da cui vengono letti i contenuti del download e imposta il file in cui si scrivono i contenuti del download. Quindi apre una connessione verso l URL del download chiamando url.openconnection(). Poiché è noto che il Download Manager supporta solo download HTTP, la connessione viene convertita nel tipo HttpURLConnection. La conversione della connessione in HttpURLConnection consente di sfruttare le funzioni di connessione specifiche per HTTP quali il metodo getresponsecode(). Si noti che la chiamata a url.openconnection() non crea una connessione verso il server dell URL; crea semplicemente una nuova istanza URLConnection associata all URL che in seguito verrà utilizzato per la connessione al server. Dopo la creazione di HttpURLConnection, si imposta la proprietà di richiesta della connessione chiamando connection.setrequestproperty(), come mostrato di seguito: // Specifica la parte di file da scaricare. connection.setrequestproperty( Range, bytes= + downloaded + - ); L impostazione delle proprietà di richiesta consente di inviare informazioni extra sulla richiesta al server da cui proviene il download. In questo caso si imposta la proprietà Range. È molto importante, poiché la proprietà Range specifica l intervallo di byte richiesti per il download dal server. Normalmente vengono scaricati tutti i byte del file in una sola volta. Tuttavia, se un download è stato interrotto o fermato, si devono recuperare solo i byte restanti del download. L impostazione della proprietà Range è la base del funzionamento del Download Manager.

Creazione di un download manager in Java 47 La proprietà Range è specificata come di seguito: byte iniziale-byte finale Per esempio, 0-12345. Tuttavia il byte finale dell intervallo è opzionale. Se il byte finale è assente, l intervallo termina alla fine del file. Il metodo run() non specifica mai il byte finale perché i download devono essere eseguiti fino allo scaricamento dell intero intervallo, se non vengono messi in pausa o interrotti. Le righe successive sono mostrate di seguito: // Si connette al server. connection.connect(); // Verifica che il codice di risposta sia nell intervallo 200. if (connection.getresponsecode() / 100!= 2) { error(); // Controlla la validità della lunghezza dei contenuti. int contentlength = connection.getcontentlength(); if (contentlength < 1) { error(); Viene chiamato il metodo connection.connect() per effettuare la connessione al server di download. Quindi si controlla il codice di risposta restituito dal server. Il protocollo HTTP ha un elenco di codici di risposta che indicano la risposta di un server a una richiesta. I codici di risposta HTTP sono organizzati in intervalli numerici di cento in cento e l intervallo 200 indica il successo. Si controlla che codice di risposta del server sia nell intervallo 200 chiamando connection.getresponsecode() e dividendolo per 100. Se il valore della divisione è 2, la connessione è stata effettuata con successo. Quindi run() ottiene la lunghezza del contenuto chiamando connection.get- ContentLength(). La lunghezza del contenuto rappresenta il numero di byte nel rile richiesto. Se la lunghezza del contenuto è minore di 1, viene chiamato il metodo error(), che aggiorna lo stato del download su ERROR, quindi chiama state- Changed(). Il metodo statechanged() è descritto in dettaglio più avanti. Dopo avere ottenuto la lunghezza del contenuto, il codice di seguito controlla se è già stato assegnato alla variabile size: PARTE IV /* Imposta le dimensioni per il download se non sono già impostate. */ if (size == -1) { size = contentlength; statechanged();

48 Capitolo 34 Come si può osservare, invece di assegnare la lunghezza del contenuto alla variabile size in modo incondizionato, viene assegnata solo se non ha già un valore. Il motivo è che la lunghezza del contenuto riflette il numero di byte che il server invierà. Se si specifica un elemento diverso da un inizio di intervallo basato su 0, la lunghezza del contenuto rappresenta solo una parte delle dimensioni del file. La variabile size deve essere impostata sulle dimensioni complete del file di download. Le righe di codice successive mostrate di seguito creano un nuovo RandomAccessFile utilizzando la parte di nome di file dell URL del download recuperato con una chiamata al metodo getfilename(): // Apre il file e ne cerca la fine. file = new RandomAccessFile(getFileName(url), rw ); file.seek(downloaded); Il RandomAccessFile viene aperto in modalità rw, che specifica che è possibile leggere il file e scrivere in esso. Quando il file è aperto, run() cerca la fine del file chiamando il metodo file.seek(), passando la variabile downloaded. Ciò indica al file di posizionarsi sul numero di byte scaricati, in altre parole alla fine. È necessario posizionare il file alla fine nel caso in cui un download sia stato ripreso. Se si riprende un download, i byte scaricati vengono aggiunti alla fine del file e non sovrascrivono i byte scaricati in precedenza. Dopo la preparazione del file di output, si ottiene un gestore del flusso di rete verso il server aperto chiamando connection.getinputstream(), come mostrato di seguito: stream = connection.getinputstream(); Inizia quindi il centro dell azione con un ciclo while: while (status == DOWNLOADING) { /* Dimensiona il buffer secondo la quantità di file restata da scaricare. */ byte buffer[]; if (size - downloaded > MAX_BUFFER_SIZE) { buffer = new byte[max_buffer_size]; else { buffer = new byte[size - downloaded]; // Legge dal server nel buffer. int read = stream.read(buffer); if (read == -1) break; // Scrive il buffer nel file. file.write(buffer, 0, read); downloaded += read; statechanged();

Creazione di un download manager in Java 49 Il ciclo viene impostato per essere eseguito fino a quando la variabile status è diversa da DOWNLOADING. Nel ciclo si crea un array di buffer byte per contenere i byte da scaricare. Il buffer è dimensionato secondo la quantità di download restante per il completamento. Se si deve scaricare una quantità di byte maggiore di MAX_BUFFER_SIZE, per dimensionare il buffer si utilizza MAX_BUFFER_ SIZE, altrimenti il buffer viene dimensionato sul numero di byte ancora da scaricare. Quando il buffer è stato dimensionato correttamente, ha luogo il download con una chiamata a stream.read(). La chiamata legge byte dal server e li inserisce nel buffer, restituendo il conteggio dei byte effettivamente letti. Se il numero di byte letti è uguale a -1, il download è completo e si esce dal ciclo, altrimenti il download non è terminato e i byte letti vengono scritti su disco con una chiamata a file.write(). Quindi si aggiorna la variabile downloaded, in modo da riflettere il numero di byte scaricati. Infine, all interno del ciclo, si chiama il metodo state- Changed(). Ulteriori dettagli più avanti. Dopo l uscita dal ciclo, il codice di seguito controlla perché si è usciti dal ciclo: /* Cambia lo stato in completo se questo punto è stato raggiunto perché è finito il download. */ if (status == DOWNLOADING) { status = COMPLETE; statechanged(); Se lo stato del download è ancora DOWNLOADING, significa che si è usciti dal ciclo perché il download è stato completato, altrimenti si è usciti dal ciclo perché è cambiato lo stato del download in un valore diverso da DOWNLOADING. Il metodo run() finisce con i blocchi catch e finally mostrati di seguito: catch (Exception e) { error(); finally { // Chiude il file. if (file!= null) { try { file.close(); catch (Exception e) { // Chiude la connessine al server. if (stream!= null) { try { stream.close(); catch (Exception e) { PARTE IV Se durante il processo di scaricamento viene lanciata un eccezione, il blocco catch rileva l eccezione e chiama il metodo error(). Il blocco finally garantisce che

50 Capitolo 34 se le connessioni file e stream sono state aperte, vengano chiuse, che sia stata lanciata un eccezione o no. Come esercizio, si può provare a modificare il codice in modo da utilizzare la nuova dichiarazione try con risorse per gestire tali risorse. Il metodo statechanged() Affinché il Download Manager visualizzi informazioni aggiornate sui download che gestisce, deve sapere quando cambiano le informazioni su un download. Per gestire tale situazione si utilizza lo schema di design Observer. Lo schema Observer è analogo a una mailing list di annunci in cui le persone si registrano per ricevere annunci. Ogni volta che esiste un nuovo annuncio, tutte le persone nell elenco ricevono un messaggio con l annuncio. Nel caso dello schema Observer esiste una classe osservata presso cui le classi osservatrici possono registrarsi per ricevere notifiche di modifiche. La classe Download utilizza lo schema Observer estendendo la classe di utility incorporata Observable di Java. L estensione della classe Observable consente alle classi che implementano l interfaccia Observer di Java di registrarsi presso la classe Download per ricevere notifiche di modifiche. Ogni volta che la classe Download deve notificare un cambiamento agli Observer registrati, viene chiamato il metodo statechanged(). Il metodo statechanged() chiama prima il metodo setchanged() della classe Observable per contrassegnare la classe come modificata. Quindi il metodo statechanged() chiama il metodo notifyobservers() di Observable, che trasmette la notifica di cambiamento agli Observer registrati. Metodi di azione e accessori La classe Download dispone di numerosi metodi di azione e accessori per controllare un download e ottenere dati da esso. Ogni metodo di azione pause(), resume() e cancel() fa semplicemente ciò che implica il nome: ferma, riprende o annulla il download, rispettivamente. Analogamente, il metodo error() contrassegna il download come con un errore. I metodi accessori geturl(), getsize(), getprogress() e getstatus() restituiscono i rispettivi valori correnti. 34.4 La classe ProgressRenderer La classe ProgressRenderer è una piccola classe di utility utilizzata per visualizzare l avanzamento corrente di un download elencato nell istanza JTable Downloads della GUI. Normalmente un istanza JTable visualizza i dati di ogni cella come testo. Tuttavia, spesso è particolarmente utile visualizzare i dati di una cella in modo diverso dal testo. Nel caso del Download Manager si desidera visualizzare ogni cella della colonna Progress della tabella come una barra di avanzamento. La classe ProgressRenderer mostrata di seguito lo rende possibile. Si noti che estende JProgressBar e implementa TableCellRenderer. import java.awt.*; import javax.swing.*; import javax.swing.table.*; // Questa classe visualizza una JProgressBar in una cella in tabella.

Creazione di un download manager in Java 51 class ProgressRenderer extends JProgressBar implements TableCellRenderer { // Costruttore per ProgressRenderer. public ProgressRenderer(int min, int max) { super(min, max); /* Restituisce questa JProgressBar come visualizzatore per la cella di tabella data. */ public Component gettablecellrenderercomponent( JTable table, Object value, boolean isselected, boolean hasfocus, int row, int column) { // Imposta il valore di completamento in percentuale di JProgressBar. setvalue((int) ((Float) value).floatvalue()); return this; La classe ProgressRenderer sfrutta il fatto che la classe JTable di Swing ha un sistema di rendering in grado di accettare plug-in per la visualizzazione di celle di tabella. Per inserire tale sistema di rendering, la classe ProgressRenderer deve implementare l interfaccia TableCellRenderer di Swing. Quindi è necessario registrare un istanza di ProgressRenderer presso un istanza di JTable; in tal modo indica all istanza JTable quali celle deve visualizzare con il plug-in. L implementazione dell interfaccia TableCellRenderer richiede che la classe ridefinisca il metodo gettablecellrenderercomponent(), chiamato ogni volta che un istanza JTable deve visualizzare una cella per cui la classe è stata registrata. Al metodo si passano numerose variabili, ma in questo caso si utilizza solo la variabile value, che contiene i dati per la cella da visualizzare e viene passata al metodo setvalue() di JProgressBar. Il metodo gettablecellrenderercomponent() termina restituendo un riferimento alla relativa classe. Funziona perché la classe ProgressRenderer è una sottoclasse di JProgressBar, discendente della classe AWT Component. PARTE IV 34.6 La classe DownloadsTableModel La classe DownloadsTableModel contiene l elenco di download del Download Manager ed è l origine di dati che supporta l istanza di JTable Downloads della GUI. La classe DownloadsTableModel è mostrata di seguito. Si noti che estende AbstractTableModel e implementa l interfaccia Observer. import java.util.*; import javax.swing.*; import javax.swing.table.*;

52 Capitolo 34 // Questa classe gestisce i dati della tabella di download. class DownloadsTableModel extends AbstractTableModel implements Observer { // Questi sono i nomi delle colonne della tabella. private static final String[] columnnames = { URL, Size, Progress, Status ; // Queste sono le classi per i valori di ogni colonna. private static final Class[] columnclasses = {String.class, String.class, JProgressBar.class, String.class; // L elenco di download della tabella. private ArrayList<Download> downloadlist = new ArrayList<Download>(); // Aggiunge un nuovo download alla tabella. public void adddownload(download download) { // Si registra per essere avvisto quando cambia il download. download.addobserver(this); downloadlist.add(download); 1); // Invia alla tabella notifica di inserimento di riga. firetablerowsinserted(getrowcount() - 1, getrowcount()- // Ottiene un download per la riga specificata. public Download getdownload(int row) { return downloadlist.get(row); // Elimina un download dall elenco. public void cleardownload(int row) { downloadlist.remove(row); // Invia alla tabella notifica di eliminazione di riga. firetablerowsdeleted(row, row); // Ottiene conteggio di colonne della tabella. public int getcolumncount() { return columnnames.length; // Ottiene il nome di una colonna. public String getcolumnname(int col) {

Creazione di un download manager in Java 53 return columnnames[col]; // Ottiene la classe di una colonna. public Class<?> getcolumnclass(int col) { return columnclasses[col]; // Ottiene conteggio di righe della tabella. public int getrowcount() { return downloadlist.size(); // Ottiene valore per una combinazione specifica di riga e di colonna. public Object getvalueat(int row, int col) { Download download = downloadlist.get(row); switch (col) { case 0: // URL return download.geturl(); case 1: // Dimensioni int size = download.getsize(); return (size == -1)? : Integer.toString(size); case 2: // Avanzamento return new Float(download.getProgress()); case 3: // Stato return Download.STATUSES[download.getStatus()]; return ; /* Update viene chiamato quando un Download informa i suoi osservatori di modifiche */ public void update(observable o, Object arg) { int index = downloadlist.indexof(o); PARTE IV // Invia alla tabella notifica di aggiornamento di riga. firetablerowsupdated(index, index); In sostanza la classe DownloadsTableModel è una classe di utility utilizzata dall istanza JTable Downloads per gestire i dati nella tabella. Quando si inizializza l istanza JTable, si passa un istanza DownloadsTableModel. JTable chiama quindi numerosi metodi sull istanza DownloadsTableModel per la compilazione della tabella. Il metodo getcolumncount() viene chiamato per recuperare il numero di colonne nella tabella. Analogamente, getrowcount() è utilizzato per recuperare il numero di righe nella tabella. Il metodo getcolumnname() restituisce il nome di una colonna dato l ID. Il metodo getdownload() prende un ID

54 Capitolo 34 di riga e restituisce l oggetto Download associato dall elenco. Gli altri metodi della classe DownloadsTableModel, più complessi, sono descritti in dettaglio nei paragrafi successivi. Il metodo adddownload() Il metodo adddownload(), mostrato di seguito, aggiunge un nuovo oggetto Download all elenco di download gestiti, quindi aggiunge una riga nella tabella: // Aggiunge un nuovo download alla tabella. public void adddownload(download download) { // Si registra per essere avvisto quando cambia il download. download.addobserver(this); downloadlist.add(download); // Invia alla tabella notifica di inserimento di riga. firetablerowsinserted(getrowcount() - 1, getrowcount() - 1); Il metodo si registra presso il nuovo Download come Observer interessato a ricevere notifiche di cambiamenti. Quindi il Download viene aggiunto all elenco interno di download gestiti. Infine viene inviata una notifica di evento di inserimento di riga nella tabella per indicare alla tabella che è stata aggiunta una nuova riga. Il metodo cleardownload() Il metodo cleardownload(), mostrato di seguito, elimina un Download dall elenco di download gestiti: // Elimina un download dall elenco. public void cleardownload(int row) { downloadlist.remove(row); // Invia alla tabella notifica di eliminazione di riga. firetablerowsdeleted(row, row); Dopo avere eliminato il Download dall elenco interno, viene inviata una notifica di eliminazione di riga di tabella per indicare alla tabella che è stata eliminata una riga. Il metodo getcolumnclass() Il metodo getcolumnclass(), mostrato di seguito, restituisce il tipo di classe per i dati visualizzati nella colonna specificata: // Ottiene la classe di una colonna. public Class<?> getcolumnclass(int col) { return columnclasses[col];

Creazione di un download manager in Java 55 Tutte le colonne sono visualizzate come testo (vale a dire oggetti String) ad eccezione della colonna Progress, visualizzata come barra di avanzamento (un oggetto di tipo JProgressBar). Il metodo getvalueat() Il metodo getvalueat(), mostrato di seguito, viene chiamato per ottenere il valore corrente da visualizzare per ogni cella della tabella: // Ottiene valore per una combinazione specifica di riga e di colonna. public Object getvalueat(int row, int col) { Download download = downloadlist.get(row); switch (col) { case 0: // URL return download.geturl(); case 1: // Dimensioni int size = download.getsize(); return (size == -1)? : Integer.toString(size); case 2: // Avanzamento return new Float(download.getProgress()); case 3: // Stato return Download.STATUSES[download.getStatus()]; return ; Il metodo prima cerca il Download corrispondente alla riga specificata, quindi si utilizza la colonna specificata per determinare quale valore delle proprietà di Download restituire. Il metodo update() Il metodo update() è mostrato di seguito. Adempie al contratto con l interfaccia Observer consentendo alla classe DownloadsTableModel di ricevere notifiche dagli oggetti Download quando cambiano. PARTE IV /* Update viene chiamato quando un Download informa i suoi osservatori di modifiche */ public void update(observable o, Object arg) { int index = downloadlist.indexof(o); // Invia alla tabella notifica di aggiornamento di riga. firetablerowsupdated(index, index); Al metodo si passa un riferimento al Download cambiato, sotto forma di oggetto Observable. Quindi si cerca un indice verso tale download nell elenco dei download e si utilizza tale indice per inviare una notifica di evento di aggiornamento di riga di tabella, che indica alla tabella che la riga data è stata modificata. La tabella visualizza la riga con l indice dato, riflettendo i nuovi valori.

56 Capitolo 34 34.6 La classe DownloadManager Gettate le basi con la spiegazione di tutte le classi helper del Download Manager, è possibile esaminare la classe DownloadManager, responsabile della creazione e dell esecuzione della GUI del Download Manager. La classe dispone di un metodo main(), il primo dichiarato durante l esecuzione. Il metodo main() crea una nuova istanza della classe DownloadManager e chiama il relativo metodo show(), che la visualizza. La classe DownloadManager è mostrata di seguito. Si noti che estende JFrame e implementa Observer. I paragrafi di seguito la esaminano in dettaglio. import java.awt.*; import java.awt.event.*; import java.net.*; import java.util.*; import javax.swing.*; import javax.swing.event.*; // Il Download Manager. public class DownloadManager extends JFrame implements Observer { // Aggiunge i campi di testo per i download. private JTextField addtextfield; // Il modello di dati della tabella di download. private DownloadsTableModel tablemodel; // Tabella che elenca i download. private JTable table; // Questi sono i pulsanti per la gestione del download selezionato. private JButton pausebutton, resumebutton; private JButton cancelbutton, clearbutton; // Download selezionato. private Download selecteddownload; // Flag per sapere se la selezione di tabella deve essere annullata. private boolean clearing; // Costruttore per Download Manager. public DownloadManager() { // Imposta titolo dell applicazione. settitle( Download Manager );

Creazione di un download manager in Java 57 // Imposta dimensioni della finestra. setsize(640, 480); // Gestisce eventi di chiusura della finestra. addwindowlistener(new WindowAdapter() { public void windowclosing(windowevent e) { actionexit(); ); // Imposta il menu di file. JMenuBar menubar = new JMenuBar(); JMenu filemenu = new JMenu( File ); filemenu.setmnemonic(keyevent.vk_f); JMenuItem fileexitmenuitem = new JMenuItem( Exit, KeyEvent.VK_X); fileexitmenuitem.addactionlistener(new ActionListener() { public void actionperformed(actionevent e) { actionexit(); ); filemenu.add(fileexitmenuitem); menubar.add(filemenu); setjmenubar(menubar); // Imposta pannello di aggiunta. JPanel addpanel = new JPanel(); addtextfield = new JTextField(30); addpanel.add(addtextfield); JButton addbutton = new JButton( Add Download ); addbutton.addactionlistener(new ActionListener() { public void actionperformed(actionevent e) { actionadd(); ); addpanel.add(addbutton); PARTE IV // Imposta tabella di download. tablemodel = new DownloadsTableModel(); table = new JTable(tableModel); table.getselectionmodel().addlistselectionlistener(new ListSelectionListener() { public void valuechanged(listselectionevent e) { tableselectionchanged(); ); // Consente di selezionare una sola riga per volta. table.setselectionmode(listselectionmodel.single_selec- TION);

58 Capitolo 34 // Imposta ProgressBar come visualizzatore per colonna di avanzamento. ProgressRenderer renderer = new ProgressRenderer(0, 100); renderer.setstringpainted(true); // mostra testo avanzamento table.setdefaultrenderer(jprogressbar.class, renderer); // Imposta altezza riga di tabella sufficiente per contenere JProgressBar. table.setrowheight( (int) renderer.getpreferredsize().getheight()); // Imposta pannello di download. JPanel downloadspanel = new JPanel(); downloadspanel.setborder( BorderFactory.createTitledBorder( Downloads )); downloadspanel.setlayout(new BorderLayout()); downloadspanel.add(new JScrollPane(table), BorderLayout.CENTER); // Imposta pannello di pulsanti. JPanel buttonspanel = new JPanel(); pausebutton = new JButton( Pause ); pausebutton.addactionlistener(new ActionListener() { public void actionperformed(actionevent e) { actionpause(); ); pausebutton.setenabled(false); buttonspanel.add(pausebutton); resumebutton = new JButton( Resume ); resumebutton.addactionlistener(new ActionListener() { public void actionperformed(actionevent e) { actionresume(); ); resumebutton.setenabled(false); buttonspanel.add(resumebutton); cancelbutton = new JButton( Cancel ); cancelbutton.addactionlistener(new ActionListener() { public void actionperformed(actionevent e) { actioncancel(); ); cancelbutton.setenabled(false); buttonspanel.add(cancelbutton); clearbutton = new JButton( Clear ); clearbutton.addactionlistener(new ActionListener() { public void actionperformed(actionevent e) { actionclear();