Corso di Reti di Calcolatori UNICAL – Facoltà di Ingegneria – a.a. 2002/2003 Esercitazione sul networking in Java (2a parte) [email protected] 1 java.net.URL • URL (String spec) crea un oggetto URL a partire dalla stringa che lo rappresenta • URL (String protocol, String host, int port, String file) crea un URL a partire dai componenti specificati (port = -1 indica l’uso della porta standard per il protocollo specificato) • URLConnection openConnection() restituisce un oggetto URLConnection che gestisce la connessione diretta alla risorsa • InputStream openStream() apre un flusso di input per la lettura dei dati della risorsa 2 Recupero del contenuto di un sito Web import java.io.*; import java.net.*; public class URLTest { public static void main(String[] args) { try { URL url = new URL("http://www.deis.unical.it"); BufferedReader in = new BufferedReader (new InputStreamReader(url.openStream())); boolean more = true; while (more) { String line = in.readLine(); if (line == null) more = false; else System.out.println(line); } } catch (IOException e) { System.out.println("Error"+e); } } } 3 java.net.URLConnection (1) Per ottenere dalla risorsa informazioni aggiuntive e controllarne meglio l’accesso si usa la classe URLConnection. In generale per creare una URLConnection si seguono i seguenti passi: 1.Si chiama il metodo openConnection di un oggetto URL: URLConnection connection = url.openConnection(); 2.Si impostano le proprietà usando i metodi: setDoInput setDoOutput setIfModifiedSince setUseCaches setAllowUserInteraction setRequestProperty 3.Si effettua la connessione alla risorsa remota mediante il metodo connect: connection.connect(); Il metodo connect crea una connessione socket con il server e richiede al server le informazioni di intestazione. 4 java.net.URLConnection (2) 4.Dopo aver attivato la connessione è possibile richiedere le informazioni di intestazione. I metodi getHeaderFieldKey e getHeaderField consentono di accedere a tutti i campi di intestazione. I seguenti metodi interrogano i campi standard: getContentType getContentLength getContentEncoding getDate getExpiration getLastModified 5.Si accede alla risorsa remota, utilizzando il metodo getInputStream per ottenere un flusso di input per leggere le informazioni, ed il metodo getOutputStream per ottenere un flusso di output per inviare informazioni. 5 java.net.URLConnection (3) • void setDoInput(boolean doInput) se doInput è true, l’utente può ricevere l’input da questo URLConnection • void setDoOutput(boolean doOutput) se doOutput è true, l’utente può ricevere l’output da questo URLConnection • void setIfModifiedSince(long time) configura questo URLConnection per recuperare solo i dati che sono stati modificati dopo la data indicata • void setUseCaches(boolean useCaches) se useCaches è true, i dati possono essere recuperati da una cache locale • void setAllowUserInteraction(boolean allowUserInteraction) se allowUserInteraction è true, all’utente può essere richiesta una password • void setRequestProperty(String key, String value) imposta una proprietà della richiesta 6 java.net.URLConnection (4) • void connect() si connette alla risorsa remota e recupera le informazioni di intestazione • String getContentType() recupera il tipo di contenuto, per esempio text/plain o image/gif • int getContentLength() recupera la lunghezza del contenuto, oppure –1 se il valore è sconosciuto • String getContentEncoding() recupera la codifica del contenuto, per esempio gzip • long getDate() recupera la data di creazione della risorsa • long getExpiration() recupera la data di scadenza della risorsa • long getLastModified() recupera la data dell’ultima modifica della risorsa 7 java.net.URLConnection (5) • String getHeaderFieldKey(int n) recupera l’n-esima chiave del campo di intestazione • String getHeaderField(n) recupera l’n-esimo valore del campo di intestazione • InputStream getInputStream() restituisce uno stream per leggere la risorsa • OutputStream getOutputStream() restituisce uno stream per scrivere sulla risorsa 8 URLConnectionTest (1) import java.io.*; import java.net.*; import java.util.*; public class URLConnectionTest { public static void main(String[] args) { try { URL url = new URL("http://java.sun.com"); URLConnection connection = url.openConnection(); connection.connect(); // print header fields int n = 1; String key; while ((key = connection.getHeaderFieldKey(n)) != null) { String value = connection.getHeaderField(n); System.out.println(key + ": " + value); n++; } // print convenience functions System.out.println("----------"); System.out.println("getContentType: "+connection.getContentType()); 9 URLConnectionTest (2) System.out.println("getContentLength: "+connection.getContentLength()); System.out.println("getContentEncoding: " +connection.getContentEncoding()); System.out.println("getDate: "+connection.getDate()); System.out.println("getExpiration: "+connection.getExpiration()); System.out.println("getLastModifed: "+connection.getLastModified()); System.out.println("----------"); BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); // print first ten lines of contents String line; n = 1; while ((line = in.readLine()) != null && n <= 10) { System.out.println(line); n++; } if (line != null) System.out.println(". . ."); } catch (IOException exception) { System.out.println("Error: " + exception); } } } 10 URLConnectionTest (3) L’output del programma è il seguente: Server: Netscape-Enterprise/6.0 Date: Sat, 12 Oct 2002 23:15:45 GMT Content-type: text/html Connection: close ---------getContentType: text/html getContentLength: -1 getContentEncoding: null getDate: 1034453745000 getExpiration: 0 getLastModifed: 0 ---------<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <HTML> <HEAD> <TITLE>The Source for Java(TM) Technology</TITLE> ... 11 Invio di dati ad un server Web (1) Per inviare informazioni ad un server Web sono disponibili due metodi: GET e POST Con il metodo GET i parametri si allegano alla fine dell’indirizzo URL: http://host/procedura?name1=value1&name2=value2 I parametri sono separati tra loro da una &, e devono essere codificati secondo la specifica URL nel modo seguente: i caratteri alfanumerici ed i caratteri - _ . * non vengono modificati, mentre gli spazi vengono sostituiti dal carattere + e tutti gli altri sono rappresentati nel formato %UV dove 0xUV è il byte di ordine minore del carattere. Esiste una classe java.net.URLEncoder che offre due metodi: • static String encode (String s) restituisce la forma codificata secondo la specifica URL della stringa s • static String decode (String s) restituisce la stringa s decodificata 12 Invio di dati ad un server Web (2) Con il metodo GET si possono includere solo un numero limitato di caratteri. Il metodo POST non utilizza parametri, ma recupera un flusso di output da URLConnection e scrive le coppie nome/valore su questo flusso. Il processo per inviare dati ad una procedura con POST è il seguente: // si stabilisce una URLConnection URL url = new URL (“http://host/procedura”); URLConnection connection = url.openConnection(); // si prepara la connessione all’output connection.setDoOutput(true); // si ottiene un flusso di output e si trasmette la sequenza di dati al server PrintWriter out = new PrintWriter(connection.getOutputStream()); out.println(name1+”=“+URLEncoder.encode(value1)+”&”); out.println(name2+”=“+URLEncoder.encode(value2)+”\n”); out.close(); 13 Invio di dati ad un server Web (3) Quindi la risposta del server viene letta nel solito modo: BufferedReader in = new BufferedReader (new InputStreamReader (connection.getInputStream())); String line; while ((line = in.readLine()) != null) { elabora riga } 14 PostTest (1) La pagina Web http://www.census.gov/ipc/www/idbprint.html contiene un modulo per richiedere dati sulla popolazione dei diversi paesi: <form method=post action="/cgi-bin/ipc/idbsprd"> … <select name="tbl" size=8 > <option value="001">001 Total Midyear Population <option value="002">002 Urban Population as a Percent of Total Population … </select> </form> La procedura eseguita quando si preme “invio” è ”/cgi-bin/ipc/idbsprd“, ed è necessario usare il metodo POST per inviare dati alla procedura. Il componente dell’interfaccia che consente di scegliere l’opzione desiderata si chiama “tbl”. Scegliendo l’opzione “001” si ottiene la tabella sulla popolazione totale a metà anno. 15 PostTest (2) La pagina consente di impostare altri campi: “cty”, il cui valore indica il paese di cui si vogliono ottenere i dati “opyr”, il cui valore indica l’intervallo temporale prescelto (si usa il valore “latest checked” per ottenere i dati più recenti) Per recuperare i dati più recenti relativi alla popolazione della Cina si costruisce la stringa: tbl=1&cty=CH&optyr=latest+checked si invia la stringa all’indirizzo URL: http://www.census.gob/cgi-bin/ipc/idbsprd quindi si recupera l’output. Il programma seguente invia i dati via POST ad un URL arbitrario. I dati sono specificati nel file di proprietà PostTest.properties. 16 PostTest (3) import java.io.*; import java.net.*; import java.util.*; public class PostTest { public static void main(String[] args) { try { String fileName = "PostTest.properties"; Properties props = new Properties(); FileInputStream in = new FileInputStream(fileName); props.load(in); URL url = new URL(props.getProperty("URL")); props.remove("URL"); String r = doPost(url, props); System.out.println(r); } catch (IOException exception) { System.out.println("Error: " + exception); } } public static String doPost(URL url, Properties nameValuePairs) throws IOException { URLConnection connection = url.openConnection(); connection.setDoOutput(true); 17 PostTest (4) PrintWriter out = new PrintWriter(connection.getOutputStream()); Enumeration enum = nameValuePairs.keys(); while (enum.hasMoreElements()) { String name = (String)enum.nextElement(); String value = nameValuePairs.getProperty(name); char ch; if (enum.hasMoreElements()) ch = '&'; else ch = '\n'; out.print(name + "="+URLEncoder.encode(value) + ch); } out.close(); BufferedReader in; try { in = new BufferedReader (new InputStreamReader(connection.getInputStream())); } 18 PostTest (5) catch (FileNotFoundException exception) { InputStream err = ((HttpURLConnection)connection).getErrorStream(); if (err == null) throw exception; in = new BufferedReader(new InputStreamReader(err)); } StringBuffer response = new StringBuffer(); String line; while ((line = in.readLine()) != null) response.append(line + "\n"); in.close(); return response.toString(); } } 19 PostTest (6) Contenuto del file PostTest.properties: URL=http://www.census.gov/cgi-bin/ipc/idbsprd tbl=001 cty=CH optyr=latest checked 20 PostTest (7) L’output del programma è il seguente: <PRE> U.S. Bureau of the Census, International Data Base Table 001. Total Midyear Population -------------------------CtYear Population -------------------------- CH2002 1279160885 -------------------------Source: U.S. Bureau of the Census, International Data Base. </PRE> 21 Bibliografia • Cay S. Horstmann, Gary Cornell Java 2 Volume II – Tecniche avanzate Quarta Edizione – McGraw-Hill Capitolo 3: Comunicazione di Rete • The Java Tutorial: http://java.sun.com/docs/books/tutorial/networking 22