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

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

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

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

Harold, Elliotte Rusty

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

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

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








16.4 More Protocol Handler Examples and Techniques


Now that you've seen how
to write one protocol handler, it's not at all
difficult to write more. Remember the five basic steps of creating a
new protocol handler:

Design a URL for the protocol if a standard URL for that protocol
doesn't already exist. As of mid-2004, the official
list of URL schemes at the IANA (http://www.iana.org/assignments/uri-schemes)
includes only 43 different URL schemes and reserves three more. For
anything else, you need to define your own.

Decide what MIME type should be returned by the protocol
handler's getContentType( )
method. The text/plain content type is often appropriate for legacy
protocols. Another option is to convert the incoming data to HTML
inside getInputStream( ) and return text/html.
Binary data often uses one of the many application types. In some
cases, you may be able to use the
URLConnection.guessContentTypeFromName( ) or
URLConnection.guessContentTypeFromStream( )
methods to determine the right MIME type.

Write a subclass of URLConnection that understands
this protocol. It should implement the connect( )
method and may override the getContentType( ),
getOutputStream( ), and getInputStream(
)
methods of URLConnection. It also
needs a constructor that builds a new
URLConnection from a URL.

Write a subclass of URLStreamHandler with an
openConnection( ) method that knows how to return
a new instance of your subclass of URLConnection.
Also provide a getDefaultPort( ) method that
returns the well-known port for the protocol. If your URL is not
hierarchical, override parseURL( ) and
toExternalForm( ) as well.

Implement the URLStreamHandlerFactory interface
and the createStreamHandler( ) method in a
convenient class.

Let's look at handlers for two more protocols,
daytime and chargen, which will bring up different challenges.


16.4.1 A daytime Protocol Handler


For a daytime protocol handler,
let's say that the URL should look like
daytime:///vision.poly.edu.
We'll allow for nonstandard port assignments in the
same way as with HTTP: follow the hostname with a colon and the port
(daytime:///vision.poly.edu:2082). Finally,
allow a terminating slash and ignore everything following the slash.
For example,
daytime:///vision.poly.edu/indexl is
equivalent to daytime:///vision.poly.edu. This
is similar enough to an http URL that the
default toExternalForm( ) and parseURL() methods will work.

Although the content returned by the daytime protocol is really
text/plain, this protocol handler is going to reformat the data into
an HTML page. Then it can return a content type of text/html and let
the web browser display it more dramatically. The resulting HTML
looks like this:

<html><head><title>The Time at metalab.unc.edu</title></head><body>
<h1>Fri Oct 29 14:32:07 1999</h1>
</body></html>

The trick is that the page can be broken up into three different
strings:

Everything before the time

The time

Everything after the time


The first and the third strings can be calculated before the
connection is even opened. We'll formulate these as
byte arrays of ASCII text and use them to create two
ByteArrayInputStreams. Then we'll
use a SequenceInputStream to combine those two
streams with the data actually returned from the server. Example 16-4
demonstrates. This is a neat trick for protocols such as daytime that
return a very limited amount of data; it can be inserted in a single
place in an HTML document. Protocols such as finger that return more
complex and less predictable text might need to use a
FilterInputStream that inserts the HTML on the fly
instead. And of course, a third possibility is to simply return a
custom content type and use a custom content handler to display it.
This third option is explored in the next chapter.


Example 16-4. The DaytimeURLConnection class


package com.macfaq.net.www.protocol.daytime;
import java.net.*;
import java.io.*;
public class DaytimeURLConnection extends URLConnection {
private Socket connection = null;
public final static int DEFAULT_PORT = 13;
public DaytimeURLConnection (URL u) {
super(u);
}
public synchronized InputStream getInputStream( ) throws IOException {
if (!connected) connect( );
String header = "<html><head><title>The Time at "
+ url.getHost( ) + "</title></head><body><h1>";
String footer = "</h1></body></html>";
InputStream in1 = new ByteArrayInputStream(header.getBytes("8859_1"));
InputStream in2 = this.connection.getInputStream( );
InputStream in3 = new ByteArrayInputStream(footer.getBytes("8859_1"));
SequenceInputStream result = new SequenceInputStream(in1, in2);
result = new SequenceInputStream(result, in3);
return result;
}
public String getContentType( ) {
return "text/html";
}
public synchronized void connect( ) throws IOException {
if (!connected) {
int port = url.getPort( );
if ( port <= 0 || port > 65535) {
port = DEFAULT_PORT;
}
this.connection = new Socket(url.getHost( ), port);
this.connected = true;
}
}
}

This class declares two fields. The first is
connection, which is a Socket
between the client and the server. The second field is
DEFAULT_PORT, a final
static int variable that holds
the default port for the daytime protocol (port 13) and is used if
the URL doesn't specify the port explicitly.

The constructor has no surprises. It just calls the
superclass's constructor with the same argument, the
URL u. The connect() method opens a connection to the specified server on the
specified port (or, if no port is specified, to the default port); if
the connection opens successfully, connect( ) sets
the boolean variable connected
to true. Recall from the previous chapter that
connected is a protected field in
URLConnection that is inherited by this subclass.
The Socket that's opened by this
method is stored in the connection field for later
use by getInputStream( ).

The getContentType( ) method returns a
String containing a MIME type for the data. This
method is called by the getContent( ) method of
URLConnection to select the appropriate content
handler. The getInputStream( ) method reformats
the text into HTML, so the getContentType( )
method returns text/html.

The getInputStream( ) method builds a
SequenceInputStream out of several string
literals, the host property of url, and the actual
stream provided by the Socket connecting the
client to the server. If the socket is not connected when this method
is called, the method calls connect( ) to
establish the connection.

Next, you need a subclass of URLStreamHandler that
knows how to handle a daytime server. This class needs an
openConnection( ) method that builds a new
DaytimeURLConnection from a URL and a
getDefaultPort( ) method that returns the
well-known daytime port 13. Since the daytime URL has been made
similar to an http URL, we
don't need to override parseURL(); once we have written openConnection(
)
, we're done. Example 16-5 shows the daytime protocol's
URLStreamHandler.


Example 16-5. The DaytimeURLStreamHandler class


package com.macfaq.net.www.protocol.daytime;
import java.net.*;
import java.io.*;
public class Handler extends URLStreamHandler {
public int getDefaultPort( ) {
return 13;
}
protected URLConnection openConnection(URL u) throws IOException {
return new DaytimeURLConnection(u);
}
}

Since we've used the same package-naming convention
here as for the previous finger protocol handler, no further changes
to HotJava's properties need to be made to let
HotJava find this. Just compile the files, put the classes somewhere
in HotJava's class path, and load a URL that points
to an active daytime server. Figure 16-2
demonstrates.


Figure 16-2. HotJava using the daytime protocol handler


16.4.2 A chargen Protocol Handler


The chargen protocol, defined in RFC
864, is a very simple protocol designed for testing clients. The
server listens for connections on port 19. When a client connects,
the server sends an endless stream of characters until the client
disconnects. Any input from the client is ignored. The RFC does not
specify which character sequence to send but recommends that the
server use a recognizable pattern. One common pattern is rotating,
72-character carriage return/linefeed delimited lines of the 95 ASCII
printing characters, like this:

!"#$%&'( )*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefgh
"#$%&'( )*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghi
#$%&'( )*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghij
$%&'( )*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijk
%&'( )*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijkl
&'( )*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklm
'( )*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmn
( )*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmno

The big trick with this protocol is deciding when to stop. A TCP
chargen server sends an unlimited amount of data. Most web browsers
don't deal well with this. HotJava
won't even attempt to display a file until it sees
the end of the stream. Consequently, the first thing
we'll need is a FilterInputStream
subclass that cuts off the server (or at least starts ignoring it)
after a certain amount of data has been sent. Example 16-6 is such a class.


Example 16-6. FiniteInputStream


package com.macfaq.io;
import java.io.*;
public class FiniteInputStream extends FilterInputStream {
private int limit = 8192;
private int bytesRead = 0;
public FiniteInputStream(InputStream in) {
this(in, 8192);
}
public FiniteInputStream(InputStream in, int limit) {
super(in);
this.limit = limit;
}
public int read( ) throws IOException {
if (bytesRead >= limit) return -1;
int c = in.read( );
bytesRead++;
return c;
}
public int read(byte[] data) throws IOException {
return this.read(data, 0, data.length);
}
public int read(byte[] data, int offset, int length)
throws IOException {
if (data == null) throw new NullPointerException( );
else if ((offset < 0) || (offset > data.length) || (length < 0) ||
((offset + length) > data.length) || ((offset + length) < 0)) {
throw new IndexOutOfBoundsException( );
}
else if (length == 0) {
return 0;
}
if (bytesRead >= limit) return -1;
else if (bytesRead + length > limit) {
int numToRead = bytesRead + length - limit;
int numRead = in.read(data, offset, numToRead);
if (numRead == -1) return -1;
bytesRead += numRead;
return numRead;
}
else { // will not exceed limit
int numRead = in.read(data, offset, length);
if (numRead == -1) return -1;
bytesRead += numRead;
return numRead;
}
}
public int available( ) throws IOException {
if (bytesRead >= limit) return 1;
else return in.available( );
}
}

Next, since there's no standard for the format of a
chargen URL, we have to create one. Ideally, this should look as much
like an http URL as possible. Therefore, we will
implement a chargen URL like this:

chargen://hostname:port

Second, we need to choose the content type to be returned by the
chargen protocol handler's getContentType() method. A chargen server returns ASCII text, so the
getContentType( ) method should return the string
text/plain. The advantage of the
text/plain MIME type is that Java already
understands it.

Example 16-7 is a
ChargenURLConnection class that subclasses
URLConnection. This class overrides the
getContentType( ) and getInputStream() methods of URLConnection and
implements connect( ). It also has a constructor
that builds a new URLConnection from a URL.


Example 16-7. The ChargenURLConnection class


package com.macfaq.net.www.protocol.chargen;
import java.net.*;
import java.io.*;
import com.macfaq.io.*;
public class ChargenURLConnection extends URLConnection {
private Socket connection = null;
public final static int DEFAULT_PORT = 19;
public ChargenURLConnection(URL u) {
super(u);
}
public synchronized InputStream getInputStream( ) throws IOException {
if (!connected) this.connect( );
return new FiniteInputStream(this.connection.getInputStream( ));
}
public String getContentType( ) {
return "text/plain";
}
public synchronized void connect( ) throws IOException {
if (!connected) {
int port = url.getPort( );
if ( port < 1 || port > 65535) {
port = DEFAULT_PORT;
}
this.connection = new Socket(url.getHost( ), port);
this.connected = true;
}
}
}

This class has two fields. connection is a
Socket between the client and the server. The
second field is DEFAULT_PORT, a
final static
int that contains the chargen
protocol's default port; this port is used if the
URL does not specify the port explicitly.

The class's constructor just passes the
URL u to the
superclass's constructor. The connect(
)
method opens a connection to the specified server on the
specified port (or, if no port is specified, to the default chargen
port, 19) and, assuming the connection is successfully opened, sets
the boolean field connected to
true. The Socket that
connect( ) opens is stored in the field
connection for later use by
getInputStream( ). The connect() method is synchronized to avoid a possible race condition
on the connected variable.

The getContentType( ) method returns a
String containing a MIME type for the data. The
data returned by a chargen server is always ASCII text, so this
getContentType( ) method always returns
text/plain.

The getInputStream( ) connects if necessary, then
gets the InputStream from
this.connection. Rather than returning it
immediately, getInputStream( ) first chains it to
a FiniteInputStream.

Now that we have a URLConnection, we need a
subclass of URLStreamHandler that knows how to
handle a chargen server. This class needs an openConnection() method that builds a new
ChargenURLConnection from a URL and a
getDefaultPort( ) method that returns the
well-known chargen port. Since we defined the
chargen URL so that it is similar to an
http URL, we don't need to
implement a parseURL( ) method. Example 16-8 is a stream handler for the chargen protocol.


Example 16-8. The chargen Handler class


package com.macfaq.net.www.protocol.chargen;
import java.net.*;
import java.io.*;
public class Handler extends URLStreamHandler {
public int getDefaultPort( ) {
return 19;
}
protected URLConnection openConnection(URL u) throws IOException {
return new ChargenURLConnection(u);
}
}

You can use HotJava to test this protocol handler. Run it and ask for
a URL of a site running a chargen server, such as
vision.poly.edu. Figure 16-3
shows the result.


Figure 16-3. HotJava using the chargen protocol handler



/ 164