KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > proactive > core > rmi > ClassServer


1 /*
2 * ################################################################
3 *
4 * ProActive: The Java(TM) library for Parallel, Distributed,
5 * Concurrent computing with Security and Mobility
6 *
7 * Copyright (C) 1997-2002 INRIA/University of Nice-Sophia Antipolis
8 * Contact: proactive-support@inria.fr
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23 * USA
24 *
25 * Initial developer(s): The ProActive Team
26 * http://www.inria.fr/oasis/ProActive/contacts.html
27 * Contributor(s):
28 *
29 * ################################################################
30 */

31 package org.objectweb.proactive.core.rmi;
32
33 import org.apache.log4j.Logger;
34
35
36 /**
37  * ClassServer is an abstract class that provides the
38  * basic functionality of a mini-webserver, specialized
39  * to load class files only. A ClassServer must be extended
40  * and the concrete subclass should define the <b>getBytes</b>
41  * method which is responsible for retrieving the bytecodes
42  * for a class.<p>
43  *
44  * The ClassServer creates a thread that listens on a socket
45  * and accepts HTTP GET requests. The HTTP response contains the
46  * bytecodes for the class that requested in the GET header. <p>
47  *
48  * For loading remote classes, an RMI application can use a concrete
49  * subclass of this server in place of an HTTP server. <p>
50  *
51  * @see ClassFileServer
52  */

53 public abstract class ClassServer implements Runnable JavaDoc {
54     protected static Logger logger = Logger.getLogger(ClassServer.class.getName());
55     protected static int DEFAULT_SERVER_BASE_PORT = 2001;
56     protected static int DEFAULT_SERVER_PORT_INCREMENT = 20;
57     protected static int MAX_RETRY = 50;
58     private static java.util.Random JavaDoc random = new java.util.Random JavaDoc();
59     private java.net.ServerSocket JavaDoc server = null;
60     protected int port;
61     protected String JavaDoc hostname;
62
63     /**
64      * Constructs a ClassServer that listens on a random port. The port number
65      * used is the first one found free starting from a default base port.
66      * obtains a class's bytecodes using the method <b>getBytes</b>.
67      * @exception java.io.IOException if the ClassServer could not listen on any port.
68      */

69     protected ClassServer() throws java.io.IOException JavaDoc {
70         this(0);
71     }
72
73     /**
74      * Constructs a ClassServer that listens on <b>port</b> and
75      * obtains a class's bytecodes using the method <b>getBytes</b>.
76      * @param port the port number
77      * @exception java.io.IOException if the ClassServer could not listen
78      * on <b>port</b>.
79      */

80     protected ClassServer(int port) throws java.io.IOException JavaDoc {
81         if (port == 0) {
82             this.port = boundServerSockect(DEFAULT_SERVER_BASE_PORT, MAX_RETRY);
83         } else {
84             this.port = port;
85             server = new java.net.ServerSocket JavaDoc(port);
86         }
87         hostname = java.net.InetAddress.getLocalHost().getHostAddress();
88         newListener();
89     }
90
91     public int getServerSocketPort() {
92         //System.out.println("XXXXXX " + this.port);
93
return port;
94     }
95
96     public String JavaDoc getHostname() {
97         return hostname;
98     }
99
100     /**
101      * The "listen" thread that accepts a connection to the
102      * server, parses the header to obtain the class file name
103      * and sends back the bytecodes for the class (or error
104      * if the class is not found or the response was malformed).
105      */

106     public void run() {
107         java.net.Socket JavaDoc socket;
108
109         // accept a connection
110
try {
111             socket = server.accept();
112         } catch (java.io.IOException JavaDoc e) {
113             logger.error("Class Server died: " + e.getMessage());
114             e.printStackTrace();
115             return;
116         }
117
118         // create a new thread to accept the next connection
119
newListener();
120         try {
121             java.io.DataOutputStream JavaDoc out = new java.io.DataOutputStream JavaDoc(socket.getOutputStream());
122             RequestInfo info = null;
123             try {
124                 // get path to class file from header
125
java.io.BufferedReader JavaDoc in = new java.io.BufferedReader JavaDoc(new java.io.InputStreamReader JavaDoc(
126                             socket.getInputStream()));
127                 info = getInfo(in);
128                 // retrieve bytecodes
129
byte[] bytecodes = getBytes(info.path);
130
131                 // send bytecodes in response (assumes HTTP/1.0 or later)
132
try {
133                     out.writeBytes("HTTP/1.0 200 OK\r\n");
134                     out.writeBytes("Content-Length: " + bytecodes.length +
135                         "\r\n");
136                     out.writeBytes("Content-Type: application/java\r\n\r\n");
137                     out.write(bytecodes);
138                     out.flush();
139                     logger.info("ClassServer sent class " + info.path +
140                         " successfully");
141                 } catch (java.io.IOException JavaDoc ie) {
142                     return;
143                 }
144             } catch (Exception JavaDoc e) {
145                 // write out error response
146
e.printStackTrace();
147                 logger.error("!!! ClassServer failed to load class " +
148                     info.path);
149                 out.writeBytes("HTTP/1.0 400 " + e.getMessage() + "\r\n");
150                 out.writeBytes("Content-Type: text/html\r\n\r\n");
151                 out.flush();
152             }
153         } catch (java.io.IOException JavaDoc ex) {
154             // eat exception (could log error to log file, but write out to stdout for now).
155
//System.out.println("error writing response: " + ex.getMessage());
156
//ex.printStackTrace();
157
} finally {
158             try {
159                 socket.close();
160             } catch (java.io.IOException JavaDoc e) {
161             }
162         }
163     }
164
165     //
166
// -- PROTECTED METHODS -----------------------------------------------
167
//
168

169     /**
170      * Returns an array of bytes containing the bytecodes for
171      * the class represented by the argument <b>path</b>.
172      * The <b>path</b> is a dot separated class name with
173      * the ".class" extension removed.
174      *
175      * @return the bytecodes for the class
176      * @exception ClassNotFoundException if the class corresponding
177      * to <b>path</b> could not be loaded.
178      * @exception java.io.IOException if error occurs reading the class
179      */

180     protected abstract byte[] getBytes(String JavaDoc path)
181         throws java.io.IOException JavaDoc, ClassNotFoundException JavaDoc;
182
183     //
184
// -- PRIVATE METHODS -----------------------------------------------
185
//
186

187     /**
188      * Create a new thread to listen.
189      */

190     private void newListener() {
191         (new Thread JavaDoc(this, "ClassServer-" + hostname + ":" + port)).start();
192     }
193
194     /**
195      * Returns the path to the class file obtained from
196      * parsing the HTML header.
197      */

198     private static RequestInfo getInfo(java.io.BufferedReader JavaDoc in)
199         throws java.io.IOException JavaDoc {
200         RequestInfo info = new RequestInfo();
201         String JavaDoc line = null;
202         do {
203             line = in.readLine();
204             if (line.startsWith("GET /")) {
205                 info.path = getPath(line);
206             } else if (line.startsWith("Host:")) {
207                 info.host = getHost(line);
208             } else {
209                 // eat line
210
}
211         } while ((line.length() != 0) && (line.charAt(0) != '\r') &&
212                 (line.charAt(0) != '\n'));
213         if (info.path != null) {
214             return info;
215         } else {
216             throw new java.io.IOException JavaDoc("Malformed Header");
217         }
218     }
219
220     /**
221      * Returns the path to the class file obtained from
222      * parsing the HTML header.
223      * @param line the GET item starting by "GET /"
224      */

225     private static String JavaDoc getPath(String JavaDoc line) {
226         // extract class from GET line
227
//line = line.substring(5, line.length() - 1).trim();
228
line = line.substring(5, line.length()).trim();
229         int index = line.indexOf(".class ");
230         if (index != -1) {
231             return line.substring(0, index).replace('/', '.');
232         } else {
233             return null;
234         }
235     }
236
237     /**
238      * Returns the path to the class file obtained from
239      * parsing the HTML header.
240      * @param line the GET item starting by "Host:"
241      */

242     private static String JavaDoc getHost(String JavaDoc line) {
243         // extract class from Host line
244

245         return line.substring(5, line.length()).trim();
246     }
247
248     private int boundServerSockect(int basePortNumber, int numberOfTry)
249         throws java.io.IOException JavaDoc {
250         for (int i = 0; i < numberOfTry; i++) {
251             try {
252                 server = new java.net.ServerSocket JavaDoc(basePortNumber);
253                 return basePortNumber;
254             } catch (java.io.IOException JavaDoc e) {
255                 basePortNumber += random.nextInt(DEFAULT_SERVER_PORT_INCREMENT);
256             }
257         }
258         throw new java.io.IOException JavaDoc(
259             "ClassServer cannot create a ServerSocket after " + numberOfTry +
260             " attempts !!!");
261     }
262
263     private static class RequestInfo {
264         String JavaDoc path;
265         String JavaDoc host;
266     }
267 }
268
Popular Tags