KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > CLIHandler


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans;
21
22 import java.io.DataInputStream JavaDoc;
23 import java.io.DataOutputStream JavaDoc;
24 import java.io.EOFException JavaDoc;
25 import java.io.File JavaDoc;
26 import java.io.FileInputStream JavaDoc;
27 import java.io.FileOutputStream JavaDoc;
28 import java.io.IOException JavaDoc;
29 import java.io.InputStream JavaDoc;
30 import java.io.InterruptedIOException JavaDoc;
31 import java.io.OutputStream JavaDoc;
32 import java.io.PrintStream JavaDoc;
33 import java.io.PrintWriter JavaDoc;
34 import java.lang.reflect.InvocationTargetException JavaDoc;
35 import java.lang.reflect.Method JavaDoc;
36 import java.net.InetAddress JavaDoc;
37 import java.net.ServerSocket JavaDoc;
38 import java.net.Socket JavaDoc;
39 import java.net.SocketException JavaDoc;
40 import java.net.UnknownHostException JavaDoc;
41 import java.security.NoSuchAlgorithmException JavaDoc;
42 import java.security.SecureRandom JavaDoc;
43 import java.util.ArrayList JavaDoc;
44 import java.util.Arrays JavaDoc;
45 import java.util.Collection JavaDoc;
46 import java.util.Collections JavaDoc;
47 import java.util.Iterator JavaDoc;
48 import java.util.List JavaDoc;
49 import java.util.Random JavaDoc;
50 import java.util.logging.Level JavaDoc;
51 import java.util.logging.Logger JavaDoc;
52 import org.openide.util.RequestProcessor;
53 import org.openide.util.Task;
54
55 /**
56  * Command Line Interface and User Directory Locker support class.
57  * Subclasses may be registered into the system to handle special command-line options.
58  * To be registered, use <samp>META-INF/services/org.netbeans.CLIHandler</code>
59  * in a JAR file in the startup or dynamic class path (e.g. <samp>lib/ext/</samp>
60  * or <samp>lib/</samp>).
61  * @author Jaroslav Tulach
62  * @since org.netbeans.core/1 1.18
63  * @see "#32054"
64  * @see <a HREF="http://openide.netbeans.org/proposals/arch/cli.html">Specification</a>
65  */

66 public abstract class CLIHandler extends Object JavaDoc {
67     /** lenght of the key used for connecting */
68     private static final int KEY_LENGTH = 10;
69     /** ok reply */
70     private static final int REPLY_OK = 1;
71     /** sends exit code */
72     private static final int REPLY_EXIT = 2;
73     /** fail reply */
74     private static final int REPLY_FAIL = 0;
75     /** the server is active, but cannot compute the value now */
76     private static final int REPLY_DELAY = 3;
77     
78     /** request to read from input stream */
79     private static final int REPLY_READ = 10;
80     /** request to write */
81     private static final int REPLY_WRITE = 11;
82     /** request to find out how much data is available */
83     private static final int REPLY_AVAILABLE = 12;
84     /** request to write to stderr */
85     private static final int REPLY_ERROR = 13;
86     
87     /**
88      * Used during bootstrap sequence. Should only be used by core, not modules.
89      */

90     public static final int WHEN_BOOT = 1;
91     /**
92      * Used during later initialization or while NetBeans is up and running.
93      */

94     public static final int WHEN_INIT = 2;
95      /** Extra set of inits.
96      */

97     public static final int WHEN_EXTRA = 3;
98     
99     /** reference to our server.
100      */

101     private static Server server;
102     
103     /** Testing output of the threads.
104      */

105     private static Logger JavaDoc OUTPUT = Logger.getLogger("org.netbeans.CLIHandler"); // NOI18N
106

107     private int when;
108     
109     /**
110      * Create a CLI handler and indicate its preferred timing.
111      * @param when when to run the handler: {@link #WHEN_BOOT} or {@link #WHEN_INIT}
112      */

113     protected CLIHandler(int when) {
114         this.when = when;
115     }
116     
117     /**
118      * Process some set of command-line arguments.
119      * Unrecognized or null arguments should be ignored.
120      * Recognized arguments should be nulled out.
121      * @param args arguments
122      * @return error value or 0 if everything is all right
123      */

124     protected abstract int cli(Args args);
125     
126     protected static void showHelp(PrintWriter JavaDoc w, Collection JavaDoc handlers, int when) {
127         Iterator JavaDoc it = handlers.iterator();
128         while (it.hasNext()) {
129             CLIHandler h = (CLIHandler)it.next();
130             if (when != -1 && when != h.when) {
131                 continue;
132             }
133             
134             h.usage(w);
135         }
136     }
137
138     /**
139      * Print usage information for this handler.
140      * @param w a writer to print to
141      */

142     protected abstract void usage(PrintWriter JavaDoc w);
143     
144     /** For testing purposes we can block the
145      * algorithm in any place in the initialize method.
146      */

147     private static void enterState(int state, Integer JavaDoc block) {
148         if (OUTPUT.isLoggable(Level.FINEST)) {
149             synchronized (OUTPUT) {
150                 // for easier debugging of CLIHandlerTest
151
OUTPUT.finest("state: " + state + " thread: " + Thread.currentThread()); // NOI18N
152
}
153         }
154         
155         if (block == null) return;
156
157         
158         synchronized (block) {
159             if (state == block.intValue()) {
160                 if (OUTPUT.isLoggable(Level.FINEST)) {
161                     OUTPUT.finest(state + " blocked"); // NOI18N
162
}
163                 block.notifyAll();
164                 try {
165                     block.wait();
166                 } catch (InterruptedException JavaDoc ex) {
167                     throw new IllegalStateException JavaDoc();
168                 }
169             } else {
170                 if (OUTPUT.isLoggable(Level.FINEST)) {
171                     OUTPUT.finest(state + " not blocked"); // NOI18N
172
}
173             }
174         }
175     }
176     
177     private static boolean checkHelp(Args args, Collection JavaDoc handlers) {
178         String JavaDoc[] argv = args.getArguments();
179         for (int i = 0; i < argv.length; i++) {
180             if (argv[i] == null) {
181                 continue;
182             }
183
184             if (argv[i].equals("-?") || argv[i].equals("--help") || argv[i].equals ("-help")) { // NOI18N
185
PrintWriter JavaDoc w = new PrintWriter JavaDoc(args.getOutputStream());
186                 showHelp(w, handlers, -1);
187                 w.flush();
188                 return true;
189             }
190         }
191         
192         return false;
193     }
194     
195     /** Notification of available handlers.
196      * @return non-zero if one of the handlers fails
197      */

198     protected static int notifyHandlers(Args args, Collection JavaDoc handlers, int when, boolean failOnUnknownOptions, boolean consume) {
199         try {
200             int r = 0;
201             Iterator JavaDoc it = handlers.iterator();
202             while (it.hasNext()) {
203                 CLIHandler h = (CLIHandler)it.next();
204                 if (h.when != when) continue;
205
206                 r = h.cli(args);
207                 //System.err.println("notifyHandlers: exit code " + r + " from " + h);
208
if (r != 0) {
209                     return r;
210                 }
211             }
212             String JavaDoc[] argv = args.getArguments();
213             if (failOnUnknownOptions) {
214                 argv = args.getArguments();
215                 for (int i = 0; i < argv.length; i++) {
216                     if (argv[i] != null) {
217                         // Unhandled option.
218
PrintWriter JavaDoc w = new PrintWriter JavaDoc(args.getOutputStream());
219                         w.println("Ignored unknown option: " + argv[i]); // NOI18N
220

221                         // XXX(-ttran) not good, this doesn't show the help for
222
// switches handled by the launcher
223
//
224
//showHelp(w, handlers);
225

226                         w.flush();
227                         return 2;
228                     }
229                 }
230             }
231             return 0;
232         } finally {
233             args.reset(consume);
234         }
235     }
236     
237     /**
238      * Represents result of initialization.
239      * @see #initialize(String[], ClassLoader)
240      * @see #initialize(Args, Integer, List)
241      */

242     static final class Status {
243         public static final int CANNOT_CONNECT = -255;
244         
245         private final File JavaDoc lockFile;
246         private final int port;
247         private int exitCode;
248         private Task parael;
249         /**
250          * General failure.
251          */

252         Status() {
253             this(0);
254         }
255         /**
256          * Failure due to a parse problem.
257          * @param c bad status code (not 0)
258          * @see #cli(Args)
259          */

260         Status(int c) {
261             this(null, 0, c, null);
262         }
263         /**
264          * Some measure of success.
265          * @param l the lock file (not null)
266          * @param p the server port (not 0)
267          * @param c a status code (0 or not)
268          */

269         Status(File JavaDoc l, int p, int c, Task parael) {
270             lockFile = l;
271             port = p;
272             exitCode = c;
273             this.parael = parael;
274         }
275         
276         private void waitFinished() {
277             if (parael != null) {
278                 parael.waitFinished();
279             }
280         }
281         
282         /**
283          * Get the lock file, if available.
284          * @return the lock file, or null if there is none
285          */

286         public File JavaDoc getLockFile() {
287             waitFinished();
288             return lockFile;
289         }
290         /**
291          * Get the server port, if available.
292          * @return a port number for the server, or 0 if there is no port open
293          */

294         public int getServerPort() {
295             return port;
296         }
297         /**
298          * Get the CLI parse status.
299          * @return 0 for success, some other value for error conditions
300          */

301         public int getExitCode() {
302             return exitCode;
303         }
304     }
305     
306     
307     /** Initializes the system by creating lock file.
308      *
309      * @param args the command line arguments to recognize
310      * @param classloader to find command CLIHandlers in
311      * @param failOnUnknownOptions if true, fail (status 2) if some options are not recognized (also checks for -? and -help)
312      * @param cleanLockFile removes lock file if it appears to be dead
313      * @return the file to be used as lock file or null parsing of args failed
314      */

315     static Status initialize(
316         String JavaDoc[] args,
317         InputStream JavaDoc is,
318         OutputStream JavaDoc os,
319         java.io.OutputStream JavaDoc err,
320         MainImpl.BootClassLoader loader,
321         boolean failOnUnknownOptions,
322         boolean cleanLockFile,
323         Runnable JavaDoc runWhenHome
324     ) {
325         return initialize(
326             new Args(args, is, os, err, System.getProperty ("user.dir")),
327             (Integer JavaDoc)null,
328             loader.allCLIs(),
329             failOnUnknownOptions,
330             cleanLockFile,
331             runWhenHome
332         );
333     }
334     
335     /**
336      * What to do later when {@link #finishInitialization} is called.
337      * May remain null, otherwise contains list of Execute
338      */

339     private static List JavaDoc<Execute> doLater = new ArrayList JavaDoc<Execute> ();
340     static interface Execute {
341         /** @return returns exit code */
342         public int exec ();
343     }
344     
345     /** Execute this runnable when finishInitialization method is called.
346      */

347     private static int registerFinishInstallation (Execute run) {
348         boolean runNow;
349     
350         synchronized (CLIHandler.class) {
351             if (doLater != null) {
352                 doLater.add (run);
353                 runNow = false;
354             } else {
355                 runNow = true;
356             }
357         }
358         
359         if (runNow) {
360             return run.exec ();
361         }
362         
363         return 0;
364     }
365
366     /**
367      * Run any {@link #WHEN_INIT} handlers that were passed to the original command line.
368      * Should be called when the system is up and ready.
369      * Cancels any existing actions, in case it is called twice.
370      * @return the result of executing the handlers
371      */

372     static int finishInitialization (boolean recreate) {
373         OUTPUT.log(Level.FINER, "finishInitialization {0}", recreate);
374         List JavaDoc toRun;
375         synchronized (CLIHandler.class) {
376             toRun = doLater;
377             doLater = recreate ? new ArrayList JavaDoc<Execute> () : null;
378             if (OUTPUT.isLoggable(Level.FINER)) {
379                 OUTPUT.finer("Notify: " + toRun);
380             }
381             if (!recreate) {
382                 CLIHandler.class.notifyAll ();
383             }
384         }
385         
386         if (toRun != null) {
387             Iterator JavaDoc it = toRun.iterator ();
388             while (it.hasNext ()) {
389                 Execute r = (Execute)it.next ();
390                 int result = r.exec ();
391                 if (result != 0) {
392                     return result;
393                 }
394             }
395         }
396         return 0;
397     }
398     
399     /** Blocks for a while and waits if the finishInitialization method
400      * was called.
401      * @param timeout ms to wait
402      * @return true if finishInitialization is over
403      */

404     private static synchronized boolean waitFinishInstallationIsOver (int timeout) {
405         if (doLater != null) {
406             try {
407                 CLIHandler.class.wait (timeout);
408             } catch (InterruptedException JavaDoc ex) {
409                 // go on, never mind
410
}
411         }
412         return doLater == null;
413     }
414     
415     /** Stops the server.
416      */

417     public static synchronized void stopServer () {
418         Server s = server;
419         if (s != null) {
420             s.stopServer ();
421         }
422     }
423     
424     /** Enhanced search for localhost address that works also behind VPN
425      */

426     private static InetAddress JavaDoc localHostAddress () throws IOException JavaDoc {
427         java.net.NetworkInterface JavaDoc net = java.net.NetworkInterface.getByName ("lo");
428         if (net == null || !net.getInetAddresses().hasMoreElements()) {
429             return InetAddress.getLocalHost();
430         }
431         else {
432             return net.getInetAddresses().nextElement();
433         }
434     }
435     
436     /** Initializes the system by creating lock file.
437      *
438      * @param args the command line arguments to recognize
439      * @param block the state we want to block in
440      * @param handlers all handlers to use
441      * @param failOnUnknownOptions if true, fail (status 2) if some options are not recognized (also checks for -? and -help)
442      * @param cleanLockFile removes lock file if it appears to be dead
443      * @param runWhenHome runnable to be executed when netbeans.user property is set
444      * @return a status summary
445      */

446     static Status initialize(
447         final Args args, final Integer JavaDoc block,
448         final Collection JavaDoc handlers,
449         final boolean failOnUnknownOptions,
450         boolean cleanLockFile,
451         Runnable JavaDoc runWhenHome
452     ) {
453         // initial parsing of args
454
{
455             int r = notifyHandlers(args, handlers, WHEN_BOOT, false, failOnUnknownOptions);
456             if (r != 0) {
457                 return new Status(r);
458             }
459         }
460         
461         // get the value
462
String JavaDoc home = System.getProperty("netbeans.user"); // NOI18N
463
if (home == null) {
464             home = System.getProperty("user.home"); // NOI18N
465
System.setProperty ("netbeans.user", home); // NOI18N
466
}
467     
468         if ("memory".equals(home)) { // NOI18N
469
return new Status(0);
470         }
471         
472         if (runWhenHome != null) {
473             // notify that we have successfully identified the home property
474
runWhenHome.run ();
475         }
476         
477         
478         File JavaDoc lockFile = new File JavaDoc(home, "lock"); // NOI18N
479

480         for (int i = 0; i < 5; i++) {
481             // try few times to succeed
482
try {
483                 if (lockFile.exists()) {
484                     enterState(5, block);
485                     throw new IOException JavaDoc("EXISTS"); // NOI18N
486
}
487                 
488                 if (i == 0 && checkHelp(args, handlers)) {
489                     return new Status(2);
490                 }
491                 
492                 lockFile.getParentFile().mkdirs();
493                 lockFile.createNewFile();
494                 lockFile.deleteOnExit();
495                 secureAccess(lockFile);
496                 
497                 enterState(10, block);
498                 
499                 final byte[] arr = new byte[KEY_LENGTH];
500                 new Random JavaDoc().nextBytes(arr);
501                 
502                 server = new Server(arr, block, handlers, failOnUnknownOptions);
503                 
504                 final DataOutputStream JavaDoc os = new DataOutputStream JavaDoc(new FileOutputStream JavaDoc(lockFile));
505                 int p = server.getLocalPort();
506                 os.writeInt(p);
507                 os.flush();
508                 
509                 enterState(20, block);
510                 
511                 Task parael = new RequestProcessor("Secure CLI Port").post(new Runnable JavaDoc() { // NOI18N
512
public void run() {
513                         try {
514                             SecureRandom.getInstance("SHA1PRNG").nextBytes(arr); // NOI18N
515
} catch (NoSuchAlgorithmException JavaDoc e) {
516                             // #36966: IBM JDK doesn't have it.
517
try {
518                                 SecureRandom.getInstance("IBMSecureRandom").nextBytes(arr); // NOI18N
519
} catch (NoSuchAlgorithmException JavaDoc e2) {
520                                 // OK, disable server...
521
server.stopServer();
522                             }
523                         }
524                         
525                         enterState(97, block);
526
527                         try {
528                             os.write(arr);
529                             os.flush();
530
531                             enterState(27,block);
532                             // if this turns to be slow due to lookup of getLocalHost
533
// address, it can be done asynchronously as nobody needs
534
// the address in the stream if the server is listening
535
byte[] host = InetAddress.getLocalHost().getAddress();
536                             if (block != null && block.intValue() == 667) {
537                                 // this is here to emulate #64004
538
throw new java.net.UnknownHostException JavaDoc ("dhcppc0"); // NOI18N
539
}
540                             for (int all = 0; all < host.length; all++) {
541                                 os.write(host[all]);
542                             }
543                         } catch (UnknownHostException JavaDoc unknownHost) {
544                             // if we just cannot get the address, we can go on
545
unknownHost.printStackTrace();
546                         } catch (IOException JavaDoc ex) {
547                             ex.printStackTrace();
548                         }
549                         try {
550                             os.close();
551                         } catch (IOException JavaDoc ex) {
552                             // ignore
553
}
554                     }
555                 });
556                 
557                 int execCode = registerFinishInstallation (new Execute () {
558                     public int exec () {
559                         return notifyHandlers(args, handlers, WHEN_INIT, failOnUnknownOptions, failOnUnknownOptions);
560                     }
561                     
562                     public String JavaDoc toString() {
563                         return handlers.toString();
564                     }
565                 });
566                 
567                 enterState(0, block);
568                 return new Status(lockFile, server.getLocalPort(), execCode, parael);
569             } catch (IOException JavaDoc ex) {
570                 if (!"EXISTS".equals(ex.getMessage())) { // NOI18N
571
ex.printStackTrace();
572                 }
573                 // already exists, try to read
574
byte[] key = null;
575                 byte[] serverAddress = null;
576                 int port = -1;
577                 DataInputStream JavaDoc is = null;
578                 try {
579                     enterState(21, block);
580                     if (OUTPUT.isLoggable(Level.FINER)) {
581                         OUTPUT.log(Level.FINER, "Reading lock file {0}", lockFile); // NOI18N
582
}
583                     is = new DataInputStream JavaDoc(new FileInputStream JavaDoc(lockFile));
584                     port = is.readInt();
585                     enterState(22, block);
586                     key = new byte[KEY_LENGTH];
587                     is.readFully(key);
588                     enterState(23, block);
589                     byte[] x = new byte[4];
590                     is.readFully(x);
591                     enterState(24, block);
592                     serverAddress = x;
593                 } catch (EOFException JavaDoc eof) {
594                     // not yet fully written down
595
if (port != -1) {
596                         try {
597                             enterState(94, block);
598                             // just wait a while
599
Thread.sleep(2000);
600                         } catch (InterruptedException JavaDoc inter) {
601                             inter.printStackTrace();
602                         }
603                         continue;
604                     }
605                 } catch (IOException JavaDoc ex2) {
606                     // ok, try to read it once more
607
enterState(26, block);
608                 } finally {
609                     if (is != null) {
610                         try {
611                             is.close();
612                         } catch (IOException JavaDoc ex3) {
613                             // ignore here
614
}
615                     }
616                     enterState(25, block);
617                 }
618                 
619                 if (key != null && port != -1) {
620                     try {
621                         // ok, try to connect
622
enterState(28, block);
623                         Socket JavaDoc socket = new Socket JavaDoc(localHostAddress (), port);
624                         // wait max of 1s for reply
625
socket.setSoTimeout(5000);
626                         DataOutputStream JavaDoc os = new DataOutputStream JavaDoc(socket.getOutputStream());
627                         os.write(key);
628                         os.flush();
629                         
630                         enterState(30, block);
631                         
632                         DataInputStream JavaDoc replyStream = new DataInputStream JavaDoc(socket.getInputStream());
633                         byte[] outputArr = new byte[4096];
634                         
635                         COMMUNICATION: for (;;) {
636                             enterState(32, block);
637                             int reply = replyStream.read();
638                             //System.err.println("reply=" + reply);
639
enterState(34, block);
640                             
641                             switch (reply) {
642                                 case REPLY_FAIL:
643                                     enterState(36, block);
644                                     break COMMUNICATION;
645                                 case REPLY_OK:
646                                     enterState(38, block);
647                                     // write the arguments
648
String JavaDoc[] arr = args.getArguments();
649                                     os.writeInt(arr.length);
650                                     for (int a = 0; a < arr.length; a++) {
651                                         os.writeUTF(arr[a]);
652                                     }
653                                     os.writeUTF (args.getCurrentDirectory().toString());
654                                     os.flush();
655                                     break;
656                                 case REPLY_EXIT:
657                                     int exitCode = replyStream.readInt();
658                                     if (exitCode == 0) {
659                                         // to signal end of the world
660
exitCode = -1;
661                                     }
662                                     
663                                     os.close();
664                                     replyStream.close();
665                                     
666                                     enterState(0, block);
667                                     return new Status(lockFile, port, exitCode, null);
668                                 case REPLY_READ: {
669                                     enterState(42, block);
670                                     int howMuch = replyStream.readInt();
671                                     if (howMuch > outputArr.length) {
672                                         outputArr = new byte[howMuch];
673                                     }
674                                     int really = args.getInputStream().read(outputArr, 0, howMuch);
675                                     os.write(really);
676                                     if (really > 0) {
677                                         os.write(outputArr, 0, really);
678                                     }
679                                     os.flush();
680                                     break;
681                                 }
682                                 case REPLY_WRITE: {
683                                     enterState(44, block);
684                                     int howMuch = replyStream.readInt();
685                                     if (howMuch > outputArr.length) {
686                                         outputArr = new byte[howMuch];
687                                     }
688                                     replyStream.read(outputArr, 0, howMuch);
689                                     args.getOutputStream().write(outputArr, 0, howMuch);
690                                     break;
691                                 }
692                                 case REPLY_ERROR: {
693                                     enterState(45, block);
694                                     int howMuch = replyStream.readInt();
695                                     if (howMuch > outputArr.length) {
696                                         outputArr = new byte[howMuch];
697                                     }
698                                     replyStream.read(outputArr, 0, howMuch);
699                                     args.getErrorStream().write(outputArr, 0, howMuch);
700                                     break;
701                                 }
702                                 case REPLY_AVAILABLE:
703                                     enterState(46, block);
704                                     os.writeInt(args.getInputStream().available());
705                                     os.flush();
706                                     break;
707                                 case REPLY_DELAY:
708                                     enterState(47, block);
709                                     // ok, try once more
710
break;
711                                 case -1:
712                                     enterState(48, block);
713                                     // EOF. Why does this happen?
714
break;
715                                 default:
716                                     enterState(49, block);
717                                     assert false : reply;
718                             }
719                         }
720                         
721                         // connection ok, butlockFile secret key not recognized
722
// delete the lock file
723
} catch (java.net.SocketTimeoutException JavaDoc ex2) {
724                         // connection failed, the port is dead
725
enterState(33, block);
726                     } catch (java.net.ConnectException JavaDoc ex2) {
727                         // connection failed, the port is dead
728
enterState(33, block);
729                     } catch (IOException JavaDoc ex2) {
730                         // some strange exception
731
ex2.printStackTrace();
732                         enterState(33, block);
733                     }
734                     
735                     boolean isSameHost = true;
736                     if (serverAddress != null) {
737                         try {
738                             isSameHost = Arrays.equals(InetAddress.getLocalHost().getAddress(), serverAddress);
739                         } catch (UnknownHostException JavaDoc ex5) {
740                             // ok, we will not try to connect
741
enterState(999, block);
742                         }
743                     }
744                     
745                     if (cleanLockFile || isSameHost) {
746                         // remove the file and try once more
747
lockFile.delete();
748                     } else {
749                         return new Status (Status.CANNOT_CONNECT);
750                     }
751                 }
752             }
753             
754             try {
755                 enterState(83, block);
756                 Thread.sleep((int)(Math.random() * 1000.00));
757                 enterState(85, block);
758             } catch (InterruptedException JavaDoc ex) {
759                 // means nothing
760
}
761         }
762         
763         // failure
764
return new Status();
765     }
766
767     /** Make the file readable just to its owner.
768      */

769     private static void secureAccess(final File JavaDoc file) throws IOException JavaDoc {
770         // using 1.6 method if available to make the file readable just
771
// to its owner
772
boolean success = false;
773         
774         String JavaDoc vm = System.getProperty("java.version"); // NOI18N
775
if (vm != null && vm.startsWith("1.6")) { // NOI18N
776
try {
777                 Method JavaDoc m = File JavaDoc.class.getMethod("setReadable", new Class JavaDoc[] { Boolean.TYPE, Boolean.TYPE }); // NOI18N
778
Object JavaDoc s1 = m.invoke(file, new Object JavaDoc[] {Boolean.FALSE, Boolean.FALSE});
779                 Object JavaDoc s2 = m.invoke(file, new Object JavaDoc[] {Boolean.TRUE, Boolean.TRUE});
780                 success = Boolean.TRUE.equals(s1) && Boolean.TRUE.equals(s2);
781             } catch (InvocationTargetException JavaDoc ex) {
782                 ex.printStackTrace();
783             } catch (IllegalAccessException JavaDoc ex) {
784                 ex.printStackTrace();
785             } catch (NoSuchMethodException JavaDoc ex) {
786                 ex.printStackTrace();
787             }
788         }
789         if (success) {
790             return;
791         }
792         try {
793             // try to make it only user-readable (on Unix)
794
// since people are likely to leave a+r on their userdir
795
File JavaDoc chmod = new File JavaDoc("/bin/chmod"); // NOI18N
796
if (!chmod.isFile()) {
797                 // Linux uses /bin, Solaris /usr/bin, others hopefully one of those
798
chmod = new File JavaDoc("/usr/bin/chmod"); // NOI18N
799
}
800             if (chmod.isFile()) {
801                 int chmoded = Runtime.getRuntime().exec(new String JavaDoc[] {
802                     chmod.getAbsolutePath(),
803                     "go-rwx", // NOI18N
804
file.getAbsolutePath()
805                 }).waitFor();
806                 if (chmoded != 0) {
807                     throw new IOException JavaDoc("could not run " + chmod + " go-rwx " + file); // NOI18N
808
}
809             }
810         } catch (InterruptedException JavaDoc e) {
811             throw (IOException JavaDoc)new IOException JavaDoc(e.toString()).initCause(e);
812         }
813     }
814
815     /** Class that represents available arguments to the CLI
816      * handlers.
817      */

818     public static final class Args extends Object JavaDoc {
819         private String JavaDoc[] args;
820         private final String JavaDoc[] argsBackup;
821         private InputStream JavaDoc is;
822         private OutputStream JavaDoc os;
823         private OutputStream JavaDoc err;
824         private File JavaDoc currentDir;
825         private boolean closed;
826         
827         Args(String JavaDoc[] args, InputStream JavaDoc is, OutputStream JavaDoc os, java.io.OutputStream JavaDoc err, String JavaDoc currentDir) {
828             argsBackup = args;
829             reset(false);
830             this.is = is;
831             this.os = os;
832             this.err = err;
833             this.currentDir = new File JavaDoc (currentDir);
834         }
835         
836         /**
837          * Restore the arguments list to a clean state.
838          * If not consuming arguments, it is just set to the original list.
839          * If consuming arguments, any nulled-out arguments are removed from the list.
840          */

841         void reset(boolean consume) {
842             if (consume) {
843                 String JavaDoc[] a = args;
844                 if (a == null) {
845                     a = argsBackup;
846                 }
847                 List JavaDoc<String JavaDoc> l = new ArrayList JavaDoc<String JavaDoc>(Arrays.asList(a));
848                 l.removeAll(Collections.singleton(null));
849                 args = l.toArray(new String JavaDoc[l.size()]);
850             } else {
851                 args = argsBackup.clone();
852             }
853         }
854         
855         /** Closes the connection.
856          */

857         final void close() {
858             closed = true;
859         }
860         
861         /**
862          * Get the command-line arguments.
863          * You may not modify the returned array except to set some elements
864          * to null as you recognize them.
865          * @return array of string arguments, may contain nulls
866          */

867         public String JavaDoc[] getArguments() {
868             return args;
869         }
870         
871         /**
872          * Get an output stream to which data may be sent.
873          * @return stream to write to
874          */

875         public OutputStream JavaDoc getOutputStream() {
876             return os;
877         }
878         
879         /** Access to error stream.
880          * @return the stream to write error messages to
881          */

882         public OutputStream JavaDoc getErrorStream() {
883             return err;
884         }
885         
886         public File JavaDoc getCurrentDirectory () {
887             return currentDir;
888         }
889         
890         /**
891          * Get an input stream that may supply additional data.
892          * @return stream to read from
893          */

894         public InputStream JavaDoc getInputStream() {
895             return is;
896         }
897         
898         /** Is open? True if the connection is still alive. Can be
899          * used with long running computations to find out if the
900          * consumer of the output has not been interupted.
901          *
902          * @return true if the connection is still alive
903          */

904         public boolean isOpen() {
905             return !closed;
906         }
907         
908     } // end of Args
909

910     /** Server that creates local socket and communicates with it.
911      */

912     private static final class Server extends Thread JavaDoc {
913         private byte[] key;
914         private ServerSocket JavaDoc socket;
915         private Integer JavaDoc block;
916         private Collection JavaDoc handlers;
917         private Socket JavaDoc work;
918         private static volatile int counter;
919         private final boolean failOnUnknownOptions;
920
921         private static long lastReply;
922         /** by default wait 100ms before sending a REPLY_FAIL message */
923         private static long failDelay = 100;
924         
925         public Server(byte[] key, Integer JavaDoc block, Collection JavaDoc handlers, boolean failOnUnknownOptions) throws IOException JavaDoc {
926             super("CLI Requests Server"); // NOI18N
927
this.key = key;
928             this.setDaemon(true);
929             this.block = block;
930             this.handlers = handlers;
931             this.failOnUnknownOptions = failOnUnknownOptions;
932             
933             socket = new ServerSocket JavaDoc(0, 50, localHostAddress());
934             start();
935         }
936         
937         public Server(Socket JavaDoc request, byte[] key, Integer JavaDoc block, Collection JavaDoc handlers, boolean failOnUnknownOptions) throws IOException JavaDoc {
938             super("CLI Handler Thread Handler: " + ++counter); // NOI18N
939
this.key = key;
940             this.setDaemon(true);
941             this.block = block;
942             this.handlers = handlers;
943             this.work = request;
944             this.failOnUnknownOptions = failOnUnknownOptions;
945             
946             start();
947         }
948         
949         public int getLocalPort() {
950             return socket.getLocalPort();
951         }
952         
953         public void run() {
954             if (work != null) {
955                 // I am a worker not listener server
956
try {
957                     handleConnect(work);
958                 } catch (IOException JavaDoc ex) {
959                     ex.printStackTrace();
960                 }
961                 return;
962             }
963             
964             ServerSocket JavaDoc toClose = socket;
965             if (toClose == null) {
966                 return;
967             }
968             
969             while (socket != null) {
970                 try {
971                     enterState(65, block);
972                     Socket JavaDoc s = socket.accept();
973                     if (socket == null) {
974                         enterState(66, block);
975                         s.getOutputStream().write(REPLY_FAIL);
976                         enterState(67, block);
977                         s.close();
978                         continue;
979                     }
980                     
981                     // spans new request handler
982
new Server(s, key, block, handlers, failOnUnknownOptions);
983                 } catch (InterruptedIOException JavaDoc ex) {
984                     if (socket != null) {
985                         ex.printStackTrace();
986                     }
987                     // otherwise ignore, we've just been asked by the stopServer
988
// to stop
989
} catch (java.net.SocketException JavaDoc ex) {
990                     if (socket != null) {
991                         ex.printStackTrace();
992                     }
993                 } catch (IOException JavaDoc ex) {
994                     ex.printStackTrace();
995                 }
996             }
997             
998             try {
999                 toClose.close();
1000            } catch (IOException JavaDoc ex) {
1001                ex.printStackTrace();
1002            }
1003        }
1004
1005        final void stopServer () {
1006            socket = null;
1007            // interrupts the listening server
1008
interrupt();
1009        }
1010        
1011        private void handleConnect(Socket JavaDoc s) throws IOException JavaDoc {
1012            
1013            byte[] check = new byte[key.length];
1014            DataInputStream JavaDoc is = new DataInputStream JavaDoc(s.getInputStream());
1015            
1016            enterState(70, block);
1017            
1018            is.readFully(check);
1019            
1020            enterState(90, block);
1021            
1022            final DataOutputStream JavaDoc os = new DataOutputStream JavaDoc(s.getOutputStream());
1023            
1024            if (Arrays.equals(check, key)) {
1025                while (!waitFinishInstallationIsOver (2000)) {
1026                    os.write (REPLY_DELAY);
1027                    os.flush ();
1028                }
1029                
1030                enterState(93, block);
1031                os.write(REPLY_OK);
1032                os.flush();
1033                
1034                // continue with arguments
1035
int numberOfArguments = is.readInt();
1036                String JavaDoc[] args = new String JavaDoc[numberOfArguments];
1037                for (int i = 0; i < args.length; i++) {
1038                    args[i] = is.readUTF();
1039                }
1040                final String JavaDoc currentDir = is.readUTF ();
1041                
1042                final Args arguments = new Args(
1043                    args,
1044                    new IS(is, os),
1045                    new OS(os, REPLY_WRITE),
1046                    new OS(os, REPLY_ERROR),
1047                    currentDir
1048                );
1049
1050                class ComputingAndNotifying extends Thread JavaDoc {
1051                    public int res;
1052                    public boolean finished;
1053                    
1054                    public ComputingAndNotifying () {
1055                        super ("Computes values in handlers");
1056                    }
1057                    
1058                    public void run () {
1059                        try {
1060                            if (checkHelp(arguments, handlers)) {
1061                                res = 2;
1062                            } else {
1063                                res = notifyHandlers (arguments, handlers, WHEN_INIT, failOnUnknownOptions, false);
1064                            }
1065
1066                            if (res == 0) {
1067                                enterState (98, block);
1068                            } else {
1069                                enterState (99, block);
1070                            }
1071                        } finally {
1072                            synchronized (this) {
1073                                finished = true;
1074                                notifyAll ();
1075                            }
1076                        }
1077                    }
1078                    
1079                    public synchronized void waitForResultAndNotifyOthers () {
1080                        // execute the handlers in another thread
1081
start ();
1082                        while (!finished) {
1083                            try {
1084                                wait (1000);
1085                                os.write (REPLY_DELAY);
1086                                os.flush ();
1087                            } catch (SocketException JavaDoc ex) {
1088                                if (isClosedSocket(ex)) { // NOI18N
1089
// mark the arguments killed
1090
arguments.close();
1091                                    // interrupt this thread
1092
interrupt();
1093                                } else {
1094                                    ex.printStackTrace();
1095                                }
1096                            } catch (InterruptedException JavaDoc ex) {
1097                                ex.printStackTrace();
1098                            } catch (IOException JavaDoc ex) {
1099                                ex.printStackTrace();
1100                            }
1101                        }
1102                    }
1103                }
1104                ComputingAndNotifying r = new ComputingAndNotifying ();
1105                r.waitForResultAndNotifyOthers ();
1106                try {
1107                    os.write(REPLY_EXIT);
1108                    os.writeInt(r.res);
1109                } catch (SocketException JavaDoc ex) {
1110                    if (isClosedSocket(ex)) { // NOI18N
1111
// mark the arguments killed
1112
arguments.close();
1113                        // interrupt r thread
1114
r.interrupt();
1115                    } else {
1116                        throw ex;
1117                    }
1118                }
1119            } else {
1120                enterState(103, block);
1121                long toWait = lastReply + failDelay - System.currentTimeMillis();
1122                if (toWait > 0) {
1123                    try {
1124                        Thread.sleep(toWait);
1125                    } catch (InterruptedException JavaDoc ex) {
1126                        ex.printStackTrace();
1127                    }
1128                    failDelay *= 2;
1129                } else {
1130                    failDelay = 100;
1131                }
1132                lastReply = System.currentTimeMillis();
1133                os.write(REPLY_FAIL);
1134            }
1135            
1136            
1137            enterState(120, block);
1138            
1139            os.close();
1140            is.close();
1141        }
1142        
1143        /** A method to find out on various systems whether an exception is
1144         * a signal of closed socket, especially if the peer is killed or exited.
1145         * @param ex the exception to investigate
1146         */

1147        static final boolean isClosedSocket(SocketException JavaDoc ex) {
1148            if (ex.getMessage().equals("Broken pipe")) { // NOI18N
1149
return true;
1150            }
1151            if (ex.getMessage().startsWith("Connection reset by peer")) { // NOI18N
1152
return true;
1153            }
1154            
1155            return false;
1156        }
1157        
1158        private static final class IS extends InputStream JavaDoc {
1159            private DataInputStream JavaDoc is;
1160            private DataOutputStream JavaDoc os;
1161            
1162            public IS(DataInputStream JavaDoc is, DataOutputStream JavaDoc os) {
1163                this.is = is;
1164                this.os = os;
1165            }
1166            
1167            public int read() throws IOException JavaDoc {
1168                byte[] arr = new byte[1];
1169                if (read(arr) == 1) {
1170                    return arr[0];
1171                } else {
1172                    return -1;
1173                }
1174            }
1175            
1176            public void close() throws IOException JavaDoc {
1177                super.close();
1178            }
1179            
1180            public int available() throws IOException JavaDoc {
1181                // ask for data
1182
os.write(REPLY_AVAILABLE);
1183                os.flush();
1184                // read provided data
1185
return is.readInt();
1186            }
1187            
1188            public int read(byte[] b) throws IOException JavaDoc {
1189                return read(b, 0, b.length);
1190            }
1191            
1192            public int read(byte[] b, int off, int len) throws IOException JavaDoc {
1193                // ask for data
1194
os.write(REPLY_READ);
1195                os.writeInt(len);
1196                os.flush();
1197                // read provided data
1198
int really = is.read ();
1199                if (really > 0) {
1200                    return is.read(b, off, really);
1201                } else {
1202                    return really;
1203                }
1204            }
1205            
1206        } // end of IS
1207

1208        private static final class OS extends OutputStream JavaDoc {
1209            private DataOutputStream JavaDoc os;
1210            private int type;
1211            
1212            public OS(DataOutputStream JavaDoc os, int type) {
1213                this.os = os;
1214                this.type = type;
1215            }
1216            
1217            public void write(int b) throws IOException JavaDoc {
1218                byte[] arr = { (byte)b };
1219                write(arr);
1220            }
1221            
1222            public void write(byte[] b) throws IOException JavaDoc {
1223                write(b, 0, b.length);
1224            }
1225            
1226            public void close() throws IOException JavaDoc {
1227                super.close();
1228            }
1229            
1230            public void flush() throws IOException JavaDoc {
1231                os.flush();
1232            }
1233            
1234            public void write(byte[] b, int off, int len) throws IOException JavaDoc {
1235                os.write(type);
1236                os.writeInt(len);
1237                os.write(b, off, len);
1238            }
1239            
1240        } // end of OS
1241

1242    } // end of Server
1243

1244    
1245}
1246
Popular Tags