Suns Class Server The Class Server

the URL. The retrieval algorithm used by URLClassLoader checks to see whether the URL ends in a . If the URL ends with , it follows the algorithm described earlier. Its very easy to forget to end a codebase URL with a . And, for some reason, if you do forget, you wont spot the er ror in your code. Instead, youll probably spend half a day or so making sure that the filesystem is working. On the other hand, if the URL doesnt end with a , it is assumed to be complete and to describe the location of a JAR file. An instance of URLClassLoader will attempt to retrieve the JAR file, which must be returned the same way as an individual class, and then attempts to find the class from within the JAR file. Thus, if the URL was http:localhost:80myclasses.jar, and the class was com.ora.rmibook.chapter9.valueobjects.Money , an instance of URLClassLoader would do the following: 1. Send the following HTTP command to the web server listening on port 80 of localHost : GET myClasses.jar HTTP1.1. 1. Attempt to interpret the return value as a JAR and load com.ora.rmibook.chapter9.valueobjects.Money from the JAR file.

19.4.4 Suns Class Server

Rather than force you to install and configure a web server, Sun Microsystems, Inc. has, at various times, provided a simple Java class that handles most of the details of serving class files in response to requests from an instance of URLClassLoader . Heres the entire class with comments removed: public abstract class ClassServer implements Runnable { private ServerSocket server = null; private int port; public abstract byte[] getBytesString path throws IOException, ClassNotFoundException; protected ClassServerint port throws IOException { this.port = port; server = new ServerSocketport; newListener ; } public void run { Socket socket; try { socket = server.accept ; } catch IOException e { System.out.printlnClass Server died: + e.getMessage ; e.printStackTrace ; return; } newListener ; try { DataOutputStream out = new DataOutputStreamsocket.getOutputStream ; try { BufferedReader in = new BufferedReadernew InputStreamReadersocket.getInputStream ; String path = getPathin; byte[] bytecodes = getBytespath; try { out.writeBytesHTTP1.0 200 OK\r\n; out.writeBytesContent-Length: + bytecodes.length + \r\n; out.writeBytesContent-Type: applicationjava\r\n\r\n; out.writebytecodes; out.flush ; } catch IOException ie { return; } } catch Exception e { out.writeBytesHTTP1.0 400 + e.getMessage + \r\n; out.writeBytesContent-Type: texthtml\r\n\r\n; out.flush ; } } catch IOException ex { System.out.printlnerror writing response: + ex.getMessage ; ex.printStackTrace ; } finally { try { socket.close ; } catch IOException e {} } } private void newListener { new Threadthis.start ; } private static String getPathBufferedReader in throws IOException { String line = in.readLine ; String path = ; if line.startsWithGET { line = line.substring5, line.length -1.trim ; int index = line.indexOf.class ; if index = -1 { path = line.substring0, index.replace, .; } } do { line = in.readLine ; } while line.length = 0 line.charAt0 = \r line.charAt0 = \n; if path.length = 0 { return path; } else { throw new IOExceptionMalformed Header; } } } How to Find ClassServer For legal reasons, I cant include the source code for ClassServer with the source code for this book; the code was written by Sun Microsystems, and they have fairly stringent rules about who can ship their source code. Similarly, in Chapt er 22 , we discuss HTTP tunneling and use examples written by Sun to illustrate the basic idea. Fortunately, its not that hard to find these, and other, examples on the Internet. The two best places to look are: Javasofts web site ht t p: w w w .j avasoft .com This is the basic source of all things Java and hence, of all things RMI. In particular, there is a very nice RMI section. The RMI mailing list ht t p: ar chives.j ava.sun.com cgi- bin w a?A0= r m i- user s The This is a high-quality mailing list. There are a lot of good developers who frequently post to the list including many of the members of Suns RMI development team. It is often the best place to look for answers to specific questions about RMI the ability to search the archives is invaluable. This may seem a little complicated. However, its really a very simple class: it listens on a port, parses HTTP requests that come in, and translates them into calls to the abstract method getBytes . Moreover, as part of the parsing, it transforms the request path into a classname. For example, it transforms: GET comorarmibookchapter9valueobjectsMoney.class HTTP1.1 into a call on getBytes using the argument com.ora.rmibook.chapter9.valueobjects.Money . The return value from getBytes is then packaged into an HTTP response with a minimal set of headers and returned to the caller. This is a very limited server. It doesnt handle either codebases that have paths or requests from JAR files very well. However, its definitely good enough for development, quite possibly good enough for debugging, and occasionally good enough for deployment. Of course, since ClassServer is an abstract class, it cant be used directly. Heres a concrete subclass that takes a root directory as a single command-line argument and serves class files based on that root: public class SimpleClassServer extends ClassServer { private static String _pathPrefix; public static void mainString[] args throws IOException { _pathPrefix = args[0]; new SimpleClassServer ; } private SimpleClassServer throws IOException { super80; } public byte[] getBytesString path throws IOExcepti on, ClassNotFoundException { path = path.replace., \\; String actualPath = _pathPrefix + path +.class; FileInputStream fileInputStream = new FileInputStreamactualPath; ByteArrayOutputStream inMemoryCopy = new ByteArrayOutputStream ; copyfileInputStream, inMemoryCopy; return inMemoryCopy.toByteArray ; } private void copyInputStream inputStream, OutputStream outputStream throws IOException { int nextByte; while nextByte = inputStream.read = -1 { outputStream.writenextByte; } } } To use SimpleClassServer , all you need to do is pass in a single command-line argument, which is the base directory where the class files are stored. For example, I store my class files in the directories below D:\classes i.e., the class com.ora.rmibook.chapter9.valueobjects.Money is contained in the directory d:\classes\com\ora\rmibook\chapter9\valueobjects. On my system, the following invocation launches SimpleClassServer correctly: start java examples.classserver.S impleClassServer D:\classes\\ The \\ at the end is necessary because of how Sun parses command-line arguments. Its worth repeating: the URL for SimpleClassServer cannot have a path. Path information will be treated as part of the package declaration for the class. If you need to have multiple class servers running e.g., if you want to serve different versions of the same classes, you either need to write a version that understands paths a little better, or use different ports for different versions e.g., Port 1299 is for the classes from December of 1999.

19.5 Using Dynamic Classloadingin an Application