Java Network Programming (3rd ed) [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Java Network Programming (3rd ed) [Electronic resources] - نسخه متنی

Harold, Elliotte Rusty

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید








16.2 The URLStreamHandler Class


The abstract URLStreamHandler class is a superclass for classes that
handle specific protocolsfor example, HTTP. You rarely call
the methods of the URLStreamHandler class; they
are called by other methods in the URL and
URLConnection classes. By overriding the
URLStreamHandler methods in your own subclass, you
teach the URL class how to handle new protocols.
Therefore, I'll focus on overriding the methods of
URLStreamHandler rather than calling the methods.


16.2.1 The Constructor


You do not create
URLStreamHandler objects
directly. Instead, when a URL is constructed with a protocol that
hasn't been seen before, Java asks the
application's
URLStreamHandlerFactory to create the appropriate
URLStreamHandler subclass for the protocol. If
that fails, Java guesses at the fully package-qualified name of the
URLStreamHandler class and uses
Class.forName( ) to attempt to construct such an
object. This means each concrete subclass should have a noargs
constructor. The single constructor for
URLStreamHandler doesn't take any
arguments:

public URLStreamHandler( )

Because URLStreamHandler is an abstract class,
this constructor is never called directly; it is only called from the
constructors of subclasses.


16.2.2 Methods for Parsing URLs


The first responsibility of a
URLStreamHandler
is to split a string representation of a URL into
its component parts and use those parts to set the various fields of
the URL object. The parseURL( )
method splits the URL into parts, possibly using setURL(
)
to assign values to the
URL's fields. It is very
difficult to imagine a situation in which you would call
parseURL( ) directly; instead, you override it to
change the behavior of the URL class.

16.2.2.1 protected void parseURL(URL u, String spec, int start, int limit)


This method parses the
String spec into a
URL u. All characters in the
spec string before start should
already have been parsed into the URL
u. Characters after limit are
ignored. Generally, the protocol will have already been parsed and
stored in u before this method is invoked, and
start will be adjusted so that it starts with the
character after the colon that delimits the protocol.

The task of parseURL( ) is to set
u's protocol,
host, port,
file, and ref fields. It can
assume that any parts of the String that are
before start and after limit
have already been parsed or can be ignored.

The parseURL( ) method that Java supplies assumes
that the URL looks more or less like an http or
other hierarchical URL:

protocol://www.host.com:port/directory/another_directory/file#fragmentID

This works for ftp and
gopher URLs. It does not work for
mailto or news URLs and may
not be appropriate for any new URL schemes you define. If the
protocol handler uses URLs that fit this hierarchical form, you
don't have to override parseURL() at all; the method inherited from
URLStreamHandler works just fine. If the URLs are
completely different, you must supply a parseURL(
)
method that parses the URL completely. However,
there's often a middle ground that can make your
task easier. If your URL looks somewhat like a standard URL, you can
implement a parseURL( ) method that handles the
nonstandard portion of the URL and then calls
super.parseURL( ) to do the rest of the work,
setting the offset and limit
arguments to indicate the portion of the URL that you
didn't parse.

For example, a mailto URL looks like
mailto:public void parseURL(URL u, String spec, int start, int limit) {
String protocol = u.getProtocol( );
String host = ";
int port = u.getPort( );
String file = "; // really username
String fragmentID = null;
if( start < limit) {
String address = spec.substring(start, limit);
int atSign = address.indexOf('@');
if (atSign >= 0) {
host = address.substring(atSign+1);
file = address.substring(0, atSign);
}
}
this.setURL(u, protocol, host, port, file, fragmentID );
}

Rather than borrowing an unused field from the URL
object, it's possibly a better idea to store
protocol-specific parts of the URL, such as the username, in fields
of the URLStreamHandler subclass. The disadvantage
of this approach is that such fields can be seen only by your own
code; in this example, you couldn't use the
getFile( ) method in the URL
class to retrieve the username. Here's a version of
parseURL( ) that stores the username in a field of
the Handler subclass. When the connection is
opened, the username can be copied into the
MailtoURLConnection object that results. That
class would provide some sort of getUserName( )
method:

String username = ";
public void parseURL(URL u, String spec, int start, int limit) {
String protocol = u.getProtocol( );
String host = ";
int port = u.getPort( );
String file = ";
String fragmentID = null;
if( start < limit) {
String address = spec.substring(start, limit);
int atSign = address.indexOf('@');
if (atSign >= 0) {
host = address.substring(atSign+1);
this.username = address.substring(0, atSign);
}
}
this.setURL(u, protocol, host, port, file, fragmentID );
}

16.2.2.2 protected String toExternalForm(URL u)


This method puts the pieces of the
URL uthat is, its
protocol, host,
port, file, and
ref fieldsback together in a
String. A class that overrides parseURL() should also override toExternalForm(
)
. Here's a toExternalForm() method for a mailto URL; it assumes
that the username has been stored in the
URL's file
field:

protected String toExternalForm(URL u) {
return "mailto:" + u.getFile( ) + "@" + u.getHost( );
}

Since toExternalForm( ) is protected, you probably
won't call this method directly. However, it is
called by the public toExternalForm( ) and
toString( ) methods of the URL
class, so any change you make here is reflected when you convert
URL objects to strings.

16.2.2.3 protected void setURL(URL u, String protocol, String host, int port, String authority, String userInfo, String path, String query, String fragmentID) // Java 1.3


This method sets the
protocol, host,
port, authority,
userInfo, path,
query, and ref fields of the
URL u to the given values.
parseURL( ) uses this method to set these fields
to the values it has found by parsing the URL. You need to call this
method at the end of the parseURL( ) method when
you subclass URLStreamHandler.

This method is a little flaky, since the host, port, and user info
together make up the authority. In the event of a conflict between
them, they're all stored separately, but the host,
port, and user info are used in preference to the authority when
deciding which site to connect to.

This is actually quite relevant to the mailto
example, since mailto URLs often have query
strings that indicate the subject or other header; for example,
mailto:elharo@metalab.unc.edu?subject=JavaReading.
Here the query string is subject=JavaReading.
Rewriting the parseURL( ) method to support
mailto URLs in this format, the result looks
like this:

public void parseURL(URL u, String spec, int start, int limit) {
String protocol = u.getProtocol( );
String host = ";
int port = u.getPort( );
String file = ";
String userInfo = null;
String query = null;
String fragmentID = null;
if (start < limit) {
String address = spec.substring(start, limit);
int atSign = address.indexOf('@');
int questionMark = address.indexOf('?');
int hostEnd = questionMark >= 0 ? questionMark : address.length( );
if (atSign >= 0) {
host = address.substring(atSign+1, hostEnd);
userInfo = address.substring(0, atSign);
}
if (questionMark >= 0 && questionMark > atSign) {
query = address.substring(questionMark + 1);
}
}
String authority = ";
if (userInfo != null) authority += userInfo + '@';
authority += host;
if (port >= 0) authority += ":" + port;
this.setURL(u, protocol, host, port, authority, userInfo, file,
query, fragmentID );
}

16.2.2.4 protected int getDefaultPort( ) // Java 1.3


The getDefaultPort() method returns the default port for the
protocol, e.g., 80 for HTTP. The default implementation of this
method simply returns -1, but each subclass should override that with
the appropriate default port for the protocol it handles. For
example, here's a getDefaultPort() method for the finger protocol that normally operates on
port 79:

public int getDefaultPort( ) {
return 79;
}

As well as providing the right port for finger, overriding this
method also makes getDefaultPort( ) public.
Although there's only a default implementation of
this method in Java 1.3, there's no reason you
can't provide it in your own subclasses in any
version of Java. You simply won't be able to invoke
it polymorphically from a reference typed as the superclass.

16.2.2.5 protected InetAddress getHostAddress(URL u) // Java 1.3


The getHostAddress() method returns an
InetAddress object pointing to the server in the
URL. This requires a DNS lookup, and the method does block while the
lookup is made. However, it does not throw any exceptions. If the
host can't be located, whether because the URL does
not contain host information as a result of a DNS failure or a
SecurityException, it simply returns null. The
default implementation of this method is sufficient for any
reasonable case. It shouldn't be necessary to
override it.

16.2.2.6 protected boolean hostsEqual(URL u1, URL u2) // Java 1.3


The hostsEqual( ) method
determines whether the two URLs refer to the same server. This method
does use DNS to look up the hosts. If the DNS lookups succeed, it can
tell that, for example, http://www.ibiblio.org/Dave/this-weekl
and

16.2.2.7 protected boolean sameFile(URL u1, URL u2) // Java 1.3


The sameFile( ) method determines whether two URLs
point to the same file. It does this by comparing the protocol, host,
port, and path. The files are considered to be the same only if each
of those four pieces is the same. However, it does not consider the
query string or the fragment identifier. Furthermore, the hosts are
compared by the hostsEqual( ) method so that
www.ibiblio.org and
metalab.unc.edu can be recognized as the same if
DNS can resolve them. This is similar to the sameFile() method of the URL class. Indeed, that
sameFile( ) method just calls this
sameFile( ) method.

The default implementation of this method is sufficient for most
cases. You probably won't need to override it. You
might perhaps want to do so if you need a more sophisticated test
that converts paths to canonical paths or follows redirects before
determining whether two URLs have the same file part.

16.2.2.8 protected boolean equals(URL u1, URL u2) // Java 1.3


The final equality method tests almost
the entire URL, including protocol, host, file, path, and fragment
identifier. Only the query string is ignored. All five of these must
be equal for the two URLs to be considered equal. Everything except
the fragment identifier is compared by the sameFile() method, so overriding that method changes the behavior of
this one. The fragment identifiers are compared by simple string
equality. Since the sameFile( ) method uses
hostsEqual( ) to compare hosts, this method does
too. Thus, it performs a DNS lookup if possible and may block. The
equals( ) method of the URL
class calls this method to compare two URL objects
for equality. Again, you probably won't need to
override this method. The default implementation should suffice for
most purposes.

16.2.2.9 protected int hashCode(URL u) // Java 1.3


URLStreamHandlers can change the default hash code
calculation by overriding this method. You should do this if you
override equals( ), sameFile(), or hostsEqual( ) to make sure that
two equal URL objects will have the same hash
code, and two unequal URL objects will not have
the same hash code, at least to a very high degree of probability.


16.2.3 A Method for Connecting


The second responsibility of a
URLStreamHandler
is to create a URLConnection object appropriate to
the URL. This is done with the abstract openConnection(
)
method.

16.2.3.1 protected abstract URLConnection openConnection(URL u) throws IOException


This method must be overridden in each
subclass of URLConnection. It takes a single
argument, u, which is the URL to connect to. It
returns an unopened URLConnection, directed at the
resource u points to. Each subclass of
URLStreamHandler should know how to find the right
subclass of URLConnection for the protocol it
handles.

The openConnection( ) method is protected, so you
usually do not call it directly; it is called by the
openConnection( ) method of a
URL class. The URL
u that is passed as an argument is the
URL that needs a connection. Subclasses override
this method to handle a specific protocol. The
subclass's openConnection( )
method is usually extremely simple; in most cases, it just calls the
constructor for the appropriate subclass of
URLConnection. For example, a
URLStreamHandler for the
mailto protocol might have an
openConnection( ) method that looks like this:

protected URLConnection openConnection(URL u) throws IOException {
return new com.macfaq.net.www.protocol.MailtoURLConnection(u);
}

Example 16-1 demonstrates a complete
URLStreamHandler for mailto
URLs. The name of the class is Handler, following
Sun's naming conventions. It assumes the existence
of a MailtoURLConnection class.

Example 16-1. A mailto URLStreamHandler


package com.macfaq.net.www.protocol.mailto;
import java.net.*;
import java.io.*;
import java.util.*;
public class Handler extends URLStreamHandler {
protected URLConnection openConnection(URL u) throws IOException {
return new MailtoURLConnection(u);
}
public void parseURL(URL u, String spec, int start, int limit) {
String protocol = u.getProtocol( );
String host = ";
int port = u.getPort( );
String file = "; // really username
String userInfo = null;
String authority = null;
String query = null;
String fragmentID = null;
if( start < limit) {
String address = spec.substring(start, limit);
int atSign = address.indexOf('@');
if (atSign >= 0) {
host = address.substring(atSign+1);
file = address.substring(0, atSign);
}
}
// For Java 1.2 comment out this next line
this.setURL(u, protocol, host, port, authority,
userInfo, file, query, fragmentID );
// In Java 1.2 and earlier uncomment the following line:
// this.setURL(u, protocol, host, port, file, fragmentID );
}
protected String toExternalForm(URL u) {
return "mailto:" + u.getFile( ) + "@" + u.getHost( );;
}
}

16.2.3.2 protected URLConnection openConnection(URL u, Proxy p) throws IOException // Java 1.5


Java 1.5 overloads the
openConnection( ) method to allow you to specify a
proxy server for the connection. The
java.net.Proxy class (also new in Java 1.5)
encapsulates the address of a proxy server. Rather than connecting to
the host directly, this URLConnection connects to
the specified proxy server, which relays data back and forth between
the client and the server. Protocols that do not support proxies can
simply ignore the second argument.

Normally connections are opened with the usual proxy server settings
within that VM. Calling this method is only necessary if you want to
use a different proxy server. If you want to bypass the usual proxy
server and connect directly instead, pass the constant
Proxy.NO_PROXY as the second argument.


/ 164