PageRenderTime 55ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/projects/netbeans-7.3/o.n.bootstrap/src/org/netbeans/CLIHandler.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 1280 lines | 889 code | 122 blank | 269 comment | 164 complexity | c78196bc3cffb03f9e8f63bd6532f52b MD5 | raw file
  1. /*
  2. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  3. *
  4. * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
  5. *
  6. * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
  7. * Other names may be trademarks of their respective owners.
  8. *
  9. * The contents of this file are subject to the terms of either the GNU
  10. * General Public License Version 2 only ("GPL") or the Common
  11. * Development and Distribution License("CDDL") (collectively, the
  12. * "License"). You may not use this file except in compliance with the
  13. * License. You can obtain a copy of the License at
  14. * http://www.netbeans.org/cddl-gplv2.html
  15. * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
  16. * specific language governing permissions and limitations under the
  17. * License. When distributing the software, include this License Header
  18. * Notice in each file and include the License file at
  19. * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
  20. * particular file as subject to the "Classpath" exception as provided
  21. * by Oracle in the GPL Version 2 section of the License file that
  22. * accompanied this code. If applicable, add the following below the
  23. * License Header, with the fields enclosed by brackets [] replaced by
  24. * your own identifying information:
  25. * "Portions Copyrighted [year] [name of copyright owner]"
  26. *
  27. * Contributor(s):
  28. *
  29. * The Original Software is NetBeans. The Initial Developer of the Original
  30. * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
  31. * Microsystems, Inc. All Rights Reserved.
  32. *
  33. * If you wish your version of this file to be governed by only the CDDL
  34. * or only the GPL Version 2, indicate your decision by adding
  35. * "[Contributor] elects to include this software in this distribution
  36. * under the [CDDL or GPL Version 2] license." If you do not indicate a
  37. * single choice of license, a recipient has the option to distribute
  38. * your version of this file under either the CDDL, the GPL Version 2 or
  39. * to extend the choice of license to its licensees as provided above.
  40. * However, if you add GPL Version 2 code and therefore, elected the GPL
  41. * Version 2 license, then the option applies only if the new code is
  42. * made subject to such option by the copyright holder.
  43. */
  44. package org.netbeans;
  45. import java.io.Closeable;
  46. import java.io.DataInput;
  47. import java.io.DataInputStream;
  48. import java.io.DataOutputStream;
  49. import java.io.EOFException;
  50. import java.io.File;
  51. import java.io.IOException;
  52. import java.io.InputStream;
  53. import java.io.InterruptedIOException;
  54. import java.io.OutputStream;
  55. import java.io.PrintWriter;
  56. import java.io.RandomAccessFile;
  57. import java.net.InetAddress;
  58. import java.net.ServerSocket;
  59. import java.net.Socket;
  60. import java.net.SocketException;
  61. import java.net.UnknownHostException;
  62. import java.nio.channels.FileLock;
  63. import java.nio.channels.OverlappingFileLockException;
  64. import java.security.NoSuchAlgorithmException;
  65. import java.security.SecureRandom;
  66. import java.util.ArrayList;
  67. import java.util.Arrays;
  68. import java.util.Collection;
  69. import java.util.Collections;
  70. import java.util.List;
  71. import java.util.Random;
  72. import java.util.logging.Level;
  73. import java.util.logging.Logger;
  74. import org.openide.util.RequestProcessor;
  75. import org.openide.util.Task;
  76. /**
  77. * Command Line Interface and User Directory Locker support class.
  78. * Subclasses may be registered into the system to handle special command-line options.
  79. * To be registered, use {@link org.openide.util.lookup.ServiceProvider}
  80. * in a JAR file in the startup or dynamic class path (e.g. <samp>lib/ext/</samp>
  81. * or <samp>lib/</samp>).
  82. * @author Jaroslav Tulach
  83. * @since org.netbeans.core/1 1.18
  84. * @see "#32054"
  85. * @see <a href="http://openide.netbeans.org/proposals/arch/cli.html">Specification</a>
  86. */
  87. public abstract class CLIHandler extends Object {
  88. /** lenght of the key used for connecting */
  89. private static final int KEY_LENGTH = 10;
  90. private static final byte[] VERSION = {
  91. 'N', 'B', 'C', 'L', 'I', 0, 0, 0, 0, 1
  92. };
  93. /** ok reply */
  94. private static final int REPLY_OK = 1;
  95. /** sends exit code */
  96. private static final int REPLY_EXIT = 2;
  97. /** fail reply */
  98. private static final int REPLY_FAIL = 0;
  99. /** the server is active, but cannot compute the value now */
  100. private static final int REPLY_DELAY = 3;
  101. /** request to read from input stream */
  102. private static final int REPLY_READ = 10;
  103. /** request to write */
  104. private static final int REPLY_WRITE = 11;
  105. /** request to find out how much data is available */
  106. private static final int REPLY_AVAILABLE = 12;
  107. /** request to write to stderr */
  108. private static final int REPLY_ERROR = 13;
  109. /** returns version of the protocol */
  110. private static final int REPLY_VERSION = 14;
  111. /**
  112. * Used during bootstrap sequence. Should only be used by core, not modules.
  113. */
  114. public static final int WHEN_BOOT = 1;
  115. /**
  116. * Used during later initialization or while NetBeans is up and running.
  117. */
  118. public static final int WHEN_INIT = 2;
  119. /** Extra set of inits.
  120. */
  121. public static final int WHEN_EXTRA = 3;
  122. private static final RequestProcessor secureCLIPort = new RequestProcessor("Secure CLI Port");
  123. /** reference to our server.
  124. */
  125. private static Server server;
  126. /** Testing output of the threads.
  127. */
  128. private static final Logger OUTPUT = Logger.getLogger(CLIHandler.class.getName());
  129. private int when;
  130. /**
  131. * Create a CLI handler and indicate its preferred timing.
  132. * @param when when to run the handler: {@link #WHEN_BOOT} or {@link #WHEN_INIT}
  133. */
  134. protected CLIHandler(int when) {
  135. this.when = when;
  136. }
  137. /**
  138. * Process some set of command-line arguments.
  139. * Unrecognized or null arguments should be ignored.
  140. * Recognized arguments should be nulled out.
  141. * @param args arguments
  142. * @return error value or 0 if everything is all right
  143. */
  144. protected abstract int cli(Args args);
  145. protected static void showHelp(PrintWriter w, Collection<? extends CLIHandler> handlers, int when) {
  146. for (CLIHandler h : handlers) {
  147. if (when != -1 && when != h.when) {
  148. continue;
  149. }
  150. h.usage(w);
  151. }
  152. }
  153. /**
  154. * Print usage information for this handler.
  155. * @param w a writer to print to
  156. */
  157. protected abstract void usage(PrintWriter w);
  158. /** For testing purposes we can block the
  159. * algorithm in any place in the initialize method.
  160. */
  161. private static void enterState(int state, Integer block) {
  162. if (OUTPUT.isLoggable(Level.FINEST)) {
  163. synchronized (OUTPUT) {
  164. // for easier debugging of CLIHandlerTest
  165. OUTPUT.finest("state: " + state + " thread: " + Thread.currentThread()); // NOI18N
  166. }
  167. }
  168. if (block == null) return;
  169. synchronized (block) {
  170. if (state == block.intValue()) {
  171. if (OUTPUT.isLoggable(Level.FINEST)) {
  172. OUTPUT.finest(state + " blocked"); // NOI18N
  173. }
  174. block.notifyAll();
  175. try {
  176. block.wait();
  177. } catch (InterruptedException ex) {
  178. throw new IllegalStateException();
  179. }
  180. } else {
  181. if (OUTPUT.isLoggable(Level.FINEST)) {
  182. OUTPUT.finest(state + " not blocked"); // NOI18N
  183. }
  184. }
  185. }
  186. }
  187. private static boolean checkHelp(Args args, Collection<? extends CLIHandler> handlers) {
  188. String[] argv = args.getArguments();
  189. for (int i = 0; i < argv.length; i++) {
  190. if (argv[i] == null) {
  191. continue;
  192. }
  193. if (argv[i].equals("-?") || argv[i].equals("--help") || argv[i].equals ("-help")) { // NOI18N
  194. // disable all logging from standard logger (which prints to stdout) to prevent help mesage disruption
  195. Logger.getLogger("").setLevel(Level.OFF); // NOI18N
  196. PrintWriter w = new PrintWriter(args.getOutputStream());
  197. showHelp(w, handlers, -1);
  198. w.flush();
  199. return true;
  200. }
  201. }
  202. return false;
  203. }
  204. /** Notification of available handlers.
  205. * @return non-zero if one of the handlers fails
  206. */
  207. protected static int notifyHandlers(Args args, Collection<? extends CLIHandler> handlers, int when, boolean failOnUnknownOptions, boolean consume) {
  208. try {
  209. int r = 0;
  210. for (CLIHandler h : handlers) {
  211. if (h.when != when) continue;
  212. r = h.cli(args);
  213. //System.err.println("notifyHandlers: exit code " + r + " from " + h);
  214. if (r != 0) {
  215. return r;
  216. }
  217. }
  218. String[] argv = args.getArguments();
  219. if (failOnUnknownOptions) {
  220. argv = args.getArguments();
  221. for (int i = 0; i < argv.length; i++) {
  222. if (argv[i] != null) {
  223. // Unhandled option.
  224. PrintWriter w = new PrintWriter(args.getOutputStream());
  225. w.println("Ignored unknown option: " + argv[i]); // NOI18N
  226. // XXX(-ttran) not good, this doesn't show the help for
  227. // switches handled by the launcher
  228. //
  229. //showHelp(w, handlers);
  230. w.flush();
  231. return 2;
  232. }
  233. }
  234. }
  235. return 0;
  236. } finally {
  237. args.reset(consume);
  238. }
  239. }
  240. private static int processInitLevelCLI(final Args args, final Collection<? extends CLIHandler> handlers, final boolean failOnUnknownOptions) {
  241. return registerFinishInstallation(new Execute() {
  242. @Override
  243. public int exec() {
  244. return notifyHandlers(args, handlers, WHEN_INIT, failOnUnknownOptions, failOnUnknownOptions);
  245. }
  246. public @Override
  247. String toString() {
  248. return handlers.toString();
  249. }
  250. });
  251. }
  252. /**
  253. * Represents result of initialization.
  254. * @see #initialize(String[], ClassLoader)
  255. * @see #initialize(Args, Integer, List)
  256. */
  257. static final class Status {
  258. public static final int CANNOT_CONNECT = -255;
  259. public static final int CANNOT_WRITE = -254;
  260. private final File lockFile;
  261. private final int port;
  262. private int exitCode;
  263. private Task parael;
  264. /**
  265. * General failure.
  266. */
  267. Status() {
  268. this(0);
  269. }
  270. /**
  271. * Failure due to a parse problem.
  272. * @param c bad status code (not 0)
  273. * @see #cli(Args)
  274. */
  275. Status(int c) {
  276. this(null, 0, c, null);
  277. }
  278. /**
  279. * Some measure of success.
  280. * @param l the lock file (not null)
  281. * @param p the server port (not 0)
  282. * @param c a status code (0 or not)
  283. */
  284. Status(File l, int p, int c, Task parael) {
  285. lockFile = l;
  286. port = p;
  287. exitCode = c;
  288. this.parael = parael;
  289. }
  290. private void waitFinished() {
  291. if (parael != null) {
  292. parael.waitFinished();
  293. }
  294. }
  295. /**
  296. * Get the lock file, if available.
  297. * @return the lock file, or null if there is none
  298. */
  299. public File getLockFile() {
  300. waitFinished();
  301. return lockFile;
  302. }
  303. /**
  304. * Get the server port, if available.
  305. * @return a port number for the server, or 0 if there is no port open
  306. */
  307. public int getServerPort() {
  308. return port;
  309. }
  310. /**
  311. * Get the CLI parse status.
  312. * @return 0 for success, some other value for error conditions
  313. */
  314. public int getExitCode() {
  315. return exitCode;
  316. }
  317. }
  318. private static FileLock tryLock(RandomAccessFile raf) throws IOException {
  319. try {
  320. return raf.getChannel().tryLock(333L, 1L, false);
  321. } catch (OverlappingFileLockException ex) {
  322. OUTPUT.log(Level.INFO, "tryLock fails in the same VM", ex);
  323. // happens in CLIHandlerTest as it simulates running multiple
  324. // instances of the application in the same VM
  325. return null;
  326. }
  327. }
  328. /** Initializes the system by creating lock file.
  329. *
  330. * @param args the command line arguments to recognize
  331. * @param classloader to find command CLIHandlers in
  332. * @param failOnUnknownOptions if true, fail (status 2) if some options are not recognized (also checks for -? and -help)
  333. * @param cleanLockFile removes lock file if it appears to be dead
  334. * @return the file to be used as lock file or null parsing of args failed
  335. */
  336. static Status initialize(
  337. String[] args,
  338. InputStream is,
  339. OutputStream os,
  340. java.io.OutputStream err,
  341. MainImpl.BootClassLoader loader,
  342. boolean failOnUnknownOptions,
  343. boolean cleanLockFile,
  344. Runnable runWhenHome
  345. ) {
  346. String userDir = System.getProperty("netbeans.user.dir");
  347. //System.err.println("nud: " + userDir);
  348. if (userDir == null) {
  349. userDir = System.getProperty ("user.dir");
  350. }
  351. //System.err.println(" ud: " + userDir);
  352. return initialize(
  353. new Args(args, is, os, err, userDir),
  354. (Integer)null,
  355. loader.allCLIs(),
  356. failOnUnknownOptions,
  357. cleanLockFile,
  358. runWhenHome
  359. );
  360. }
  361. /**
  362. * What to do later when {@link #finishInitialization} is called.
  363. * May remain null, otherwise contains list of Execute
  364. */
  365. private static List<Execute> doLater = new ArrayList<Execute> ();
  366. static interface Execute {
  367. /** @return returns exit code */
  368. public int exec ();
  369. }
  370. /** Execute this runnable when finishInitialization method is called.
  371. */
  372. private static int registerFinishInstallation (Execute run) {
  373. boolean runNow;
  374. synchronized (CLIHandler.class) {
  375. if (doLater != null) {
  376. doLater.add (run);
  377. runNow = false;
  378. } else {
  379. runNow = true;
  380. }
  381. }
  382. if (runNow) {
  383. return run.exec ();
  384. }
  385. return 0;
  386. }
  387. /**
  388. * Run any {@link #WHEN_INIT} handlers that were passed to the original command line.
  389. * Should be called when the system is up and ready.
  390. * Cancels any existing actions, in case it is called twice.
  391. * @return the result of executing the handlers
  392. */
  393. static int finishInitialization (boolean recreate) {
  394. OUTPUT.log(Level.FINER, "finishInitialization {0}", recreate);
  395. List<Execute> toRun;
  396. synchronized (CLIHandler.class) {
  397. toRun = doLater;
  398. doLater = recreate ? new ArrayList<Execute> () : null;
  399. if (OUTPUT.isLoggable(Level.FINER)) {
  400. OUTPUT.finer("Notify: " + toRun);
  401. }
  402. if (!recreate) {
  403. CLIHandler.class.notifyAll ();
  404. }
  405. }
  406. if (toRun != null) {
  407. for (Execute r : toRun) {
  408. int result = r.exec ();
  409. if (result != 0) {
  410. return result;
  411. }
  412. }
  413. }
  414. return 0;
  415. }
  416. /** Blocks for a while and waits if the finishInitialization method
  417. * was called.
  418. * @param timeout ms to wait
  419. * @return true if finishInitialization is over
  420. */
  421. private static synchronized boolean waitFinishInstallationIsOver (int timeout) {
  422. if (doLater != null) {
  423. try {
  424. CLIHandler.class.wait (timeout);
  425. } catch (InterruptedException ex) {
  426. // go on, never mind
  427. }
  428. }
  429. return doLater == null;
  430. }
  431. static void waitSecureCLIOver() {
  432. secureCLIPort.post(Task.EMPTY).waitFinished();
  433. }
  434. /** Stops the server.
  435. */
  436. public static synchronized void stopServer () {
  437. Server s = server;
  438. if (s != null) {
  439. s.stopServer ();
  440. }
  441. }
  442. /** Enhanced search for localhost address that works also behind VPN
  443. */
  444. private static InetAddress localHostAddress () throws IOException {
  445. java.net.NetworkInterface net = java.net.NetworkInterface.getByName ("lo");
  446. if (net == null || !net.getInetAddresses().hasMoreElements()) {
  447. net = java.net.NetworkInterface.getByInetAddress(InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 }));
  448. }
  449. if (net == null || !net.getInetAddresses().hasMoreElements()) {
  450. return InetAddress.getLocalHost();
  451. }
  452. else {
  453. return net.getInetAddresses().nextElement();
  454. }
  455. }
  456. /** Initializes the system by creating lock file.
  457. *
  458. * @param args the command line arguments to recognize
  459. * @param block the state we want to block in
  460. * @param handlers all handlers to use
  461. * @param failOnUnknownOptions if true, fail (status 2) if some options are not recognized (also checks for -? and -help)
  462. * @param cleanLockFile removes lock file if it appears to be dead
  463. * @param runWhenHome runnable to be executed when netbeans.user property is set
  464. * @return a status summary
  465. */
  466. static Status initialize(
  467. final Args args, final Integer block,
  468. final Collection<? extends CLIHandler> handlers,
  469. final boolean failOnUnknownOptions,
  470. boolean cleanLockFile,
  471. Runnable runWhenHome
  472. ) {
  473. // initial parsing of args
  474. {
  475. int r = notifyHandlers(args, handlers, WHEN_BOOT, false, failOnUnknownOptions);
  476. if (r != 0) {
  477. return new Status(r);
  478. }
  479. }
  480. // get the value
  481. String home = System.getProperty("netbeans.user"); // NOI18N
  482. if (home == null) {
  483. home = System.getProperty("user.home"); // NOI18N
  484. System.setProperty("netbeans.user", home); // NOI18N
  485. }
  486. if (/*Places.MEMORY*/"memory".equals(home)) { // NOI18N
  487. int execCode = processInitLevelCLI(args, handlers, failOnUnknownOptions);
  488. return new Status(execCode);
  489. }
  490. File lockFile = new File(home, "lock"); // NOI18N
  491. for (int i = 0; i < 5; i++) {
  492. // try few times to succeed
  493. RandomAccessFile raf = null;
  494. FileLock lock = null;
  495. try {
  496. NO_LOCK: if (lockFile.isFile()) {
  497. if (!cleanLockFile && !lockFile.canWrite()) {
  498. return new Status(Status.CANNOT_WRITE);
  499. }
  500. raf = new RandomAccessFile(lockFile, "rw");
  501. enterState(2, block);
  502. lock = tryLock(raf);
  503. if (OUTPUT.isLoggable(Level.FINER)) {
  504. OUTPUT.log(Level.FINER, "tryLock on {0} result: {1}", new Object[] { lockFile, lock });
  505. }
  506. if (lock != null && lock.isValid()) {
  507. // not locked by anyone,
  508. enterState(4, block);
  509. break NO_LOCK;
  510. }
  511. enterState(5, block);
  512. if (runWhenHome != null) {
  513. // notify that we have successfully identified the home property
  514. runWhenHome.run();
  515. runWhenHome = null;
  516. }
  517. throw new IOException("EXISTS"); // NOI18N
  518. }
  519. if (i == 0 && checkHelp(args, handlers)) {
  520. if (runWhenHome != null) {
  521. // notify that we have successfully identified the home property
  522. runWhenHome.run();
  523. runWhenHome = null;
  524. }
  525. return new Status(2);
  526. }
  527. if (lock == null) {
  528. lockFile.getParentFile().mkdirs();
  529. try {
  530. raf = new RandomAccessFile(lockFile, "rw");
  531. lock = tryLock(raf);
  532. OUTPUT.log(Level.FINER, "tryLock when null on {0} result: {1}", new Object[] { lockFile, lock });
  533. if (!cleanLockFile && !lockFile.canWrite()) {
  534. return new Status(Status.CANNOT_WRITE);
  535. }
  536. } catch (IOException ex) {
  537. if (!cleanLockFile && !lockFile.getParentFile().canWrite()) {
  538. return new Status(Status.CANNOT_WRITE);
  539. }
  540. throw ex;
  541. }
  542. }
  543. lockFile.deleteOnExit();
  544. assert lock != null : "Null lock on " + lockFile;
  545. if (runWhenHome != null) {
  546. // notify that we have successfully identified the home property
  547. runWhenHome.run ();
  548. runWhenHome = null;
  549. }
  550. secureAccess(lockFile);
  551. enterState(10, block);
  552. final byte[] arr = new byte[KEY_LENGTH];
  553. new Random().nextBytes(arr);
  554. final RandomAccessFile os = raf;
  555. raf.seek(0L);
  556. server = new Server(new FileAndLock(os, lock), arr, block, handlers, failOnUnknownOptions);
  557. int p = server.getLocalPort();
  558. os.writeInt(p);
  559. os.getChannel().force(true);
  560. enterState(20, block);
  561. Task parael = secureCLIPort.post(new Runnable() { // NOI18N
  562. @Override
  563. public void run() {
  564. SecureRandom random = null;
  565. enterState(95, block);
  566. try {
  567. random = SecureRandom.getInstance("SHA1PRNG"); // NOI18N
  568. } catch (NoSuchAlgorithmException e) {
  569. // #36966: IBM JDK doesn't have it.
  570. try {
  571. random = SecureRandom.getInstance("IBMSecureRandom"); // NOI18N
  572. } catch (NoSuchAlgorithmException e2) {
  573. // OK, disable server...
  574. server.stopServer();
  575. }
  576. }
  577. enterState(96, block);
  578. if (random != null) {
  579. random.nextBytes(arr);
  580. }
  581. enterState(97, block);
  582. try {
  583. os.write(arr);
  584. os.getChannel().force(true);
  585. enterState(27,block);
  586. // if this turns to be slow due to lookup of getLocalHost
  587. // address, it can be done asynchronously as nobody needs
  588. // the address in the stream if the server is listening
  589. byte[] host;
  590. try {
  591. if (block != null && block.intValue() == 667) {
  592. // this is here to emulate #64004
  593. throw new UnknownHostException("dhcppc0"); // NOI18N
  594. }
  595. host = InetAddress.getLocalHost().getAddress();
  596. } catch (UnknownHostException unknownHost) {
  597. if (!"dhcppc0".equals(unknownHost.getMessage())) { // NOI18N, see above
  598. // if we just cannot get the address, we can go on
  599. unknownHost.printStackTrace();
  600. }
  601. host = new byte[] {127, 0, 0, 1};
  602. }
  603. os.write(host);
  604. } catch (IOException ex) {
  605. ex.printStackTrace();
  606. }
  607. try {
  608. os.getChannel().force(true);
  609. } catch (IOException ex) {
  610. // ignore
  611. }
  612. }
  613. });
  614. int execCode = processInitLevelCLI (args, handlers, failOnUnknownOptions);
  615. enterState(0, block);
  616. return new Status(lockFile, server.getLocalPort(), execCode, parael);
  617. } catch (IOException ex) {
  618. if (!"EXISTS".equals(ex.getMessage())) { // NOI18N
  619. ex.printStackTrace();
  620. }
  621. // already exists, try to read
  622. byte[] key = null;
  623. byte[] serverAddress = null;
  624. int port = -1;
  625. DataInput is = null;
  626. try {
  627. enterState(21, block);
  628. if (OUTPUT.isLoggable(Level.FINER)) {
  629. OUTPUT.log(Level.FINER, "Reading lock file {0}", lockFile); // NOI18N
  630. }
  631. is = raf;
  632. port = is.readInt();
  633. enterState(22, block);
  634. key = new byte[KEY_LENGTH];
  635. is.readFully(key);
  636. enterState(23, block);
  637. byte[] x = new byte[4];
  638. is.readFully(x);
  639. enterState(24, block);
  640. serverAddress = x;
  641. } catch (EOFException eof) {
  642. // not yet fully written down
  643. if (port != -1) {
  644. try {
  645. enterState(94, block);
  646. try {
  647. Socket socket = new Socket(localHostAddress (), port);
  648. socket.close();
  649. } catch (Exception ex3) {
  650. // socket is not open, remove the file and try once more
  651. lockFile.delete();
  652. continue;
  653. }
  654. // just wait a while
  655. Thread.sleep(2000);
  656. } catch (InterruptedException inter) {
  657. inter.printStackTrace();
  658. }
  659. continue;
  660. }
  661. } catch (IOException ex2) {
  662. // ok, try to read it once more
  663. enterState(26, block);
  664. } finally {
  665. if (is instanceof Closeable) {
  666. try {
  667. ((Closeable)is).close();
  668. } catch (IOException ex3) {
  669. // ignore here
  670. }
  671. }
  672. enterState(25, block);
  673. }
  674. if (key != null && port != -1) {
  675. int version = -1;
  676. RESTART: for (;;) try {
  677. // ok, try to connect
  678. enterState(28, block);
  679. Socket socket = new Socket(localHostAddress (), port);
  680. // wait max of 1s for reply
  681. socket.setSoTimeout(5000);
  682. DataOutputStream os = new DataOutputStream(socket.getOutputStream());
  683. if (version == -1) {
  684. os.write(VERSION);
  685. } else {
  686. os.write(key);
  687. }
  688. assert VERSION.length == key.length;
  689. os.flush();
  690. enterState(30, block);
  691. DataInputStream replyStream = new DataInputStream(socket.getInputStream());
  692. byte[] outputArr = new byte[4096];
  693. COMMUNICATION: for (;;) {
  694. enterState(32, block);
  695. int reply = replyStream.read();
  696. //System.err.println("reply=" + reply);
  697. enterState(34, block);
  698. switch (reply) {
  699. case REPLY_VERSION:
  700. version = replyStream.readInt();
  701. os.write(key);
  702. os.flush();
  703. break;
  704. case REPLY_FAIL:
  705. if (version == -1) {
  706. os.close();
  707. replyStream.close();
  708. socket.close();
  709. version = 0;
  710. continue RESTART;
  711. }
  712. enterState(36, block);
  713. break COMMUNICATION;
  714. case REPLY_OK:
  715. enterState(38, block);
  716. // write the arguments
  717. String[] arr = args.getArguments();
  718. os.writeInt(arr.length);
  719. for (int a = 0; a < arr.length; a++) {
  720. os.writeUTF(arr[a]);
  721. }
  722. os.writeUTF (args.getCurrentDirectory().toString());
  723. os.flush();
  724. break;
  725. case REPLY_EXIT:
  726. int exitCode = replyStream.readInt();
  727. if (exitCode == 0) {
  728. // to signal end of the world
  729. exitCode = -1;
  730. }
  731. os.close();
  732. replyStream.close();
  733. enterState(0, block);
  734. return new Status(lockFile, port, exitCode, null);
  735. case REPLY_READ: {
  736. enterState(42, block);
  737. int howMuch = replyStream.readInt();
  738. if (howMuch > outputArr.length) {
  739. outputArr = new byte[howMuch];
  740. }
  741. int really = args.getInputStream().read(outputArr, 0, howMuch);
  742. if (version >= 1) {
  743. os.writeInt(really);
  744. } else {
  745. os.write(really);
  746. }
  747. if (really > 0) {
  748. os.write(outputArr, 0, really);
  749. }
  750. os.flush();
  751. break;
  752. }
  753. case REPLY_WRITE: {
  754. enterState(44, block);
  755. int howMuch = replyStream.readInt();
  756. if (howMuch > outputArr.length) {
  757. outputArr = new byte[howMuch];
  758. }
  759. replyStream.read(outputArr, 0, howMuch);
  760. args.getOutputStream().write(outputArr, 0, howMuch);
  761. break;
  762. }
  763. case REPLY_ERROR: {
  764. enterState(45, block);
  765. int howMuch = replyStream.readInt();
  766. if (howMuch > outputArr.length) {
  767. outputArr = new byte[howMuch];
  768. }
  769. replyStream.read(outputArr, 0, howMuch);
  770. args.getErrorStream().write(outputArr, 0, howMuch);
  771. break;
  772. }
  773. case REPLY_AVAILABLE:
  774. enterState(46, block);
  775. os.writeInt(args.getInputStream().available());
  776. os.flush();
  777. break;
  778. case REPLY_DELAY:
  779. enterState(47, block);
  780. // ok, try once more
  781. break;
  782. case -1:
  783. enterState(48, block);
  784. // EOF. Why does this happen?
  785. break COMMUNICATION;
  786. default:
  787. enterState(49, block);
  788. assert false : reply;
  789. }
  790. }
  791. // connection ok, butlockFile secret key not recognized
  792. // delete the lock file
  793. break RESTART;
  794. } catch (java.net.SocketTimeoutException ex2) {
  795. // connection failed, the port is dead
  796. enterState(33, block);
  797. break RESTART;
  798. } catch (java.net.ConnectException ex2) {
  799. // connection failed, the port is dead
  800. enterState(33, block);
  801. break RESTART;
  802. } catch (IOException ex2) {
  803. // some strange exception
  804. ex2.printStackTrace();
  805. enterState(33, block);
  806. break RESTART;
  807. }
  808. boolean isSameHost = true;
  809. if (serverAddress != null) {
  810. try {
  811. isSameHost = Arrays.equals(InetAddress.getLocalHost().getAddress(), serverAddress);
  812. } catch (UnknownHostException ex5) {
  813. // ok, we will not try to connect
  814. enterState(999, block);
  815. }
  816. }
  817. if (lock == null) {
  818. // process exists (we cannot lock the file), but does not respond
  819. return new Status (Status.CANNOT_CONNECT);
  820. }
  821. if (cleanLockFile || isSameHost) {
  822. // remove the file and try once more
  823. lockFile.delete();
  824. } else {
  825. return new Status (Status.CANNOT_CONNECT);
  826. }
  827. }
  828. }
  829. try {
  830. enterState(83, block);
  831. Thread.sleep((int)(Math.random() * 1000.00));
  832. enterState(85, block);
  833. } catch (InterruptedException ex) {
  834. // means nothing
  835. }
  836. }
  837. // failure
  838. return new Status();
  839. }
  840. /** Make the file readable just to its owner.
  841. */
  842. private static void secureAccess(final File file) throws IOException {
  843. file.setReadable(false, false);
  844. file.setReadable(true, true);
  845. }
  846. /** Class that represents available arguments to the CLI
  847. * handlers.
  848. */
  849. public static final class Args extends Object {
  850. private String[] args;
  851. private final String[] argsBackup;
  852. private InputStream is;
  853. private OutputStream os;
  854. private OutputStream err;
  855. private File currentDir;
  856. private boolean closed;
  857. Args(String[] args, InputStream is, OutputStream os, java.io.OutputStream err, String currentDir) {
  858. argsBackup = args;
  859. reset(false);
  860. this.is = is;
  861. this.os = os;
  862. this.err = err;
  863. this.currentDir = new File (currentDir);
  864. }
  865. /**
  866. * Restore the arguments list to a clean state.
  867. * If not consuming arguments, it is just set to the original list.
  868. * If consuming arguments, any nulled-out arguments are removed from the list.
  869. */
  870. void reset(boolean consume) {
  871. if (consume) {
  872. String[] a = args;
  873. if (a == null) {
  874. a = argsBackup;
  875. }
  876. List<String> l = new ArrayList<String>(Arrays.asList(a));
  877. l.removeAll(Collections.singleton(null));
  878. args = l.toArray(new String[l.size()]);
  879. } else {
  880. args = argsBackup.clone();
  881. }
  882. }
  883. /** Closes the connection.
  884. */
  885. final void close() {
  886. closed = true;
  887. }
  888. /**
  889. * Get the command-line arguments.
  890. * You may not modify the returned array except to set some elements
  891. * to null as you recognize them.
  892. * @return array of string arguments, may contain nulls
  893. */
  894. public String[] getArguments() {
  895. return args;
  896. }
  897. /**
  898. * Get an output stream to which data may be sent.
  899. * @return stream to write to
  900. */
  901. public OutputStream getOutputStream() {
  902. return os;
  903. }
  904. /** Access to error stream.
  905. * @return the stream to write error messages to
  906. */
  907. public OutputStream getErrorStream() {
  908. return err;
  909. }
  910. public File getCurrentDirectory () {
  911. return currentDir;
  912. }
  913. /**
  914. * Get an input stream that may supply additional data.
  915. * @return stream to read from
  916. */
  917. public InputStream getInputStream() {
  918. return is;
  919. }
  920. /** Is open? True if the connection is still alive. Can be
  921. * used with long running computations to find out if the
  922. * consumer of the output has not been interupted.
  923. *
  924. * @return true if the connection is still alive
  925. */
  926. public boolean isOpen() {
  927. return !closed;
  928. }
  929. } // end of Args
  930. /** Server that creates local socket and communicates with it.
  931. */
  932. private static final class Server extends Thread {
  933. private Closeable unlock;
  934. private byte[] key;
  935. private ServerSocket socket;
  936. private Integer block;
  937. private Collection<? extends CLIHandler> handlers;
  938. private Socket work;
  939. private static volatile int counter;
  940. private final boolean failOnUnknownOptions;
  941. private static long lastReply;
  942. /** by default wait 100ms before sending a REPLY_FAIL message */
  943. private static long failDelay = 100;
  944. public Server(Closeable lock, byte[] key, Integer block, Collection<? extends CLIHandler> handlers, boolean failOnUnknownOptions) throws IOException {
  945. super("CLI Requests Server"); // NOI18N
  946. this.unlock = lock;
  947. this.key = key;
  948. this.setDaemon(true);
  949. this.block = block;
  950. this.handlers = handlers;
  951. this.failOnUnknownOptions = failOnUnknownOptions;
  952. socket = new ServerSocket(0, 50, localHostAddress());
  953. start();
  954. }
  955. public Server(Socket request, byte[] key, Integer block, Collection<? extends CLIHandler> handlers, boolean failOnUnknownOptions) throws IOException {
  956. super("CLI Handler Thread Handler: " + ++counter); // NOI18N
  957. this.key = key;
  958. this.setDaemon(true);
  959. this.block = block;
  960. this.handlers = handlers;
  961. this.work = request;
  962. this.failOnUnknownOptions = failOnUnknownOptions;
  963. start();
  964. }
  965. public int getLocalPort() {
  966. return socket.getLocalPort();
  967. }
  968. public @Override void run() {
  969. if (work != null) {
  970. // I am a worker not listener server
  971. try {
  972. handleConnect(work);
  973. } catch (IOException ex) {
  974. OUTPUT.log(Level.INFO, null, ex);
  975. }
  976. return;
  977. }
  978. ServerSocket toClose = socket;
  979. if (toClose == null) {
  980. return;
  981. }
  982. while (socket != null) {
  983. try {
  984. enterState(65, block);
  985. Socket s = socket.accept();
  986. if (socket == null) {
  987. enterState(66, block);
  988. s.getOutputStream().write(REPLY_FAIL);
  989. enterState(67, block);
  990. s.close();
  991. continue;
  992. }
  993. // spans new request handler
  994. new Server(s, key, block, handlers, failOnUnknownOptions);
  995. } catch (InterruptedIOException ex) {
  996. if (socket != null) {
  997. ex.printStackTrace();
  998. }
  999. // otherwise ignore, we've just been asked by the stopServer
  1000. // to stop
  1001. } catch (java.net.SocketException ex) {
  1002. if (socket != null) {
  1003. ex.printStackTrace();
  1004. }
  1005. } catch (IOException ex) {
  1006. ex.printStackTrace();
  1007. }
  1008. }
  1009. try {
  1010. toClose.close();
  1011. } catch (IOException ex) {
  1012. ex.printStackTrace();
  1013. }
  1014. }
  1015. final void stopServer () {
  1016. OUTPUT.log(Level.FINER, "stopServer, unlock: {0}", unlock);
  1017. if (unlock != null) {
  1018. try {
  1019. unlock.close();
  1020. } catch (IOException ex) {
  1021. OUTPUT.log(Level.WARNING, "Cannot unlock {0}", ex.getMessage());
  1022. }
  1023. }
  1024. socket = null;
  1025. // interrupts the listening server
  1026. interrupt();
  1027. }
  1028. private void handleConnect(Socket s) throws IOException {
  1029. int requestedVersion;
  1030. byte[] check = new byte[key.length];
  1031. DataInputStream is = new DataInputStream(s.getInputStream());
  1032. enterState(70, block);
  1033. is.readFully(check);
  1034. final DataOutputStream os = new DataOutputStream(s.getOutputStream());
  1035. boolean match = true;
  1036. for (int i = 0; i < VERSION.length - 1; i++) {
  1037. if (VERSION[i] != check[i]) {
  1038. match = false;
  1039. }
  1040. }
  1041. if (match) {
  1042. requestedVersion = check[VERSION.length - 1];
  1043. os.write(REPLY_VERSION);
  1044. os.writeInt(VERSION[VERSION.length - 1]);
  1045. os.flush();
  1046. is.readFully(check);
  1047. } else {
  1048. requestedVersion = 0;
  1049. }
  1050. enterState(90, block);
  1051. if (Arrays.equals(check, key)) {
  1052. while (!waitFinishInstallationIsOver (2000)) {
  1053. os.write (REPLY_DELAY);
  1054. os.flush ();
  1055. }
  1056. enterState(93, block);
  1057. os.write(REPLY_OK);
  1058. os.flush();
  1059. // continue with arguments
  1060. int numberOfArguments = is.readInt();
  1061. String[] args = new String[numberOfArguments];
  1062. for (int i = 0; i < args.length; i++) {
  1063. args[i] = is.readUTF();
  1064. }
  1065. final String currentDir = is.readUTF ();
  1066. final Args arguments = new Args(
  1067. args,
  1068. new IS(is, os, requestedVersion),
  1069. new OS(os, REPLY_WRITE),
  1070. new OS(os, REPLY_ERROR),
  1071. currentDir
  1072. );
  1073. class ComputingAndNotifying extends Thread {
  1074. public int res;
  1075. public boolean finished;
  1076. public ComputingAndNotifying () {
  1077. super ("Computes values in handlers");
  1078. }
  1079. public @Override void run() {
  1080. try {
  1081. if (checkHelp(arguments, handlers)) {
  1082. res = 2;
  1083. } else {
  1084. res = notifyHandlers (arguments, handlers, WHEN_INIT, failOnUnknownOptions, false);
  1085. }
  1086. if (res == 0) {
  1087. enterState (98, block);
  1088. } else {
  1089. enterState (99, block);
  1090. }
  1091. } finally {
  1092. synchronized (this) {
  1093. finished = true;
  1094. notifyAll ();
  1095. }
  1096. }
  1097. }
  1098. public synchronized void waitForResultAndNotifyOthers () {
  1099. // execute the handlers in another thread
  1100. start ();
  1101. while (!finished) {
  1102. try {
  1103. wait (1000);
  1104. os.write (REPLY_DELAY);
  1105. os.flush ();
  1106. } catch (SocketException ex) {
  1107. if (isClosedSocket(ex)) { // NOI18N
  1108. // mark the arguments killed
  1109. arguments.close();
  1110. // interrupt this thread
  1111. interrupt();
  1112. } else {
  1113. ex.printStackTrace();
  1114. }
  1115. } catch (InterruptedException ex) {
  1116. ex.printStackTrace();
  1117. } catch (IOException ex) {
  1118. ex.printStackTrace();
  1119. }
  1120. }
  1121. }
  1122. }
  1123. ComputingAndNotifying r = new ComputingAndNotifying ();
  1124. r.waitForResultAndNotifyOthers ();
  1125. try {
  1126. os.write(REPLY_EXIT);
  1127. os.writeInt(r.res);
  1128. } catch (SocketException ex) {
  1129. if (isClosedSocket(ex)) { // NOI18N
  1130. // mark the arguments killed
  1131. arguments.close();
  1132. // interrupt r thread
  1133. r.interrupt();
  1134. } else {
  1135. throw ex;
  1136. }
  1137. }
  1138. } else {
  1139. enterState(103, block);
  1140. long toWait = lastReply + failDelay - System.currentTimeMillis();
  1141. if (toWait > 0) {
  1142. try {
  1143. Thread.sleep(toWait);
  1144. } catch (InterruptedException ex) {
  1145. ex.printStackTrace();
  1146. }
  1147. failDelay *= 2;
  1148. } else {
  1149. failDelay = 100;
  1150. }
  1151. lastReply = System.currentTimeMillis();
  1152. os.write(REPLY_FAIL);
  1153. }
  1154. enterState(120, block);
  1155. os.close();
  1156. is.close();
  1157. }
  1158. /** A method to find out on va