Some of the improvements to the java.net classes
in JDK 1.1 allow sockets (Socket/ServerSocket) to be non-final,
extendable classes. The basic goal is to allow extended sockets
to be used whereever a base class Socket is used without rewriting
application code. So for example an already-existing email client
could be handed a subclass of Socket that does peer authentication
or encryption, which the Socket subclass would handle transparently.
Other examples include sockets that transparently use compression,
or which add functionality such as mirroring traffic to multiple
servers (e.g. using reliable ordered multicast).
JDK 1.0 allows licensees to extend socket functionality in
one very particular way: they can subclass
java.net.SocketImpl , and provide an enhanced
java.net.SocketImplFactory() which returns such classes as needed.
The SocketImpl/SocketImplFactory scheme is useful for, and was
designed for, java
apps/applications to be portable across environments that use
different transport mechanisms. A client application that uses
a java.net.Socket can work in the general case (where the runtime
uses a PlainSocketImpl), as well as in environments where network
connections must do something particular. For example, a java program
should be able to work behind a firewall where connections to the
Internet must be done through a proxy protocol, like
SOCKS, assuming the right
kind of SocketImpl is set for the java runtime in that environment.
Though the SocketImpl is useful for things like
transparently providing proxy support to java apps, its utility is limited
as a way to provide added functionality of network transport, or for layering
other protocols on top of TCP. Additionally, it's only possible to have a single
type of SocketImpl installed for a java runtime, which limits large-scale
applications. It's cleaner and more intuitive to make ServerSocket and
Socket extendable.
Note that the SocketImpl mechanism is designed to
be orthogonal to the functionality provided by a subclass of Socket:
for example, a subclass of Socket that is capable of doing compression on
its streams would still want to use the system-default SocketImpl to get
proxy support behind certain kinds of firewalls. The system-default
SocketImpl can be thought of as an extension of the transport layer that
applications needn't be aware of, and subclasses of Socket/ServerSocket
provide richer functionality at the application layer.
In JDK 1.1, Socket and ServerSocket are made non-final, which is a pretty simple
change. The one basic caveat is that subclasses do not have direct access to the
underlying SocketImpl in the base classes, primarily for security reasons. But
other than that, subclasses of Socket/ServerSocket inherit and can override methods
from their superclass.
The JDK changed by:
Removing the final modifier from the
Socket and
ServerSocket classes.
Reattaching that final modifier only to
methods where it is needed to avoid bypassing
security manager calls.
Defining a new method in ServerSocket with signature:
protected final void implAccept(Socket client)
to use when initializing a newly accepted socket.
Exposing a default Socket constructor so
that Socket subclasses could initialize their superclass without
doing an actual connection in the superclass. This is also
required so that a ServerSocket subclasses can return the correct
Socket subclass from accept() (e.g., FooServerSocket.accept() returns
a FooSocket)
The public constructors to Socket of the general form:
Socket(String host, int port) {
...
}
can be used by subclasses to initialize the superclass, but they will
also create a system-default SocketImpl and connect it to the specified
host, port.
Socket also has two protected constructors to intialize the superclass
without connecting the Socket:
The first constructor installs a system-default SocketImpl (either from the
factory or a PlainSocketImpl). The second allows for a subclass of Socket
to install its own impl if need be. If the Socket subclass doesn't need the default
SocketImpl, it's perfectly valid to use the second
constructor and pass it null. (But in this case the subclass should of course override
all the base class methods, since they all rely on the underlying SocketImpl).
Subclasses of ServerSocket also have a protected constructor exposed to them
that creates a default SocketImpl in the base class, but doesn't otherwise
initialize it (e.g., doesn't call impl.create(), impl.bind() or impl.listen()).
The public constructors of ServerSocket will initialize the underlying SocketImpl.
The only other thing about extending ServerSocket that should be explained is how
to override accept() when the underlying SocketImpl for Socket/ServerSocket isn't
accesible to subclasses. Since the base class can do this, ServerSocket has
a method to make the necessary calls on the underlying SocketImpl's on behalf of
subclasses:
public class ServerSocket {
...
protected final void implAccept(Socket s) throws IOException {
...
// on return from this call s will be connected to a client
}
...
Note that subclasses of Socket/ServerSocket that don't use a SocketImpl
needn't use this method.
As an example of how this works, here's what some SSL code
might look like.
class SSLServerSocket extends ServerSocket {
...
public Socket accept () throws IOException
{
SSLSocket s = new SSLSocket (certChain, privateKey);
// create an unconnected client SSLSocket, that we'll
// return from accept
implAccept (s);
s.handshake ();
return s;
}
...
}
class SSLSocket extends java.net.Socket {
...
public SSLSocket(CertChain c, PrivateKey k) {
super();
...
}
...
}