KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mmbase > servlet > rmi > RMIHandlerServlet


1 package org.mmbase.servlet.rmi;
2
3 import java.io.*;
4 import java.net.*;
5
6 import javax.servlet.*;
7 import javax.servlet.http.*;
8
9 import org.mmbase.servlet.MMBaseServlet;
10 import org.mmbase.util.logging.*;
11
12 /**
13  * The default RMI socket factory contains several "fallback"
14  * mechanisms which enable an RMI client to communicate with a remote
15  * server. When an RMI client initiates contact with a remote server,
16  * it attempts to establish a connection using each of the following
17  * protocols in turn, until one succeeds:
18  *
19  * 1. Direct TCP connection.
20  * 2. Direct HTTP connection.
21  * 3. Proxy connection (SOCKS or HTTP).
22  * 4. Connection on port 80 over HTTP to a CGI script.
23  * 5. Proxy HTTP connection to CGI script on port 80.
24  *
25  * The RMI ServletHandler can be used as replacement for the
26  * java-rmi.cgi script that comes with the Java Development Kit (and
27  * is invoked in protocols 4 and 5 above). The java-rmi.cgi script
28  * and the ServletHandler both function as proxy applications that
29  * forward remote calls embedded in HTTP to local RMI servers which
30  * service these calls. The RMI ServletHandler enables RMI to tunnel
31  * remote method calls over HTTP more efficiently than the existing
32  * java-rmi.cgi script. The ServletHandler is only loaded once from
33  * the servlet administration utility. The script, java-rmi.cgi, is
34  * executed once every remote call.
35  *
36  * The ServletHandler class contains methods for executing as a Java
37  * servlet extension. Because the RMI protocol only makes use of the
38  * HTTP post command, the ServletHandler only supports the
39  * <code>doPost</code> <code>HttpServlet</code> method. The
40  * <code>doPost</code> method of this class interprets a servlet
41  * request's query string as a command of the form
42  * "<command>=<parameters>". These commands are represented by the
43  * abstract interface, <code>RMICommandHandler</code>. Once the
44  * <code>doPost</code> method has parsed the requested command, it
45  * calls the execute method on one of several command handlers in the
46  * <code>commands</code> array.
47  *
48  * The command that actually proxies remote calls is the
49  * <code>ServletForwardCommand</code>. When the execute method is
50  * invoked on the ServletForwardCommand, the command will open a
51  * connection on a local port specified by its <code>param</code>
52  * parameter and will proceed to write the body of the relevant post
53  * request into this connection. It is assumed that an RMI server
54  * (e.g. SampleRMIServer) is listening on the local port, "param."
55  * The "forward" command will then read the RMI server's response and
56  * send this information back to the RMI client as the body of the
57  * response to the HTTP post method.
58  *
59  * Because the ServletHandler uses a local socket to proxy remote
60  * calls, the servlet has the ability to forward remote calls to local
61  * RMI objects that reside in the ServletVM or outside of it.
62  *
63  * Servlet documentation may be found at the following location:
64  *
65  * http://jserv.javasoft.com/products/java-server/documentation/
66  * webserver1.0.2/apidoc/Package-javax.servlet.http.html
67  */

68 public class RMIHandlerServlet extends MMBaseServlet {
69     private static Logger log = Logging.getLoggerInstance(RMIHandlerServlet.class.getName());
70     
71     public void init(ServletConfig config) throws ServletException {
72         super.init(config);
73         log.service("HTTP RMI bridge loaded");
74     }
75     
76     public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
77         try {
78             // Command and parameter for this POST request.
79
String JavaDoc queryString = req.getQueryString();
80             String JavaDoc command, param;
81             int delim = queryString.indexOf("=");
82             if (delim == -1) {
83                 command = queryString;
84                 param = "";
85             } else {
86                 command = queryString.substring(0, delim);
87                 param = queryString.substring(delim + 1);
88             }
89             
90             log.debug("command/param: " + command +"/" + param);
91             try {
92                 if (command.equals("forward")){
93                     forward(req,res,param);
94                 } else if (command.equals("gethostname")){
95                     getHostname(req,res,param);
96                 } else if (command.equals("ping")){
97                     ping(req,res,param);
98                 } else if (command.equals("hostname")){
99                     hostname(req,res,param);
100                 } else {
101                     returnClientError(res, "invalid command: " + command);
102                 }
103             } catch (ServletClientException e) {
104                 log.debug(e.getMessage(), e);
105                 returnClientError(res, "client error: " + e.getMessage());
106             } catch (ServletServerException e) {
107                 log.debug(e.getMessage(), e);
108                 returnServerError(res, "internal server error: " + e.getMessage());
109             }
110         } catch (Exception JavaDoc e) {
111             log.debug(e.getMessage(), e);
112             returnServerError(res, "internal error: " + e.getMessage() + " " + Logging.stackTrace(e));
113         }
114     }
115     
116     /**
117      * Provide more intelligible errors for methods that are likely to
118      * be called. Let unsupported HTTP "do*" methods result in an
119      * error generated by the super class.
120      *
121      * @param req http Servlet request, contains incoming command and
122      * arguments
123      *
124      * @param res http Servlet response
125      *
126      * @exception ServletException and IOException when invoking
127      * methods of <code>req<code> or <code>res<code>.
128      */

129     public void doGet(HttpServletRequest req, HttpServletResponse res)
130     throws ServletException, IOException {
131         returnClientError(res, "GET Operation not supported: Can only forward POST requests.");
132     }
133     
134     public void doPut(HttpServletRequest req, HttpServletResponse res)
135     throws ServletException, IOException {
136         returnClientError(res,
137         "PUT Operation not supported: " +
138         "Can only forward POST requests.");
139     }
140     
141     public String JavaDoc getServletInfo() {
142         return "RMI Call Forwarding Servlet";
143     }
144     
145     /**
146      * Return an HTML error message indicating there was error in
147      * the client's request.
148      *
149      * @param res Servlet response object through which <code>message</code>
150      * will be written to the client which invoked one of
151      * this servlet's methods.
152      * @param message Error message to be written to client.
153      */

154     private static void returnClientError(HttpServletResponse res, String JavaDoc message)
155     throws IOException {
156         
157         res.sendError(HttpServletResponse.SC_BAD_REQUEST,
158         "<HTML><HEAD>" +
159         "<TITLE>Java RMI Client Error</TITLE>" +
160         "</HEAD>" +
161         "<BODY>" +
162         "<H1>Java RMI Client Error</H1>" +
163         message +
164         "</BODY></HTML>");
165         
166         log.warn(HttpServletResponse.SC_BAD_REQUEST + "Java RMI Client Error" + message);
167     }
168     
169     /**
170      * Return an HTML error message indicating an internal error
171      * occurred here on the server.
172      *
173      * @param res Servlet response object through which <code>message</code>
174      * will be written to the servlet client.
175      * @param message Error message to be written to servlet client.
176      */

177     private static void returnServerError(HttpServletResponse res,
178     String JavaDoc message)
179     throws IOException {
180         
181         res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
182         "<HTML><HEAD>" +
183         "<TITLE>Java RMI Server Error</TITLE>" +
184         "</HEAD>" +
185         "<BODY>" +
186         "<H1>Java RMI Server Error</H1>" +
187         message + "</BODY></HTML>");
188         
189         log.warn(HttpServletResponse.SC_INTERNAL_SERVER_ERROR +
190         "Java RMI Server Error: " +
191         message);
192     }
193     
194     
195     
196     /**
197      * Execute the forward command. Forwards data from incoming servlet
198      * request to a port on the local machine. Presumably, an RMI server
199      * will be reading the data that this method sends.
200      *
201      * @param req The servlet request.
202      * @param res The servlet response.
203      * @param param Port to which data will be sent.
204      */

205     public void forward(HttpServletRequest req, HttpServletResponse res, String JavaDoc param)
206     throws ServletClientException, ServletServerException, IOException {
207         
208         int port;
209         try {
210             port = Integer.parseInt(param);
211         } catch (NumberFormatException JavaDoc e) {
212             throw new ServletClientException("invalid port number: " +
213             param);
214         }
215         if (port <= 0 || port > 0xFFFF)
216             throw new ServletClientException("invalid port: " + port);
217         if (port < 1024)
218             throw new ServletClientException("permission denied for port: "
219             + port);
220         
221         byte buffer[];
222         Socket socket;
223         try {
224             socket = new Socket(InetAddress.getLocalHost(), port);
225         } catch (IOException e) {
226             throw new ServletServerException("could not connect to " +
227             "local port");
228         }
229         
230         // read client's request body
231
DataInputStream clientIn =
232         new DataInputStream(req.getInputStream());
233         buffer = new byte[req.getContentLength()];
234         try {
235             clientIn.readFully(buffer);
236         } catch (EOFException e) {
237             throw new ServletClientException("unexpected EOF " +
238             "reading request body");
239         } catch (IOException e) {
240             throw new ServletClientException("error reading request" +
241             " body");
242         }
243         
244         DataOutputStream socketOut = null;
245         // send to local server in HTTP
246
try {
247             socketOut =
248             new DataOutputStream(socket.getOutputStream());
249             socketOut.writeBytes("POST / HTTP/1.0\r\n");
250             socketOut.writeBytes("Content-length: " +
251             req.getContentLength() + "\r\n\r\n");
252             socketOut.write(buffer);
253             socketOut.flush();
254         } catch (IOException e) {
255             throw new ServletServerException("error writing to server");
256         }
257         
258         // read response from local server
259
DataInputStream socketIn;
260         try {
261             socketIn = new DataInputStream(socket.getInputStream());
262         } catch (IOException e) {
263             throw new ServletServerException("error reading from " +
264             "server");
265         }
266         String JavaDoc key = "Content-length:".toLowerCase();
267         boolean contentLengthFound = false;
268         String JavaDoc line;
269         int responseContentLength = -1;
270         do {
271             try {
272                 line = socketIn.readLine();
273             } catch (IOException e) {
274                 throw new ServletServerException("error reading from server");
275             }
276             if (line == null)
277                 throw new ServletServerException(
278                 "unexpected EOF reading server response");
279             
280             if (line.toLowerCase().startsWith(key)) {
281                 if (contentLengthFound)
282                     ; // what would we want to do in this case??
283
responseContentLength =
284                 Integer.parseInt(line.substring(key.length()).trim());
285                 contentLengthFound = true;
286             }
287         } while ((line.length() != 0) &&
288         (line.charAt(0) != '\r') && (line.charAt(0) != '\n'));
289         
290         if (!contentLengthFound || responseContentLength < 0)
291             throw new ServletServerException(
292             "missing or invalid content length in server response");
293         buffer = new byte[responseContentLength];
294         try {
295             socketIn.readFully(buffer);
296         } catch (EOFException e) {
297             throw new ServletServerException(
298             "unexpected EOF reading server response");
299         } catch (IOException e) {
300             throw new ServletServerException("error reading from server");
301         }
302         
303         // send local server response back to servlet client
304
res.setStatus(HttpServletResponse.SC_OK);
305         res.setContentType("application/octet-stream");
306         res.setContentLength(buffer.length);
307         
308         try {
309             OutputStream out = res.getOutputStream();
310             out.write(buffer);
311             out.flush();
312         } catch (IOException e) {
313             throw new ServletServerException("error writing response");
314         } finally {
315             socketOut.close();
316             socketIn.close();
317         }
318     }
319     public void getHostname(HttpServletRequest req, HttpServletResponse res, String JavaDoc param)
320     throws ServletClientException, ServletServerException, IOException {
321         
322         byte[] getHostStringBytes = req.getServerName().getBytes();
323         
324         res.setStatus(HttpServletResponse.SC_OK);
325         res.setContentType("application/octet-stream");
326         res.setContentLength(getHostStringBytes.length);
327         
328         OutputStream out = res.getOutputStream();
329         out.write(getHostStringBytes);
330         out.flush();
331     }
332     
333     
334     public void ping(HttpServletRequest req, HttpServletResponse res, String JavaDoc param)
335     throws ServletClientException, ServletServerException, IOException {
336         res.setStatus(HttpServletResponse.SC_OK);
337         res.setContentType("application/octet-stream");
338         res.setContentLength(0);
339     }
340     
341     public void hostname(HttpServletRequest req, HttpServletResponse res, String JavaDoc param)
342     throws ServletClientException, ServletServerException, IOException {
343         
344         PrintWriter pw = res.getWriter();
345         
346         pw.println("");
347         pw.println("<HTML>" +
348         "<HEAD><TITLE>Java RMI Server Hostname Info" +
349         "</TITLE></HEAD>" +
350         "<BODY>");
351         pw.println("<H1>Java RMI Server Hostname Info</H1>");
352         pw.println("<H2>Local host name available to Java VM:</H2>");
353         pw.print("<P>InetAddress.getLocalHost().getHostName()");
354         try {
355             String JavaDoc localHostName = InetAddress.getLocalHost().getHostName();
356             
357             pw.println(" = " + localHostName);
358         } catch (UnknownHostException e) {
359             pw.println(" threw java.net.UnknownHostException");
360         }
361         
362         pw.println("<H2>Server host information obtained through Servlet " +
363         "interface from HTTP server:</H2>");
364         pw.println("<P>SERVER_NAME = " + req.getServerName());
365         pw.println("<P>SERVER_PORT = " + req.getServerPort());
366         pw.println("</BODY></HTML>");
367     }
368     
369     
370     /**
371      * ServletClientException is thrown when an error is detected
372      * in a client's request.
373      */

374     protected static class ServletClientException extends Exception JavaDoc {
375         public ServletClientException(String JavaDoc s) {
376             super(s);
377         }
378     }
379     
380     /**
381      * ServletServerException is thrown when an error occurs here on the server.
382      */

383     protected static class ServletServerException extends Exception JavaDoc {
384         public ServletServerException(String JavaDoc s) {
385             super(s);
386         }
387     }
388 }
389
Popular Tags