16.2 URL-Verbindungen und URL-Objekte
Eine Verbindung über eine URL (Uniform Resource Locator) ist am leichtesten zu benutzen und zu programmieren. Eine URL (RFC 1738) ist das Adressenformat für eine Ressource im Web. Sie ist also für das Internet so etwas wie ein Dateiname für das Dateisystem. Die allgemeinste Form einer URL ist folgende:
Schema:Spezialisierung des Schemas
Nach dem RFC 1738 werden folgende Schemas unterschieden:
Schema
|
Bedeutung des Schemas
|
ftp
|
File Transfer Protocol
|
http
|
Hypertext Transfer Protocol
|
gopher
|
Gopher Protocol
|
mailto
|
Elektronische Mail
|
news
|
USENET News
|
nntp
|
USENET News mit NNTP-Zugang
|
telnet
|
Interaktive Session
|
wais
|
Wide Area Information Servers
|
file
|
Hostspezifischer Dateiname
|
prospero
|
Prospero Directory Service
|
Tabelle 16.1 Schemas nach dem RFC 1738
Wir wollen ein Schema im Folgenden auch Protokoll nennen. Das Protokoll bestimmt die Zugriffsart, und das meistverwendete Protokoll ist mittlerweile HTTP (Hypertext Transfer Protocol), mit dem auf Inhalte des Webs zugegriffen wird. Die URL für die Dienste im Web beginnt mit »http«. Als Beispiel soll http://www.java-tutor.com/seminare/servlets.html dienen. Die URL enthält den Namen des benutzten Schemas (http), und nach einem Doppelpunkt folgt der Servername und ein Pfad auf das Verzeichnis. Sun unterstützt im Java-SDK die zusätzlichen Protokolle »doc«, »file«, »ftp«, »gopher«, »http«, »jar«, »mailto«, »system«, »verbatim«. Unterschiedliche Implementierungen haben mehr oder weniger unterstützte Protokolle. Eigene Protokolle lassen sich leicht selbst implementieren.
Während die Syntax für oben nicht genannte Adressierungen schwankt, lässt sich für die IP-basierten Protokolle (IP für »Internet Protocol«) eine Syntax für den schemenspezifischen Teil ausmachen. Dieser beginnt nach einem Doppel-Slash. Somit ist deutlich, dass diese Angabe dem Internet-Schema folgt:
//user:password@host:port/filepath#ref
Einige oder alle Teile können bei einer URL ausgelassen werden. So sind »user:password@«, »:password«, »:port«, »filepath« und »ref« optional und protokollabhängig. Sind Benutzername und das Passwort angegeben, so folgt ein At-Zeichen »@«. Natürlich dürfen im Passwort und Benutzernamen Doppelpunkt, At-Zeichen oder Slash nicht vorkommen.
Die einzelnen Komponenten haben folgende Bedeutung:
|
user
Ein optionaler Benutzername. Dieser kann für den Zugriff über ftp vergeben werden. |
|
password
Ein optionales Passwort. Es folgt getrennt durch einen Doppelpunkt nach dem Benutzernamen. Ein leerer Benutzername und ein leeres Passwort (etwa ftp://@host.com) sind etwas anders als kein Benutzername beziehungsweise kein Passwort (zum Beispiel ftp:// host.com/). Ein Passwort ohne Benutzername kann nicht angegeben werden. Umgekehrt natürlich schon: So hat ein ftp://oing:@host.com einen Benutzernamen, aber ein leeres Passwort. |
|
host
Auf die Angabe des Protokolls folgt der Name der Domain oder die IP-Adresse des Servers. Name und IP-Adressen sind in der Regel gleichwertig, da von einem besonderen Dienst der Name in eine IP-Adresse umgesetzt wird - verlangt doch eine achtstellige IP-Adresse ein zu gutes Gedächtnis. |
|
port
Eine Verbindung zu einem Rechner entsteht immer durch eine Art Tür, die Port genannt wird. Diese Port-Nummer lässt den Server die Dienste kategorisieren. Jeder Dienst bekommt eine andere Port-Nummer, damit sie sich unterscheiden lassen. Normalerweise horcht der HTTP-Server auf Port 80. |
|
url-path
Auf den Servernamen folgt die Angabe der Datei, auf die wir via HTTP oder FTP zugreifen wollen. Da sie in einem Verzeichnis liegen kann, beschreibt die folgende Angabe den Weg zur Datei. Ist keine Datei vorhanden und endet die Angabe der URL mit einem Slash »/«, so versucht der Web-Server, auf eine der Dateien index.html oder index.htm zuzugreifen. |
16.2.1 Die Klasse URL
Um ein URL-Objekt zu erzeugen, ist es am einfachsten, über eine String-Repräsentation der URL-Adresse zu gehen.
Beispiel Um eine Verbindung zum Host vom Java-Tutor zu erhalten, nehmen wir die bekannte URL und erzeugen damit das Objekt:
URL jtURL = new URL( ">http://java-tutor.com/index.html" );
|
Die URL-Klasse besitzt noch zusätzliche Konstruktoren; diese sind dann nützlich, wenn Komponenten der Adresse, also Zugriffsart (beispielsweise das HTTP), Host-Name und Dateireferenz getrennt gegeben sind.
Beispiel Eine Alternative zur oben genannten Form ist:
URL jtURL = new URL( "http", "java-tutor.com", "index.html" );
|
Das zweite Argument ist die Basisadresse der URL. Was dem String im dritten Parameter übergeben wird, ist der Ressourcen-Name relativ zur Basisadresse. Ist diese Basisadresse null, was möglich ist, dann ist die zweite Angabe absolut zu nehmen. Und ist der zweite Parameter in absoluter Notation formuliert, wird alles im ersten Parameter ignoriert.
Da eine URL auch einen entfernten Rechner an einem anderen Port ansprechen kann, existiert dafür ebenfalls ein Konstruktor:
URL url = new URL( "http", "java-tutor.com", 80, "index.html" );
Die URL des Objekts wurde durch eine absolute Adresse erzeugt. Diese enthält dann alle Informationen, die für den Aufbau zum Host nötig sind. Es können jedoch auch URL-Objekte erzeugt werden, wo nur eine relative Angabe bekannt ist. Relative Angaben werden häufig bei HTML-Seiten verwendet, da die Seite so besser vor Verschiebungen geschützt ist. Damit die Erzeugung eines URL-Objekts mit relativer Adressierung gelingt, muss eine Basisadresse bekannt sein. Ein Konstruktor für relative Adressen erwartet diese Basisadresse als Parameter.
Beispiel Für die Java-Tutor-Seite nutzen wir ein URL-Objekt, welches auf die Datei index.html zeigt:
URL jtURL = new URL( ">http://java-tutor.com" );
URL jtIndex = new URL( jtURL, "index.html");
|
Diese Art und Weise der URL-Objekt-Erzeugung ist besonders praktisch für Referenzen innerhalb von Web-Seiten (Named Anchors).
Beispiel Besitzt eine Web-Seite eine Textmarke lang, so kann der URL-Konstruktor für relative URLs verwendet werden.
URL virtuellURL = new URL( ">http://java-tutor.com" );
URL virutellBottomURL = new URL( virtuellURL, "faq.html#lang" );
|
Jeder der Konstruktoren wirft eine MalformedURLException, wenn der Parameter im Konstruktor entweder null ist oder er ein unbekanntes Protokoll (wie in telepatic:\\ulli\brain\java) beschreibt. Somit ist der Code in der Regel von einem Block der folgenden Art umgeben:
try {
URL myURL = new URL( . . . )
}
catch ( MalformedURLException e ) {
// Fehlerbehandlung
}
Es ist wichtig zu erkennen, dass die Ausnahme nicht erzeugt wird, weil der angesprochene Rechner nicht erreicht werden kann. Nur die Schreibweise der URL ist für die Ausnahme ausschlaggebend. Internet-Verbindungen bauen die Konstruktoren nicht auf.
class java.net.URL
implements Serializable, Comparable
|
Hier klicken, um das Bild zu Vergrößern
|
URL( String protocol, String host, int port, String file )
throws MalformedURLException
Erzeugt ein URL-Objekt mit dem gegebenen Protokoll, Host-Namen, Port-Nummer und Datei. Ist die Port-Nummer -1, so wird der Standardport verwendet, zum Beispiel für das WWW ist der Port 80. |
|
URL( String protocol, String host, String file )
throws MalformedURLException
Das Gleiche wie URL(protocol, host, -1, file) |
|
URL( String ) throws MalformedURLException
Erzeugt ein Objekt aus der URL-Zeichenkette. |
|
URL( URL, String ) throws MalformedURLException
Erzeugt relativ zur URL ein neues URL-Objekt. |
16.2.2 Informationen über eine URL
Ist das URL-Objekt einmal angelegt, so lassen sich Attribute des Objekts erfragen, aber nicht mehr ändern. Es gibt zwar Setter-Methoden, aber diese sind protected und somit den Unterklassen vorbehalten. Uns normalen Klassenbenutzern bietet die URL-Klasse nur Methoden zum Zugriff. So lassen sich Protokoll, Host-Name, Port-Nummer und Dateiname mit Zugriffsmethoden erfragen. Es lassen sich jedoch nicht alle URL-Adressen so detailliert aufschlüsseln, und außerdem sind manche der Zugriffsmethoden nur für HTTP sinnvoll.
|
String getProtocol()
Liefert das Protokoll der URL. |
|
String getHost()
Liefert den Host-Namen der URL, falls dies möglich ist. Für das Protokoll »file« ist dies ein leerer String. |
|
int getPort()
Liefert die Port-Nummer. Ist sie nicht gesetzt, liefert getPort() eine -1. |
|
String getFile()
Gibt den Dateinamen der URL zurück. |
|
String getRef()
Gibt die relative Adresse der URL zurück. |
final class java.net.URL
implements Serializable, Comparable
|
Das kleine nachfolgende Programm erzeugt ein URL-Objekt zu http://java-tutor.com. Alle Möglichkeiten zur Angabe von URL-Informationen werden genutzt. Anschließend erfolgt ein Auslesen aller Attribute.
Listing 16.1 ParseURL.java
import java.net.*;
class ParseURL
{
public static void main( String args[] )
{
try
{
URL url = new URL( ">http://java-tutor.com/faq.html#lang");
System.out.println( "protocol = " + url.getProtocol() );
System.out.println( "host = " + url.getHost() );
System.out.println( "filename = " + url.getFile() );
System.out.println( "port = " + url.getPort() );
System.out.println( "ref = " + url.getRef() );
}
catch ( MalformedURLException e ) {
System.out.println( "MalformedURLException: " + e );
}
}
}
Und dies ist die Ausgabe:
protocol = http
host = java-tutor.com
filename = /faq.html
port = -1
ref = lang
Verweisen die URLs auf die gleiche Seite?
Die Methode equals() aus der Klasse Object ist uns bekannt. Sie soll von jeder Klasse so implementiert werden, dass gleiche Objekte true zurückliefern. Jede Klasse soll aber selbst ihre Inhalte vergleichen und nicht nur ihre Objektreferenzen. So muss also die URL-Klasse untersuchen, ob alle Komponenten der einen URL mit der anderen URL übereinstimmen. equals() untersucht dafür zuerst, ob es sich bei der vergleichenden Klasse um ein Exemplar von URL handelt. Wenn ja, wird untersucht, ob die Komponenten Referenzen besitzen oder nicht. Dies wird erreicht, indem Protokoll, Host, Port und Datei untersucht werden. Hierfür bietet sich auch die öffentliche Methode sameFile() an. Ein Anker ist für den Vergleich nicht bestimmend.
final class java.net.URL
implements Serializable, Comparable
|
|
boolean sameFile( URL other)
Vergleicht zwei URL-Objekte. Die Methode liefert true, falls beide Objekte auf die gleiche Ressource zeigen. Der Anker der HTML-Dateien ist unwichtig. |
Beispiel equals() für URL-Objekte
Listing 16.2 URLContentsTheSame.java
import java.net.*;
class URLContentsTheSame
{
public static void main( String args[] )
{
try
|
{
URL sunsite = new URL(
">http://sunsite.unc.edu/javafaq/oldnews.html");
URL helios = new URL(
">http://helios.oit.unc.edu/javafaq/oldnews.html");
if ( sunsite.equals(helios) )
System.out.println( sunsite + " = " + helios );
else
System.out.println( sunsite + " != " + helios );
}
catch ( MalformedURLException e ) {
System.err.println(e);
}
}
}
|
16.2.3 Der Zugriff auf die Daten über die Klasse URL
Um auf die auf dem Web-Server gespeicherten Daten zuzugreifen, gibt es drei Möglichkeiten. Zwei davon nutzen Streams, und zwar einmal über die Klasse URL und einmal über eine URLConnection. Bei der dritten Möglichkeit ist Handarbeit angesagt, und deshalb wird sie im Kapitel über Sockets beschrieben.
Jedes URL-Objekt besitzt die Methode openStream(), die einen InputStream zum Weiterverarbeiten liefert, so dass wir dort die Daten auslesen können:
InputStream in = myURL.openStream();
final class java.net.URL
implements Serializable, Comparable
|
|
final InputStream openStream() throws IOException
Öffnet eine Verbindung zum Server und liefert einen InputStream zurück. Diese Methode ist eine Abkürzung für openConnection().getInputStream(). |
|
URLConnection openConnection() throws IOException
Liefert ein URLConnection-Objekt, welches die Verbindung zum entfernten Objekt vertritt. openConnection() wird vom Protokoll-Handler immer dann aufgerufen, wenn eine neue Verbindung geöffnet wird. |
Verweist die URL auf eine Textdatei, dann erweitern wir oft den InputStream zu einem BufferedReader, da dieser eine readLine()-Methode besitzt. Folgender Programmcode liest so lange Zeilen, bis das Ende der Eingabe signalisiert wird. Glücklicherweise ist uns die Art des Vorgehens schon bekannt, da sich ja das Lesen von einer Datei nicht vom Lesen eines entfernten URL-Objekts unterscheidet.
Listing 16.3 OpenURLStream.java
import java.net.*;
import java.io.*;
class OpenURLStream
{
public static void main( String args[] )
{
try
{
URL url = new URL( ">http://java-tutor.com/aufgaben/bond.txt" );
Reader is = new InputStreamReader( url.openStream() );
BufferedReader in = new BufferedReader( is );
for ( String s; ( s = in.readLine() ) != null; )
System.out.println( s );
in.close();
}
catch ( MalformedURLException e ) {
System.out.println( "MalformedURLException: " + e );
}
catch ( IOException e ) {
System.out.println( "IOException: " + e );
}
}
}
Wir erzeugen ein URL-Objekt und rufen darauf die openStream()-Methode auf. Diese liefert einen InputStream auf den Dateiinhalt. In der API-Beschreibung wurde aber schon kurz erwähnt, dass diese Funktion eigentlich nur eine Abkürzung für openConnection().getInputStream() ist. openConnection() erzeugt ein URLConnection-Objekt und sendet diesem die Nachricht getInputStream(). Sind die Daten gelesen, schließt close() den Datenstrom - close() bezieht sich allerdings nicht auf das URL-Objekt, sondern auf den Datenstrom.
Wir wollen uns im nächsten Abschnitt mit dem URLConnection-Objekt beschäftigen, denn damit wird die Verbindung über das Netzwerk zum Inhalt aufgebaut. Die URL-Klasse besitzt nur deshalb die Abkürzung über openStream(), da zum einen nicht jeder wissen muss, dass URLConnection dahinter steckt, und zweitens, weil es Tipperei erspart.
Das Beispiel zeigt auch, dass bei openConnection() ein try/catch-Block notwendig ist. Denn geht etwas daneben, zum Beispiel, wenn der Dienst nicht verfügbar ist, so wird eine IOException ausgelöst:
try
{
URL ohoURL = new URL( ">http://www.oho.com/" );
ohoURL.openConnection();
}
catch ( MalformedURLException e ) { // new URL() ging daneben
...
}
catch ( IOException e ) { // openConnection() schlug fehl
...
}
16.2.4 Verbindungen durch einen Proxy-Server
In vielen größeren Unternehmen bekommt ein einzelner Client keinen direkten Zugriff auf das Internet. Vielmehr laufen die Verbindungen über eine Zwischenstelle, die Proxy genannt wird. Dieser cacht zum Beispiel Webseiten und erhöht damit die Performanz. Gleichzeitig ist ein Proxy Teil einer wichtigen unternehmensweiten Sicherheitsstrategie. Seiten können geloggt und ausgefiltert werden - welcher Chef möchte schon, dass seine Mitarbeiter laufend Firmenkugelschreiber auf Ebay verticken.
Java nutzt zur Festlegung der Proxy-Eigenschaften zwei beziehungsweise drei System-Properties, die unter http://java.sun.com/j2se/1.4.1/docs/guide/net/properties.html beschrieben sind.
|
http.proxyHost. Adresse des Rechners, der als Proxy dient |
|
http.proxyPort. Port des Proxy-Rechners |
Um nun in Applikationen einen Proxy einzusetzen, sind die Eigenschaften zu setzen; eine Möglichkeit ist über die Kommandozeile mit -D, eine andere über System.setProperty().
System.setProperty( "proxySet", "true" );
System.setProperty( "proxyHost", "myProxyHost" );
System.setProperty( "proxyPort", "myProxyPort" );
Für Applets sind diese Eigenschaften automatisch über die Browser-Einstellungen gesetzt.
Wenn der Proxy eine Autorisierung erfordert - häufig angezeigt, wenn der Client einen Fehler mit 407 (authentication required) bekommt - muss ihm ein Benutzername und Passwort übermittelt werden. Hier gibt es bedauerlicherweise eine ganze Reihe von unterschiedlichen Lösungen, abhängig von den Java-Versionen. Funktionieren sollte im Allgemeinen:
System.setProperty( "http.proxyUser", "myProxyUser" );
System.setProperty( "http.proxyPassword", "myProxyPasswort" );
Soll ein sicherer Webserver über eine Proxy-Verbindung kontaktiert werden, so lässt sich mit installierter Java Secure Socket Extension (JSSE) https.ProxyHost und https.ProxyPort passend setzen.
Im Fall von SOCKS1 sind die Eigenschaften socksProxyHost für den Server und socksProxyPort für den Port zu setzen. Der steht standardmäßig auf 1080.
1 Mit SOCKS lässt sich eine Proxy-Firewall einrichten, die bei vertrauten SOCKS-Servern eine anonyme Bewegung im Internet erlauben. SOCKS-Dienste erfordern Einstellungen an der Software.
|