001package org.biojava.utils;
002
003
004///////////////////////////////////////////////////////////////////////////////
005// Copyright (C) 2002  Scott McCrory
006//
007// This program is free software; you can redistribute it and/or
008// modify it under the terms of the GNU Lesser General Public License
009// as published by the Free Software Foundation; either version 2
010// of the License, or (at your option) any later version.
011//
012// This program is distributed in the hope that it will be useful,
013// but WITHOUT ANY WARRANTY; without even the implied warranty of
014// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
015// GNU Lesser General Public License for more details.
016//
017// You should have received a copy of the GNU Lesser General Public License
018// along with this program; if not, write to the Free Software
019// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
020///////////////////////////////////////////////////////////////////////////////
021
022import java.io.BufferedReader;
023import java.io.IOException;
024import java.io.InputStream;
025import java.io.InputStreamReader;
026import java.io.ObjectInputStream;
027import java.io.ObjectOutputStream;
028import java.io.OutputStream;
029import java.io.PrintWriter;
030import java.io.StringWriter;
031import java.util.Date;
032import java.util.StringTokenizer;
033
034  /**
035   * <P>Makes running external executables easier, optionally under a watched thread.
036   *
037   * In addition, probably the most useful feature of ExecRunner is using it to
038   * run a command-line program and obtain its stdout and stderr results in two
039   * strings.  This is done with exec(String) - see that method for an example.
040   *
041   * With acknowledgements to Michael C. Daconta, author of "Java Pitfalls,
042   * Time Saving Solutions, and Workarounds to Improve Programs." and his
043   * article in JavaWorld "When Runtime.exec() Won't".</P>
044   *
045   * @author <a href="mailto:smccrory@users.sourceforge.net">Scott McCrory</a>.
046   * @author Francois Pepin
047   * @author Andreas Dr&auml;ger 
048   * @version CVS $Id$
049   */
050  public class ExecRunner {
051
052      /** Win NT/2K/MEPro require cmd.exe to run programs **/
053      private static final String WINDOWS_NT_2000_COMMAND_1 = "cmd.exe";
054
055      /** Win NT/2K/MEPro require the /C to specify what to run **/
056      private static final String WINDOWS_NT_2000_COMMAND_2 = "/C";
057
058      /** Win 9X/MEHome require cmd.exe to run programs **/
059      private static final String WINDOWS_9X_ME_COMMAND_1 = "command.exe";
060
061      /** Win 9X/MEHome require the /C to specify what to run **/
062      private static final String WINDOWS_9X_ME_COMMAND_2 = "/C";
063
064      /** String to send to STDERR if program exceeds max run time **/
065      private static final String MAX_RUN_TIME_EXCEEDED_STRING =
066          "MAX_RUN_TIME_EXCEEDED";
067
068      /** String to capture STDOUT **/
069      private String out = new String();
070
071      /** String to capture STDERR **/
072      private String err = new String();
073
074      /** Default max run time (in seconds) **/
075      private int maxRunTimeSecs = 0;
076
077      /** Flag to indicate if we've exceeded max run time **/
078      private boolean maxRunTimeExceeded = false;
079
080      /**
081       * Basic ExecRunner constructor.
082       *
083       */
084      public ExecRunner() {
085          super();
086      }
087
088      /**
089       * ExecRunner constructor which also conveniently runs exec(String).
090       *
091       * @param command The program or command to run
092       * @throws ExceptionInInitializerError thrown if a problem occurs
093       */
094      public ExecRunner(String command) throws ExceptionInInitializerError {
095          this();
096          try {
097              exec(command);
098          }
099          catch (IOException ioe) {
100             throw new ExceptionInInitializerError(ioe.getMessage());
101         }
102         catch (InterruptedException inte) {
103             throw new ExceptionInInitializerError(inte.getMessage());
104         }
105
106     }
107      
108      /**
109       * ExecRunner constructor which also conveniently runs exec(String).
110       *
111       * @param command The program or command to run
112       * @param arguments An array of Strings in which every single String 
113       *   is exactly one of the program's arguments. So these arguments are
114       *   allowed to contain white spaces and are marked as Strings by the
115       *   system.
116       * @throws ExceptionInInitializerError thrown if a problem occurs
117       * @since 1.5
118       */
119      public ExecRunner(String command, String[] arguments) 
120        throws ExceptionInInitializerError {
121        this();
122        try {
123          exec(command, arguments);
124        } catch (IOException ioe) {
125          throw new ExceptionInInitializerError(ioe.getMessage());
126        } catch (InterruptedException inte) {
127          throw new ExceptionInInitializerError(inte.getMessage());
128        }
129      }
130
131      
132     /**
133      * We override the <code>clone</code> method here to prevent cloning of our class.
134      *
135      * @throws CloneNotSupportedException To indicate cloning is not allowed
136      * @return Nothing ever really returned since we throw a CloneNotSupportedException
137      **/
138     public final Object clone() throws CloneNotSupportedException {
139
140         throw new java.lang.CloneNotSupportedException();
141
142     }
143
144     /**
145      * The <B>exec(String)</B> method runs a process inside of a watched thread.
146      * It returns the client's exit code and feeds its STDOUT and STDERR to
147      * ExecRunner's out and err strings, where you then use getOutString()
148      * and getErrString() to obtain these values. 
149      * If the command String contains arguments which are Strings containing white 
150      * spaces, the method <pre>exec(program, arguments)</pre> should be used 
151      * instead, where <code>arguments</code> is a <code>String[]</code> array 
152      * containing the arguments of the program.
153      * 
154      * Example:
155      *
156      * <pre>
157      * // Execute the program and grab the results
158      * String out = "";
159      * String err = "";
160      * try {
161      *
162      *     ExecRunner er = new ExecRunner();
163      *     er.setMaxRunTimeSecs(maxRunTime);
164      *     er.exec(program);
165      *     if (!er.getMaxRunTimeExceeded()) {
166      *         out = er.getOutString();
167      *         err = er.getErrString();
168      *     }
169      *     else {
170      *         log.error("Maximum run time exceeded!");
171      *         continue;
172      *     }
173      *
174      * }
175      * catch (Exception e) {
176      *     log.error("Error executing " + program + ": " + e.getMessage());
177      *     continue;
178      * }
179      * </pre>
180      *
181      * @return The command's return code
182      * @param command The program or command to run
183      * @throws IOException thrown if a problem occurs
184      * @throws InterruptedException thrown if a problem occurs
185      */
186     public int exec(String command) throws IOException, InterruptedException {
187
188         StringWriter swOut = new StringWriter();
189         PrintWriter pwOut = new PrintWriter(swOut, true);
190
191         StringWriter swErr = new StringWriter();
192         PrintWriter pwErr = new PrintWriter(swErr, true);
193
194         int rc = exec(command, pwOut, pwErr);
195
196         out = swOut.toString();
197         err = swErr.toString();
198
199         return rc;
200
201     }
202     
203     
204  /** Sometimes special cases may occur that the arguments of an external program 
205   * are Strings containing white spaces. Another example are external processes 
206   * needing the String arguments to be characterized in a special way. 
207   * In this cases it is sensefull to write every single argument in an String 
208   * array. The system itselfe will mark these arguments as Strings so that these 
209   * special encoding can be omitted. 
210   * @param command The external program to be executed.
211   * @param arguments An array of Strings. Every entry of this array is an argument
212   *   of the external program to be executed.
213   * @return The command's return code
214   * @throws IOException thrown if a problem occurs
215   * @throws InterruptedException thrown if a problem occurs
216   * @since 1.5
217   */
218  public int exec(String command, String[] arguments) 
219    throws IOException, InterruptedException {
220
221     StringWriter swOut = new StringWriter();
222     PrintWriter pwOut = new PrintWriter(swOut, true);
223
224     StringWriter swErr = new StringWriter();
225     PrintWriter pwErr = new PrintWriter(swErr, true);
226
227     int rc = exec(command, arguments, pwOut, pwErr);
228
229     out = swOut.toString();
230     err = swErr.toString();
231
232     return rc;
233   }
234
235     /**
236      * Convenience method for calling exec with OutputStreams.
237      *
238      * @return The command's return code
239      * @param command The program or command to run
240      * @param stdoutStream java.io.OutputStream
241      * @param stderrStream java.io.OutputStream
242      * @throws IOException thrown if a problem occurs
243      * @throws InterruptedException thrown if a problem occurs
244      **/
245     public int exec(
246         String command,
247         OutputStream stdoutStream,
248         OutputStream stderrStream)
249         throws IOException, InterruptedException {
250
251         PrintWriter pwOut = new PrintWriter(stdoutStream, true);
252         PrintWriter pwErr = new PrintWriter(stderrStream, true);
253
254         return exec(command, pwOut, pwErr);
255     }
256     
257     
258     /**
259      * Convenience method for calling exec with OutputStreams.
260      *
261      * @return The command's return code
262      * @param command The program or command to run
263      * @param arguments An array of Strings containing the program's arguments.
264      * @param stdoutStream java.io.OutputStream
265      * @param stderrStream java.io.OutputStream
266      * @throws IOException thrown if a problem occurs
267      * @throws InterruptedException thrown if a problem occurs
268      * @since 1.5
269      **/
270     public int exec(
271       String command,
272       String[] arguments,
273       OutputStream stdoutStream,
274       OutputStream stderrStream)
275     throws IOException, InterruptedException {
276
277       PrintWriter pwOut = new PrintWriter(stdoutStream, true);
278       PrintWriter pwErr = new PrintWriter(stderrStream, true);
279
280       return exec(command, arguments, pwOut, pwErr);
281     }
282
283     /**
284      * The <code>exec(String, PrintWriter, PrintWriter)</code> method runs
285      * a process inside of a watched thread.  It returns the client's exit code
286      * and feeds its STDOUT and STDERR to the passed-in streams.
287      *
288      * @return The command's return code
289      * @param command The program or command to run
290      * @param stdoutWriter java.io.PrintWriter
291      * @param stderrWriter java.io.PrintWriter
292      * @throws IOException thrown if a problem occurs
293      * @throws InterruptedException thrown if a problem occurs
294      **/
295     public int exec(
296         String command,
297         PrintWriter stdoutWriter,
298         PrintWriter stderrWriter)
299         throws IOException, InterruptedException {
300
301         // Default exit value is non-zero to indicate a problem.
302         int exitVal = 1;
303
304         ////////////////////////////////////////////////////////////////
305         Runtime rt = Runtime.getRuntime();
306         Process proc;
307         String[] cmd = null;
308
309         // First get the start time & calculate comparison numbers
310         Date startTime = new Date();
311         long startTimeMs = startTime.getTime();
312         long maxTimeMs = startTimeMs + (maxRunTimeSecs * 1000);
313
314         ////////////////////////////////////////////////////////////////
315         // First determine the OS to build the right command string
316         String osName = System.getProperty("os.name");
317         if (osName.equals("Windows NT") || osName.equals("Windows 2000")) {
318             cmd = new String[3];
319             cmd[0] = WINDOWS_NT_2000_COMMAND_1;
320             cmd[1] = WINDOWS_NT_2000_COMMAND_2;
321             cmd[2] = command;
322         }
323         else if (
324             osName.equals("Windows 95")
325                 || osName.equals("Windows 98")
326                 || osName.equals("Windows ME")) {
327             cmd = new String[3];
328             cmd[0] = WINDOWS_9X_ME_COMMAND_1;
329             cmd[1] = WINDOWS_9X_ME_COMMAND_2;
330             cmd[2] = command;
331         }
332         else {
333             // Linux (and probably other *nixes) prefers to be called
334             // with each argument supplied separately, so we first
335             // Tokenize it across spaces as the boundary.
336     StringTokenizer st = new StringTokenizer(command, " ");
337     cmd = new String[st.countTokens()];
338     int token = 0;
339     while (st.hasMoreTokens()) {
340   String tokenString = st.nextToken();
341   //System.out.println(tokenString);
342   cmd[token++] = tokenString;
343     }
344         }
345
346         // Execute the command and start the two output gobblers
347         if (cmd != null && cmd.length > 0) {
348             //System.out.println("**Checkpoint** :" + cmd.length);
349             proc = rt.exec(cmd);
350         }
351         else throw new IOException("Insufficient commands!");
352
353         StreamGobbler outputGobbler =
354             new StreamGobbler(proc.getInputStream(), stdoutWriter);
355         StreamGobbler errorGobbler =
356             new StreamGobbler(proc.getErrorStream(), stderrWriter);
357         outputGobbler.start();
358         errorGobbler.start();
359
360         // Wait for the program to finish running and return the
361         // exit value obtained from the executable
362   while (true) {
363     try {
364                 exitVal = proc.exitValue();
365                   break;
366           }
367           catch (IllegalThreadStateException e) {
368                   // If we get this exception, then the process isn't
369                   // done executing and we determine if our time is up.
370                   if (maxRunTimeSecs > 0) {
371              Date endTime = new Date();
372               long endTimeMs = endTime.getTime();
373               if (endTimeMs > maxTimeMs) {
374                             // Time's up - kill the process and the gobblers and return
375                             proc.destroy();
376                             maxRunTimeExceeded = true;
377                             stderrWriter.println(MAX_RUN_TIME_EXCEEDED_STRING);
378                             outputGobbler.quit();
379                             errorGobbler.quit();
380           stdoutWriter.close();
381           stderrWriter.close();
382           proc.getOutputStream().close();
383                             return exitVal;
384               } else {
385                   // Time is not up yet so wait 100 ms before testing again
386                           Thread.sleep(100);
387                     }
388                   }
389     }
390   }
391   
392   ////////////////////////////////////////////////////////////////
393   // Wait for output gobblers to finish forwarding the output
394   while (outputGobbler.isAlive() || errorGobbler.isAlive()) {
395   }
396
397   ////////////////////////////////////////////////////////////////
398   // All done, flush the streams and return the exit value
399   stdoutWriter.flush();
400   stderrWriter.flush();
401   stdoutWriter.close();
402   stderrWriter.close();
403   proc.getOutputStream().close();
404   return exitVal;
405
406         }
407   
408   
409   /**
410    * The <code>exec(String, PrintWriter, PrintWriter)</code> method runs
411    * a process inside of a watched thread.  It returns the client's exit code
412    * and feeds its STDOUT and STDERR to the passed-in streams.
413    * The arguments for the external program or command are passed as an array
414    * of Strings. This has the advantage that the arguments are allowed to 
415    * contain white spaces and are marked to be Strings by the System itselfe, 
416    * which is sometimes important. Every single argument has to be exactly one
417    * dimension of the arguments array.
418    *
419    * @return The command's return code
420    * @param command The program or command to run
421    * @param arguments An array of Strings containing the program's arguments/options.
422    * @param stdoutWriter java.io.PrintWriter
423    * @param stderrWriter java.io.PrintWriter
424    * @throws IOException thrown if a problem occurs
425    * @throws InterruptedException thrown if a problem occurs
426    * @since 1.5
427    **/
428   public int exec(
429     String command,
430     String[] arguments,
431     PrintWriter stdoutWriter,
432     PrintWriter stderrWriter)
433     throws IOException, InterruptedException {
434
435     // Default exit value is non-zero to indicate a problem.
436     int exitVal = 1;
437
438     ////////////////////////////////////////////////////////////////
439     Runtime rt = Runtime.getRuntime();
440     Process proc;
441     String[] cmd = null;
442
443     // First get the start time & calculate comparison numbers
444     Date startTime = new Date();
445     long startTimeMs = startTime.getTime();
446     long maxTimeMs = startTimeMs + (maxRunTimeSecs * 1000);
447     int i = 0;
448
449     ////////////////////////////////////////////////////////////////
450     // First determine the OS to build the right command string
451     String osName = System.getProperty("os.name");
452     if (osName.startsWith("Windows")) {
453       cmd = new String[3+arguments.length];
454       if (osName.endsWith("NT") || osName.endsWith("2000")) {
455         cmd[0] = WINDOWS_NT_2000_COMMAND_1;
456         cmd[1] = WINDOWS_NT_2000_COMMAND_2;  
457       } else if (osName.endsWith("95") || osName.endsWith("98") || osName.endsWith("ME")) {
458         cmd[0] = WINDOWS_9X_ME_COMMAND_1;
459         cmd[1] = WINDOWS_9X_ME_COMMAND_2;  
460       }
461       cmd[2] = command;
462       for (i = 3; i<arguments.length; i++)
463         cmd[i] = arguments[i-3];
464     } else {
465       // Linux (and probably other *nixes) prefers to be called
466       // with each argument supplied separately.
467       cmd = new String[arguments.length+1];
468       cmd[0] = command;
469       for (i=1; i<cmd.length; i++) 
470         cmd[i] = arguments[i-1];
471     }
472
473     // Execute the command and start the two output gobblers
474     if (cmd != null && cmd.length > 0) {
475       //System.out.println("**Checkpoint** :" + cmd.length);
476       proc = rt.exec(cmd);
477     } else {
478       throw new IOException("Insufficient commands!");
479     }
480
481     StreamGobbler outputGobbler =
482       new StreamGobbler(proc.getInputStream(), stdoutWriter);
483     StreamGobbler errorGobbler =
484       new StreamGobbler(proc.getErrorStream(), stderrWriter);
485     outputGobbler.start();
486     errorGobbler.start();
487
488     // Wait for the program to finish running and return the
489     // exit value obtained from the executable
490     while (true) {
491       try {
492         exitVal = proc.exitValue();
493         break;
494       } catch (IllegalThreadStateException e) {
495         // If we get this exception, then the process isn't
496         // done executing and we determine if our time is up.
497         if (maxRunTimeSecs > 0) {
498           Date endTime = new Date();
499           long endTimeMs = endTime.getTime();
500           if (endTimeMs > maxTimeMs) {
501             // Time's up - kill the process and the gobblers and return
502             proc.destroy();
503             maxRunTimeExceeded = true;
504             stderrWriter.println(MAX_RUN_TIME_EXCEEDED_STRING);
505             outputGobbler.quit();
506             errorGobbler.quit();
507             stdoutWriter.close();
508             proc.getOutputStream().close();
509             stderrWriter.close();
510             return exitVal;
511           } else {
512             // Time is not up yet so wait 100 ms before testing again
513             Thread.sleep(100);
514           }
515         }
516       }
517     }
518
519         ////////////////////////////////////////////////////////////////
520           // Wait for output gobblers to finish forwarding the output
521           while (outputGobbler.isAlive() || errorGobbler.isAlive()) {
522           }
523
524         ////////////////////////////////////////////////////////////////
525           // All done, flush the streams and return the exit value
526           stdoutWriter.flush();
527           stderrWriter.flush();
528     stdoutWriter.close();
529     stderrWriter.close();
530     proc.getOutputStream().close();
531           return exitVal;
532
533   }
534
535     /**
536      * Returns the error string if exec(String) was invoked.
537      *
538      * @return The error string if exec(String) was invoked.
539      */
540     public String getErrString() {
541         return err;
542     }
543
544     /**
545      * Returns whether the maximum runtime was exceeded or not.
546      *
547      * @return boolean indicating whether the maximum runtime was exceeded or not.
548      */
549     public boolean getMaxRunTimeExceeded() {
550         return maxRunTimeExceeded;
551     }
552
553     /**
554      * Returns the maximum run time in seconds for this object.
555      *
556      * @return the maximum run time in seconds for this object.
557      */
558     public int getMaxRunTimeSecs() {
559         return maxRunTimeSecs;
560     }
561
562     /**
563      * Returns the output string if exec(String) was invoked.
564      *
565      * @return The output string if exec(String) was invoked.
566      */
567     public String getOutString() {
568         return out;
569     }
570
571     /**
572      * This is for unit testing of the class.
573      *
574      * @param args an array of command-line arguments
575      * @throws IOException thrown if a problem occurs
576      **/
577     public static void main(String[] args) throws IOException {
578
579         try {
580
581             ExecRunner er = new ExecRunner();
582
583             /////////////////////////////////////////////////////////////////////
584             // Linux: Test the exec operation with just STDOUT and STDERR
585             //System.out.println("Testing ExecRunner with STDOUT and STDERR...");
586             //er.exec("ls -l", System.out, System.err);
587             //System.out.println("Complete");
588
589             /////////////////////////////////////////////////////////////////////
590             // Windows: Test the exec operation with just STDOUT and STDERR
591             System.out.println("Testing ExecRunner with StringWriter...");
592
593             er = new ExecRunner();
594             er.setMaxRunTimeSecs(1);
595             er.exec("dir /s c:\\");
596             //er.exec("ls -l");
597
598             System.out.println("<STDOUT>\n" + er.getOutString() + "</STDOUT>");
599             System.out.println("<STDERR>\n" + er.getErrString() + "</STDERR>");
600             System.out.println("Testing Done");
601
602             /////////////////////////////////////////////////////////////////////
603             // Exit nicely
604             System.exit(0);
605
606         }
607         catch (Exception e) {
608
609             e.printStackTrace();
610             System.exit(1);
611
612         }
613     }
614
615     /**
616      * We override the <code>readObject</code> method here to prevent
617      * deserialization of our class for security reasons.
618      *
619      * @param in java.io.ObjectInputStream
620      * @throws IOException thrown if a problem occurs
621      **/
622     private final void readObject(ObjectInputStream in) throws IOException {
623
624         throw new IOException("Object cannot be deserialized");
625
626     }
627
628     /**
629      * Sets the maximum run time in seconds.
630      * If you do not want to limit the executable's run time, simply pass in
631      * a 0 (which is also the default).
632      *
633      * @param max Maximim number of seconds to let program run
634      */
635     public void setMaxRunTimeSecs(int max) {
636
637         maxRunTimeSecs = max;
638
639     }
640
641     /**
642      * We override the <code>writeObject</code> method here to prevent
643      * serialization of our class for security reasons.
644      *
645      * @param out java.io.ObjectOutputStream
646      * @throws IOException thrown if a problem occurs
647      **/
648     private final void writeObject(ObjectOutputStream out) throws IOException {
649
650         throw new IOException("Object cannot be serialized");
651
652     }
653
654 }
655
656  /**
657   * <P>Captures the output of an InputStream.</P>
658   *
659   * With acknowledgements to Michael C. Daconta, author of "Java Pitfalls,
660   * Time Saving Solutions, and Workarounds to Improve Programs." and his
661   * article in JavaWorld "When Runtime.exec() Won't".
662   *
663   * See the ExecRunner class for a reference implementation.
664   *
665   * @author <a href="mailto:smccrory@users.sourceforge.net">Scott McCrory</a>.
666   * @version CVS $Id$
667   */
668  class StreamGobbler extends Thread {
669
670      /** The input stream we're gobbling **/
671      private InputStream in = null;
672
673      /** The printwriter we'll send the gobbled characters to if asked**/
674      private PrintWriter pwOut = null;
675
676      /** Our flag to allow us to safely terminate the monitoring thread **/
677      private boolean quit = false;
678
679
680      /**
681       * A simpler constructor for StreamGobbler - defaults to stdout.
682       *
683       * @param in InputStream
684       */
685      public StreamGobbler(InputStream in) {
686
687          this.in = in;
688          this.pwOut = new PrintWriter(System.out, true);
689
690      }
691
692      /**
693       * A more explicit constructor for StreamGobbler where you can tell
694       * it exactly where to relay the output to.
695       * Creation date: (9/23/2001 8:48:01 PM)
696       *
697       * @param in InputStream
698       * @param out OutputStream
699       */
700      public StreamGobbler(InputStream in, OutputStream out) {
701
702          this.in = in;
703          this.pwOut = new PrintWriter(out, true);
704
705      }
706
707      /**
708       * A more explicit constructor for StreamGobbler where you can tell
709       * it exactly where to relay the output to.
710       * Creation date: (9/23/2001 8:48:01 PM)
711       *
712       * @param in InputStream
713       * @param pwOut PrintWriter
714       */
715      public StreamGobbler(InputStream in, PrintWriter pwOut) {
716
717          this.in = in;
718          this.pwOut = pwOut;
719
720      }
721
722     /**
723      * We override the <code>clone</code> method here to prevent cloning of our class.
724      *
725      * @throws CloneNotSupportedException To indicate cloning is not allowed
726      * @return Nothing ever really returned since we throw a CloneNotSupportedException
727      **/
728     public final Object clone() throws CloneNotSupportedException {
729
730         throw new CloneNotSupportedException();
731
732     }
733
734     /**
735      * Tells the StreamGobbler to quit it's operation.
736      * This is safer than using stop() since it uses a semophore checked in the
737      * main wait loop instead of possibly forcing semaphores to untimely unlock.
738      */
739     public void quit() {
740
741         quit = true;
742
743     }
744
745     /**
746      * We override the <code>readObject</code> method here to prevent
747      * deserialization of our class for security reasons.
748      *
749      * @param in java.io.ObjectInputStream
750      * @throws IOException thrown if a problem occurs
751      **/
752     private final void readObject(ObjectInputStream in) throws IOException {
753
754         throw new IOException("Object cannot be deserialized");
755
756     }
757
758     /**
759      * Gobbles up all the stuff coming from the InputStream and
760      * sends it to the OutputStream specified during object construction.
761      **/
762     public void run() {
763
764         try {
765             // Set up the input stream
766             BufferedReader br = new BufferedReader(new InputStreamReader(in));
767
768             // Initialize the temporary results containers
769             String line = null;
770
771             // Main processing loop which captures the output
772             while ((line = br.readLine()) != null) {
773                     if (quit) 
774                       break;
775                   else 
776                       pwOut.println(line);
777             }
778       this.in.close();
779         }
780         catch (Exception e) {
781             e.printStackTrace();
782         }
783
784     }
785
786     /**
787      * We override the <code>writeObject</code> method here to prevent
788      * serialization of our class for security reasons.
789      *
790      * @param out java.io.ObjectOutputStream
791      * @throws IOException thrown if a problem occurs
792      **/
793     private final void writeObject(ObjectOutputStream out) throws IOException {
794
795         throw new IOException("Object cannot be serialized");
796
797     }
798
799 }