KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > catalina > util > ProcessHelper


1
2
3 /*
4  * The contents of this file are subject to the terms
5  * of the Common Development and Distribution License
6  * (the "License"). You may not use this file except
7  * in compliance with the License.
8  *
9  * You can obtain a copy of the license at
10  * glassfish/bootstrap/legal/CDDLv1.0.txt or
11  * https://glassfish.dev.java.net/public/CDDLv1.0.html.
12  * See the License for the specific language governing
13  * permissions and limitations under the License.
14  *
15  * When distributing Covered Code, include this CDDL
16  * HEADER in each file and include the License file at
17  * glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable,
18  * add the following below this CDDL HEADER, with the
19  * fields enclosed by brackets "[]" replaced with your
20  * own identifying information: Portions Copyright [yyyy]
21  * [name of copyright owner]
22  *
23  * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24  *
25  * Portions Copyright Apache Software Foundation.
26  */

27
28
29 package org.apache.catalina.util;
30
31 import java.lang.Process JavaDoc;
32 import java.io.File JavaDoc;
33 import java.io.Writer JavaDoc;
34 import java.io.Reader JavaDoc;
35 import java.io.PrintWriter JavaDoc;
36 import java.io.BufferedWriter JavaDoc;
37 import java.io.BufferedReader JavaDoc;
38 import java.io.InputStream JavaDoc;
39 import java.io.OutputStream JavaDoc;
40 import java.io.InputStreamReader JavaDoc;
41 import java.io.OutputStreamWriter JavaDoc;
42 import java.io.BufferedInputStream JavaDoc;
43 import java.io.BufferedOutputStream JavaDoc;
44 import java.io.IOException JavaDoc;
45 import java.net.URLEncoder JavaDoc;
46 import java.util.Hashtable JavaDoc;
47 import java.util.Vector JavaDoc;
48 import java.util.Enumeration JavaDoc;
49 import java.util.StringTokenizer JavaDoc;
50 import java.util.Locale JavaDoc;
51 import java.util.Date JavaDoc;
52 import javax.servlet.ServletException JavaDoc;
53 import javax.servlet.ServletOutputStream JavaDoc;
54 import javax.servlet.ServletContext JavaDoc;
55 import javax.servlet.ServletConfig JavaDoc;
56 import javax.servlet.http.HttpServlet JavaDoc;
57 import javax.servlet.http.HttpServletRequest JavaDoc;
58 import javax.servlet.http.HttpServletResponse JavaDoc;
59 import javax.servlet.http.HttpSession JavaDoc;
60 import javax.servlet.http.Cookie JavaDoc;
61 import org.apache.catalina.Context;
62 import org.apache.catalina.Wrapper;
63 // import org.apache.catalina.util.StringManager;
64

65
66 //class CGIServlet
67

68
69 /**
70      * Encapsulates the knowledge of how to run a CGI script, given the
71      * script's desired environment and (optionally) input/output streams
72      *
73      * <p>
74      *
75      * Exposes a <code>run</code> method used to actually invoke the
76      * CGI.
77      *
78      * </p>
79      * <p>
80      *
81      * The CGI environment and settings are derived from the information
82      * passed to the constuctor.
83      *
84      * </p>
85      * <p>
86      *
87      * The input and output streams can be set by the <code>setInput</code>
88      * and <code>setResponse</code> methods, respectively.
89      * </p>
90      *
91      * @author Martin Dengler [root@martindengler.com]
92      * @version $Revision: 1.3 $, $Date: 2006/03/12 01:27:08 $
93      */

94 public class ProcessHelper {
95
96 private static com.sun.org.apache.commons.logging.Log log=
97     com.sun.org.apache.commons.logging.LogFactory.getLog( ProcessHelper.class );
98
99 /** script/command to be executed */
100 private String JavaDoc command = null;
101
102 /** environment used when invoking the cgi script */
103 private Hashtable JavaDoc env = null;
104
105 /** working directory used when invoking the cgi script */
106 private File JavaDoc wd = null;
107
108 /** query parameters to be passed to the invoked script */
109 private Hashtable JavaDoc params = null;
110
111 /** stdin to be passed to cgi script */
112 private InputStream JavaDoc stdin = null;
113
114 /** response object used to set headers & get output stream */
115 private HttpServletResponse JavaDoc response = null;
116
117 /** boolean tracking whether this object has enough info to run() */
118 private boolean readyToRun = false;
119
120 /** the debugging detail level for this instance. */
121 private int debug = 0;
122
123 /** the time in ms to wait for the client to send us CGI input data */
124 private int iClientInputTimeout;
125
126 /**
127  * Creates a ProcessHelper and initializes its environment, working
128  * directory, and query parameters.
129  * <BR>
130  * Input/output streams (optional) are set using the
131  * <code>setInput</code> and <code>setResponse</code> methods,
132  * respectively.
133  *
134  * @param command string full path to command to be executed
135  * @param env Hashtable with the desired script environment
136  * @param wd File with the script's desired working directory
137  * @param params Hashtable with the script's query parameters
138  *
139  * @param res HttpServletResponse object for setting headers
140  * based on CGI script output
141  *
142  */

143 public ProcessHelper(String JavaDoc command, Hashtable JavaDoc env, File JavaDoc wd,
144                     Hashtable JavaDoc params) {
145     this.command = command;
146     this.env = env;
147     this.wd = wd;
148     this.params = params;
149     updateReadyStatus();
150 }
151
152
153
154 /**
155  * Checks & sets ready status
156  */

157 protected void updateReadyStatus() {
158     if (command != null
159         && env != null
160         && wd != null
161         && params != null
162         && response != null) {
163         readyToRun = true;
164     } else {
165         readyToRun = false;
166     }
167 }
168
169
170
171 /**
172  * Gets ready status
173  *
174  * @return false if not ready (<code>run</code> will throw
175  * an exception), true if ready
176  */

177 public boolean isReady() {
178     return readyToRun;
179 }
180
181
182
183 /**
184  * Sets HttpServletResponse object used to set headers and send
185  * output to
186  *
187  * @param response HttpServletResponse to be used
188  *
189  */

190 public void setResponse(HttpServletResponse JavaDoc response) {
191     this.response = response;
192     updateReadyStatus();
193 }
194
195
196
197 /**
198  * Sets standard input to be passed on to the invoked cgi script
199  *
200  * @param stdin InputStream to be used
201  *
202  */

203 public void setInput(InputStream JavaDoc stdin) {
204     this.stdin = stdin;
205     updateReadyStatus();
206 }
207
208
209
210 /**
211  * Converts a Hashtable to a String array by converting each
212  * key/value pair in the Hashtable to a String in the form
213  * "key=value" (hashkey + "=" + hash.get(hashkey).toString())
214  *
215  * @param h Hashtable to convert
216  *
217  * @return converted string array
218  *
219  * @exception NullPointerException if a hash key has a null value
220  *
221  */

222 private String JavaDoc[] hashToStringArray(Hashtable JavaDoc h)
223     throws NullPointerException JavaDoc {
224     Vector JavaDoc v = new Vector JavaDoc();
225     Enumeration JavaDoc e = h.keys();
226     while (e.hasMoreElements()) {
227         String JavaDoc k = e.nextElement().toString();
228         v.add(k + "=" + h.get(k));
229     }
230     String JavaDoc[] strArr = new String JavaDoc[v.size()];
231     v.copyInto(strArr);
232     return strArr;
233 }
234
235
236
237 /**
238  * Executes a process script with the desired environment, current working
239  * directory, and input/output streams
240  *
241  * <p>
242  * This implements the following CGI specification recommedations:
243  * <UL>
244  * <LI> Servers SHOULD provide the "<code>query</code>" component of
245  * the script-URI as command-line arguments to scripts if it
246  * does not contain any unencoded "=" characters and the
247  * command-line arguments can be generated in an unambiguous
248  * manner.
249  * <LI> Servers SHOULD set the AUTH_TYPE metavariable to the value
250  * of the "<code>auth-scheme</code>" token of the
251  * "<code>Authorization</code>" if it was supplied as part of the
252  * request header. See <code>getCGIEnvironment</code> method.
253  * <LI> Where applicable, servers SHOULD set the current working
254  * directory to the directory in which the script is located
255  * before invoking it.
256  * <LI> Server implementations SHOULD define their behavior for the
257  * following cases:
258  * <ul>
259  * <LI> <u>Allowed characters in pathInfo</u>: This implementation
260  * does not allow ASCII NUL nor any character which cannot
261  * be URL-encoded according to internet standards;
262  * <LI> <u>Allowed characters in path segments</u>: This
263  * implementation does not allow non-terminal NULL
264  * segments in the the path -- IOExceptions may be thrown;
265  * <LI> <u>"<code>.</code>" and "<code>..</code>" path
266  * segments</u>:
267  * This implementation does not allow "<code>.</code>" and
268  * "<code>..</code>" in the the path, and such characters
269  * will result in an IOException being thrown;
270  * <LI> <u>Implementation limitations</u>: This implementation
271  * does not impose any limitations except as documented
272  * above. This implementation may be limited by the
273  * servlet container used to house this implementation.
274  * In particular, all the primary CGI variable values
275  * are derived either directly or indirectly from the
276  * container's implementation of the Servlet API methods.
277  * </ul>
278  * </UL>
279  * </p>
280  *
281  * For more information, see java.lang.Runtime#exec(String command,
282  * String[] envp, File dir)
283  *
284  * @exception IOException if problems during reading/writing occur
285  *
286  */

287 public void run() throws IOException JavaDoc {
288
289     /*
290      * REMIND: this method feels too big; should it be re-written?
291      */

292
293     if (!isReady()) {
294         throw new IOException JavaDoc(this.getClass().getName()
295                               + ": not ready to run.");
296     }
297
298     if (debug >= 1 ) {
299         log("runCGI(envp=[" + env + "], command=" + command + ")");
300     }
301
302     if ((command.indexOf(File.separator + "." + File.separator) >= 0)
303         || (command.indexOf(File.separator + "..") >= 0)
304         || (command.indexOf(".." + File.separator) >= 0)) {
305         throw new IOException JavaDoc(this.getClass().getName()
306                               + "Illegal Character in CGI command "
307                               + "path ('.' or '..') detected. Not "
308                               + "running CGI [" + command + "].");
309     }
310
311     /* original content/structure of this section taken from
312      * http://developer.java.sun.com/developer/
313      * bugParade/bugs/4216884.html
314      * with major modifications by Martin Dengler
315      */

316     Runtime JavaDoc rt = null;
317     BufferedReader JavaDoc commandsStdOut = null;
318     BufferedReader JavaDoc commandsStdErr = null;
319     BufferedOutputStream JavaDoc commandsStdIn = null;
320     Process JavaDoc proc = null;
321     byte[] bBuf = new byte[1024];
322     char[] cBuf = new char[1024];
323     int bufRead = -1;
324
325     //create query arguments
326
Enumeration JavaDoc paramNames = params.keys();
327     StringBuffer JavaDoc cmdAndArgs = new StringBuffer JavaDoc(command);
328     if (paramNames != null && paramNames.hasMoreElements()) {
329         cmdAndArgs.append(" ");
330         while (paramNames.hasMoreElements()) {
331             String JavaDoc k = (String JavaDoc) paramNames.nextElement();
332             String JavaDoc v = params.get(k).toString();
333             if ((k.indexOf("=") < 0) && (v.indexOf("=") < 0)) {
334                 cmdAndArgs.append(k);
335                 cmdAndArgs.append("=");
336                 v = java.net.URLEncoder.encode(v);
337                 cmdAndArgs.append(v);
338                 cmdAndArgs.append(" ");
339             }
340         }
341     }
342
343     String JavaDoc postIn = getPostInput(params);
344     int contentLength = (postIn.length()
345             + System.getProperty("line.separator").length());
346     if ("POST".equals(env.get("REQUEST_METHOD"))) {
347         env.put("CONTENT_LENGTH", new Integer JavaDoc(contentLength));
348     }
349
350     rt = Runtime.getRuntime();
351     proc = rt.exec(cmdAndArgs.toString(), hashToStringArray(env), wd);
352
353
354     /*
355      * provide input to cgi
356      * First -- parameters
357      * Second -- any remaining input
358      */

359     commandsStdIn = new BufferedOutputStream JavaDoc(proc.getOutputStream());
360     if (debug >= 2 ) {
361         log("runCGI stdin=[" + stdin + "], qs="
362             + env.get("QUERY_STRING"));
363     }
364     if ("POST".equals(env.get("REQUEST_METHOD"))) {
365         if (debug >= 2) {
366             log("runCGI: writing ---------------\n");
367             log(postIn);
368             log("runCGI: new content_length=" + contentLength
369                 + "---------------\n");
370         }
371         commandsStdIn.write(postIn.getBytes());
372     }
373     if (stdin != null) {
374         //REMIND: document this
375
/* assume if nothing is available after a time, that nothing is
376          * coming...
377          */

378         if (stdin.available() <= 0) {
379             if (debug >= 2 ) {
380                 log("runCGI stdin is NOT available ["
381                     + stdin.available() + "]");
382             }
383             try {
384                 Thread.currentThread().sleep(iClientInputTimeout);
385             } catch (InterruptedException JavaDoc ignored) {
386             }
387         }
388         if (stdin.available() > 0) {
389             if (debug >= 2 ) {
390                 log("runCGI stdin IS available ["
391                     + stdin.available() + "]");
392             }
393             bBuf = new byte[1024];
394             bufRead = -1;
395             try {
396                 while ((bufRead = stdin.read(bBuf)) != -1) {
397                     if (debug >= 2 ) {
398                         log("runCGI: read [" + bufRead
399                             + "] bytes from stdin");
400                     }
401                     commandsStdIn.write(bBuf, 0, bufRead);
402                 }
403                 if (debug >= 2 ) {
404                     log("runCGI: DONE READING from stdin");
405                 }
406             } catch (IOException JavaDoc ioe) {
407                 //REMIND: replace with logging
408
//REMIND: should I throw this exception?
409
log("runCGI: couldn't write all bytes.");
410                 ioe.printStackTrace();
411             }
412         }
413     }
414     commandsStdIn.flush();
415     commandsStdIn.close();
416
417     /* we want to wait for the process to exit, Process.waitFor()
418      * is useless in our situation; see
419      * http://developer.java.sun.com/developer/
420      * bugParade/bugs/4223650.html
421      */

422
423     boolean isRunning = true;
424     commandsStdOut = new BufferedReader JavaDoc
425         (new InputStreamReader JavaDoc(proc.getInputStream()));
426     commandsStdErr = new BufferedReader JavaDoc
427         (new InputStreamReader JavaDoc(proc.getErrorStream()));
428     BufferedWriter JavaDoc servletContainerStdout = null;
429
430     try {
431         if (response.getOutputStream() != null) {
432             servletContainerStdout =
433                 new BufferedWriter JavaDoc(new OutputStreamWriter JavaDoc
434                     (response.getOutputStream()));
435         }
436     } catch (IOException JavaDoc ignored) {
437         //NOOP: no output will be written
438
}
439
440     while (isRunning) {
441
442         try {
443             //read stderr first
444
cBuf = new char[1024];
445             while ((bufRead = commandsStdErr.read(cBuf)) != -1) {
446                 if (servletContainerStdout != null) {
447                     servletContainerStdout.write(cBuf, 0, bufRead);
448                 }
449             }
450
451             //set headers
452
String JavaDoc line = null;
453             while (((line = commandsStdOut.readLine()) != null)
454                    && !("".equals(line))) {
455                 if (debug >= 2) {
456                     log("runCGI: addHeader(\"" + line + "\")");
457                 }
458                 if (line.startsWith("HTTP")) {
459                     //TODO: should set status codes (NPH support)
460
/*
461                      * response.setStatus(getStatusCode(line));
462                      */

463                 } else {
464                     response.addHeader
465                         (line.substring(0, line.indexOf(":")).trim(),
466                          line.substring(line.indexOf(":") + 1).trim());
467                 }
468             }
469
470             //write output
471
cBuf = new char[1024];
472             while ((bufRead = commandsStdOut.read(cBuf)) != -1) {
473                 if (servletContainerStdout != null) {
474                     if (debug >= 4) {
475                         log("runCGI: write(\"" + cBuf + "\")");
476                     }
477                     servletContainerStdout.write(cBuf, 0, bufRead);
478                 }
479             }
480
481             if (servletContainerStdout != null) {
482                 servletContainerStdout.flush();
483             }
484
485             proc.exitValue(); // Throws exception if alive
486

487             isRunning = false;
488
489         } catch (IllegalThreadStateException JavaDoc e) {
490             try {
491                 Thread.currentThread().sleep(500);
492             } catch (InterruptedException JavaDoc ignored) {
493             }
494         }
495     } //replacement for Process.waitFor()
496

497
498 }
499
500
501 /**
502  * Gets a string for input to a POST cgi script
503  *
504  * @param params Hashtable of query parameters to be passed to
505  * the CGI script
506  * @return for use as input to the CGI script
507  */

508
509 protected String JavaDoc getPostInput(Hashtable JavaDoc params) {
510     String JavaDoc lineSeparator = System.getProperty("line.separator");
511     Enumeration JavaDoc paramNames = params.keys();
512     StringBuffer JavaDoc postInput = new StringBuffer JavaDoc("");
513     StringBuffer JavaDoc qs = new StringBuffer JavaDoc("");
514     if (paramNames != null && paramNames.hasMoreElements()) {
515         while (paramNames.hasMoreElements()) {
516             String JavaDoc k = (String JavaDoc) paramNames.nextElement();
517             String JavaDoc v = params.get(k).toString();
518             if ((k.indexOf("=") < 0) && (v.indexOf("=") < 0)) {
519                 postInput.append(k);
520                 qs.append(k);
521                 postInput.append("=");
522                 qs.append("=");
523                 postInput.append(v);
524                 qs.append(v);
525                 postInput.append(lineSeparator);
526                 qs.append("&");
527             }
528         }
529     }
530     qs.append(lineSeparator);
531     return qs.append(postInput).toString();
532 }
533
534
535 private void log(String JavaDoc s) {
536     if (log.isDebugEnabled())
537         log.debug(s);
538 }
539
540
541 public int getIClientInputTimeout(){
542         return iClientInputTimeout;
543     }
544
545
546 public void setIClientInputTimeout(int iClientInputTimeout){
547         this.iClientInputTimeout = iClientInputTimeout;
548     }
549 }
550
551
552
Popular Tags