diff options
author | Konrad Kolosowski | 2003-08-22 01:51:32 +0000 |
---|---|---|
committer | Konrad Kolosowski | 2003-08-22 01:51:32 +0000 |
commit | 7c6299446895ecab51993b88f55c1812de75125b (patch) | |
tree | 1d2dc12229d95e7e1a6cf7c23c7816f5caced856 | |
parent | f3f97db25ac2b94590de6ab4812961204f599e72 (diff) | |
download | eclipse.platform.ua-v20030821_syncStandalone.tar.gz eclipse.platform.ua-v20030821_syncStandalone.tar.xz eclipse.platform.ua-v20030821_syncStandalone.zip |
35407 Standalone help may hang if started/shutdown randomlyv20030821_syncStandalone
7 files changed, 256 insertions, 211 deletions
diff --git a/org.eclipse.help/src/org/eclipse/help/internal/HelpApplication.java b/org.eclipse.help/src/org/eclipse/help/internal/HelpApplication.java index ed3fec756..60054ea27 100644 --- a/org.eclipse.help/src/org/eclipse/help/internal/HelpApplication.java +++ b/org.eclipse.help/src/org/eclipse/help/internal/HelpApplication.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.eclipse.help.internal; import java.io.*; +import java.nio.channels.*; import java.util.*; import org.eclipse.core.boot.IPlatformRunnable; @@ -26,10 +27,13 @@ import org.eclipse.help.internal.appserver.WebappManager; */ public class HelpApplication implements IPlatformRunnable, IExecutableExtension { + private static final String APPLICATION_LOCK_FILE = ".applicationlock"; private static final int STATUS_EXITTING = 0; private static final int STATUS_RESTARTING = 2; private static final int STATUS_RUNNING = 1; private static int status = STATUS_RUNNING; + private File metadata; + private FileLock lock; /** * Causes help service to stop and exit */ @@ -48,21 +52,33 @@ public class HelpApplication * Runs help service application. */ public Object run(Object args) throws Exception { + if (status == STATUS_RESTARTING) { + return EXIT_RESTART; + } + + metadata = new File(Platform.getLocation().toFile(), ".metadata/"); if (!HelpSystem.ensureWebappRunning()) { System.out.println( "Help web application could not start. Check log file for details."); return EXIT_OK; } + + if (status == STATUS_RESTARTING) { + return EXIT_RESTART; + + } writeHostAndPort(); + obtainLock(); + // main program loop while (status == STATUS_RUNNING) { try { - Thread.sleep(250); + Thread.sleep(100); } catch (InterruptedException ie) { break; } } - + releaseLock(); if (status == STATUS_RESTARTING) { return EXIT_RESTART; } else { @@ -88,8 +104,7 @@ public class HelpApplication p.put("host", WebappManager.getHost()); p.put("port", "" + WebappManager.getPort()); - File workspace = Platform.getLocation().toFile(); - File hostPortFile = new File(workspace, ".metadata/.connection"); + File hostPortFile = new File(metadata, ".connection"); hostPortFile.deleteOnExit(); FileOutputStream out = null; try { @@ -105,4 +120,21 @@ public class HelpApplication } } + private void obtainLock() { + File lockFile = new File(metadata, APPLICATION_LOCK_FILE); + try { + RandomAccessFile raf = new RandomAccessFile(lockFile, "rw"); + lock = raf.getChannel().lock(); + } catch (IOException ioe) { + lock = null; + } + } + private void releaseLock() { + if (lock != null) { + try { + lock.channel().close(); + } catch (IOException ioe) { + } + } + } } diff --git a/org.eclipse.help/src/org/eclipse/help/internal/standalone/Eclipse.java b/org.eclipse.help/src/org/eclipse/help/internal/standalone/Eclipse.java index 474294fa9..967eccb6d 100644 --- a/org.eclipse.help/src/org/eclipse/help/internal/standalone/Eclipse.java +++ b/org.eclipse.help/src/org/eclipse/help/internal/standalone/Eclipse.java @@ -27,14 +27,16 @@ public class Eclipse extends Thread { File dir; String[] cmdarray; - private int status; - private Exception exception = new Exception("Unknown exception."); + private int status = STATUS_INIT; + private Exception exception; Process pr; + private EclipseLifeCycleListener lifeCycleListener; /** * Constructor */ - public Eclipse() { + public Eclipse(EclipseLifeCycleListener listener) { super(); + this.lifeCycleListener = listener; this.setName("Eclipse"); this.dir = Options.getEclipseHome(); } @@ -88,24 +90,18 @@ public class Eclipse extends Thread { if (Options.isDebug()) { printCommand(); } - launchProcess(); - } catch (Exception exc) { - exception = exc; - status = STATUS_ERROR; - } finally { - if (status == STATUS_INIT) { - status = STATUS_ERROR; - } - } - } - private void launchProcess() throws IOException { - try { do { pr = Runtime.getRuntime().exec(cmdarray, (String[]) null, dir); (new StreamConsumer(pr.getInputStream())).start(); (new StreamConsumer(pr.getErrorStream())).start(); - status = STATUS_STARTED; - pr.waitFor(); + if (status == STATUS_INIT) { + // started first time + status = STATUS_STARTED; + } + try { + pr.waitFor(); + } catch (InterruptedException e) { + } if (Options.isDebug()) { System.out.println( "Eclipse exited with status code " + pr.exitValue()); @@ -115,7 +111,17 @@ public class Eclipse extends Thread { } } } while (pr.exitValue() == NEEDS_RESTART); - } catch (InterruptedException e) { + } catch (Exception exc) { + exception = exc; + status = STATUS_ERROR; + } finally { + if (status == STATUS_INIT) { + status = STATUS_ERROR; + } + if (status == STATUS_ERROR) { + exception = new Exception("Unknown exception."); + } + lifeCycleListener.eclipseEnded(); } } /** @@ -202,7 +208,7 @@ public class Eclipse extends Thread { } /** - * Used in unit testing. + * Forcibly kill Eclipse process. */ public void killProcess() { if (pr != null) { diff --git a/org.eclipse.help/src/org/eclipse/help/internal/standalone/EclipseConnection.java b/org.eclipse.help/src/org/eclipse/help/internal/standalone/EclipseConnection.java index 59bf71bda..5427660a3 100644 --- a/org.eclipse.help/src/org/eclipse/help/internal/standalone/EclipseConnection.java +++ b/org.eclipse.help/src/org/eclipse/help/internal/standalone/EclipseConnection.java @@ -20,30 +20,12 @@ import java.util.Properties; * It should be launched from command line. */ public class EclipseConnection { - // timout for .hostport file to apper since starting eclipse [ms] - // 0 if no waiting for file should occur - int startupTimeout; - // number of retries to connectect to webapp - int connectionRetries; - // time between retries to connectect to webapp [ms] - int connectionRetryInterval; // help server host private String host; // help server port private String port; public EclipseConnection() { - this(0, 0, 5 * 1000); - } - - public EclipseConnection( - int startupTimeout, - int connectionRetries, - int connectionRetryInterval) { - - this.startupTimeout = startupTimeout; - this.connectionRetries = connectionRetries; - this.connectionRetryInterval = connectionRetryInterval; } public String getPort() { @@ -64,34 +46,28 @@ public class EclipseConnection { } public void connect(URL url) throws InterruptedException, Exception { - for (int i = 0; i <= connectionRetries; i++) { - try { - HttpURLConnection connection = - (HttpURLConnection) url.openConnection(); - if (Options.isDebug()) { - System.out.println( - "Connection to control servlet created."); - } - connection.connect(); - if (Options.isDebug()) { - System.out.println( - "Connection to control servlet connected."); - } - int code = connection.getResponseCode(); - if (Options.isDebug()) { - System.out.println( - "Response code from control servlet=" + code); - } - connection.disconnect(); - return; - } catch (IOException ioe) { - if (Options.isDebug()) { - ioe.printStackTrace(); - } + try { + HttpURLConnection connection = + (HttpURLConnection) url.openConnection(); + if (Options.isDebug()) { + System.out.println("Connection to control servlet created."); + } + connection.connect(); + if (Options.isDebug()) { + System.out.println("Connection to control servlet connected."); + } + int code = connection.getResponseCode(); + if (Options.isDebug()) { + System.out.println( + "Response code from control servlet=" + code); + } + connection.disconnect(); + return; + } catch (IOException ioe) { + if (Options.isDebug()) { + ioe.printStackTrace(); } - Thread.sleep(connectionRetryInterval); } - throw new Exception("Connection to Help System timed out."); } /** @@ -100,28 +76,6 @@ public class EclipseConnection { * and help might be starting up. */ public void renew() throws Exception { - long time1 = System.currentTimeMillis(); - while (!Options.getConnectionFile().exists()) { - // wait for .hostport file to appear - if (Options.isDebug()) { - System.out.println( - "File " - + Options.getConnectionFile() - + " does not exist, at the moment."); - } - // timeout - if (System.currentTimeMillis() - time1 >= startupTimeout) { - if (Options.isDebug()) { - System.out.println( - "Timeout waiting for file " - + Options.getConnectionFile()+"\nEclipse is not running."); - } - throw new Exception( - "Timeout waiting for file " + Options.getConnectionFile()+"\nEclipse is not running."); - } - // wait more - Thread.sleep(2000); - } Properties p = new Properties(); FileInputStream is = null; try { @@ -129,7 +83,7 @@ public class EclipseConnection { p.load(is); is.close(); } catch (IOException ioe) { - // it ok, eclipse might have just exited + // it is ok, eclipse might have just exited throw ioe; } finally { if (is != null) { diff --git a/org.eclipse.help/src/org/eclipse/help/internal/standalone/EclipseController.java b/org.eclipse.help/src/org/eclipse/help/internal/standalone/EclipseController.java index fa96b67e2..8c284f92e 100644 --- a/org.eclipse.help/src/org/eclipse/help/internal/standalone/EclipseController.java +++ b/org.eclipse.help/src/org/eclipse/help/internal/standalone/EclipseController.java @@ -10,14 +10,16 @@ *******************************************************************************/ package org.eclipse.help.internal.standalone; +import java.io.*; import java.net.*; +import java.nio.channels.*; /** * This program is used to start or stop Eclipse * Infocenter application. * It should be launched from command line. */ -public class EclipseController { +public class EclipseController implements EclipseLifeCycleListener { // control servlet path private static final String CONTROL_SERVLET_PATH = @@ -29,7 +31,10 @@ public class EclipseController { // Eclipse connection params protected EclipseConnection connection; - private Eclipse eclipse = null; + public Eclipse eclipse = null; + // Inter process lock + private FileLock lock; + private boolean eclipseEnded = false; /** * Constructs help system * @param applicationID ID of Eclipse help application @@ -44,36 +49,35 @@ public class EclipseController { this.applicationId = applicationId; Options.init(applicationId, args); - connection = initConnection(); - } - - /** - * Creates a connection to Eclipse. May need to override this to pass retry parameters. - */ - protected EclipseConnection initConnection() { - return new EclipseConnection(); + connection = new EclipseConnection(); } /** * @see org.eclipse.help.standalone.Help#shutdown() */ - public void shutdown() throws Exception { + public final synchronized void shutdown() throws Exception { try { - sendHelpCommand("shutdown", new String[0]); + obtainLock(); + sendHelpCommandInternal("shutdown", new String[0]); } catch (MalformedURLException mue) { mue.printStackTrace(); } catch (InterruptedException ie) { + } finally { + releaseLock(); } - - connection.reset(); } /** * @see org.eclipse.help.standalone.Help#start() */ - public void start() throws Exception { - connection.reset(); - startEclipse(); + public final synchronized void start() throws Exception { + try { + obtainLock(); + startEclipse(); + } finally { + releaseLock(); + } + } /** @@ -82,20 +86,91 @@ public class EclipseController { * If connection fails, retries several times, * in case webapp is starting up. */ - protected void sendHelpCommand(String command, String[] parameters) + protected final synchronized void sendHelpCommand( + String command, + String[] parameters) throws Exception { - if (!"shutdown".equalsIgnoreCase(command)) { - if (eclipse == null || !eclipse.isAlive()) { - startEclipse(); + try { + obtainLock(); + sendHelpCommandInternal(command, parameters); + } finally { + releaseLock(); + } + + } + + /** + * Starts Eclipse if not yet running. + */ + private void startEclipse() throws Exception { + boolean fullyRunning = isApplicationRunning(); + if (fullyRunning) { + return; + } + if (Options.isDebug()) { + System.out.println( + "Using workspace " + Options.getWorkspace().getAbsolutePath()); + } + // delete old connection file + Options.getConnectionFile().delete(); + connection.reset(); + + if (Options.isDebug()) { + System.out.println( + "Ensured old .connection file is deleted. Launching Eclipse."); + } + eclipseEnded = false; + eclipse = new Eclipse(this); + eclipse.start(); + fullyRunning = isApplicationRunning(); + while (!eclipseEnded && !fullyRunning) { + try { + Thread.sleep(250); + } catch (InterruptedException ie) { } + fullyRunning = isApplicationRunning(); + } + if (eclipseEnded) { + if (eclipse.getStatus() == Eclipse.STATUS_ERROR) { + throw eclipse.getException(); + } + return; + } + if (Options.isDebug()) { + System.out.println("Eclipse launched"); + } + // in case controller is killed + Runtime.getRuntime().addShutdownHook(new EclipseCleaner()); + } + private void sendHelpCommandInternal(String command, String[] parameters) + throws Exception { + if (!"shutdown".equalsIgnoreCase(command)) { + startEclipse(); + } + if (!isApplicationRunning()) { + return; } if (!connection.isValid()) { connection.renew(); } - try { URL url = createCommandURL(command, parameters); - connection.connect(url); + if ("shutdown".equalsIgnoreCase(command) + && Options.getConnectionFile().exists()) { + connection.connect(url); + long timeLimit = System.currentTimeMillis() + 60 * 1000; + while (Options.getConnectionFile().exists()) { + Thread.sleep(200); + if (System.currentTimeMillis() > timeLimit) { + System.out.println( + "Shutting down is taking too long. Will not wait."); + break; + } + } + + } else { + connection.connect(url); + } } catch (MalformedURLException mue) { mue.printStackTrace(); } catch (InterruptedException ie) { @@ -128,92 +203,72 @@ public class EclipseController { return new URL(urlStr.toString()); } - /** - * Starts Eclipse if not yet running. - */ - private void startEclipse() throws Exception { - if (Options.isDebug()) { - System.out.println( - "Using workspace " + Options.getWorkspace().getAbsolutePath()); - System.out.println( - "Checking if file " + Options.getLockFile() + " exists."); - } - if (isAnotherRunning()) { + public void eclipseEnded() { + connection.reset(); + } + private void obtainLock() throws IOException { + if (lock != null) { + // we already have lock return; } - // delete old connection file - Options.getConnectionFile().delete(); - + if (!Options.getLockFile().exists()) { + Options.getLockFile().getParentFile().mkdirs(); + } + RandomAccessFile raf = + new RandomAccessFile(Options.getLockFile(), "rw"); + lock = raf.getChannel().lock(); if (Options.isDebug()) { - System.out.println( - "Ensured old .connection file is deleted. Launching Eclipse."); + System.out.println("Lock obtained."); } - eclipse = new Eclipse(); - eclipse.start(); - while (eclipse.getStatus() == Eclipse.STATUS_INIT) { + } + private void releaseLock() { + if (lock != null) { try { - Thread.sleep(50); - } catch (InterruptedException ie) { + lock.channel().close(); + if (Options.isDebug()) { + System.out.println("Lock released."); + } + lock = null; + } catch (IOException ioe) { } } - if (eclipse.getStatus() == Eclipse.STATUS_ERROR) { - throw eclipse.getException(); - } - if (Options.isDebug()) { - System.out.println("Eclipse launched"); - } } - - /** - * @return true if eclipse is already running in another process + /** Tests whether HelpApplication is running + * by testing if .applicationlock is locked */ - private boolean isAnotherRunning() { - if (!Options.getLockFile().exists()) { - if (Options.isDebug()) { - System.out.println( - "File " - + Options.getLockFile() - + " does not exist. Eclipse needs to be started."); - } - return false; - } - - if (System.getProperty("os.name").startsWith("Win")) { - // if file cannot be deleted, Eclipse is running - if (!Options.getLockFile().delete()) { - if (Options.isDebug()) { - System.out.println( - "File " - + Options.getLockFile() - + " is locked. Eclipse is already running."); + private boolean isApplicationRunning() { + File applicationLockFile = + new File(Options.getLockFile().getParentFile(), ".applicationlock"); + RandomAccessFile randomAccessFile = null; + FileLock applicationLock = null; + try { + randomAccessFile = new RandomAccessFile(applicationLockFile, "rw"); + applicationLock = randomAccessFile.getChannel().tryLock(); + } finally { + if (applicationLock != null) { + try { + applicationLock.release(); + } catch (IOException ioe) { } - return true; - } else { - return false; } - } else { - // if connection to control servlet can be made, Eclipse is running - try { - connection.renew(); - if (connection.getHost() != null - && connection.getPort() != null) { - URL url = createCommandURL("test", new String[0]); - connection.connect(url); - if (Options.isDebug()) { - System.out.println( - "Test connection to Eclipse established. No need to start new Eclipse instance."); - } - return true; + if (randomAccessFile != null) { + try { + randomAccessFile.close(); + } catch (IOException ioe) { } - } catch (Exception e) { } if (Options.isDebug()) { System.out.println( - "Test connection to Eclipse could not be established. Eclipse instance needs to be started."); + "isApplicationRunning? " + (applicationLock == null)); + } + return applicationLock == null; + } + } + public class EclipseCleaner extends Thread { + public void run() { + if (eclipse != null) { + eclipse.killProcess(); } - connection.reset(); - return false; } } - } diff --git a/org.eclipse.help/src/org/eclipse/help/internal/standalone/EclipseLifeCycleListener.java b/org.eclipse.help/src/org/eclipse/help/internal/standalone/EclipseLifeCycleListener.java new file mode 100644 index 000000000..cb91e1f5e --- /dev/null +++ b/org.eclipse.help/src/org/eclipse/help/internal/standalone/EclipseLifeCycleListener.java @@ -0,0 +1,17 @@ +/******************************************************************************* + * Copyright (c) 2003, 2003 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.help.internal.standalone; +/** + * Notified when Eclipse process exits. + */ +public interface EclipseLifeCycleListener { + public void eclipseEnded(); +} diff --git a/org.eclipse.help/src/org/eclipse/help/internal/standalone/Options.java b/org.eclipse.help/src/org/eclipse/help/internal/standalone/Options.java index 372e738fc..6cd9cf234 100644 --- a/org.eclipse.help/src/org/eclipse/help/internal/standalone/Options.java +++ b/org.eclipse.help/src/org/eclipse/help/internal/standalone/Options.java @@ -80,8 +80,8 @@ public class Options { // consume -command option helpCommand = extractOption(eclipseArgs, "-command"); - if(helpCommand==null){ - helpCommand=new ArrayList(0); + if (helpCommand == null) { + helpCommand = new ArrayList(0); } // read -debug option @@ -95,10 +95,10 @@ public class Options { } // consume -eclipsehome (accept eclipse_home too) option List homes = extractOption(eclipseArgs, "-eclipseHome"); - if (homes==null || homes.isEmpty()) { + if (homes == null || homes.isEmpty()) { homes = extractOption(eclipseArgs, "-eclipse_Home"); } - if (homes!=null && !homes.isEmpty()) { + if (homes != null && !homes.isEmpty()) { eclipseHome = new File((String) homes.get(0)); } else { eclipseHome = new File(System.getProperty("user.dir")); @@ -111,7 +111,7 @@ public class Options { } else { workspace = new File(eclipseHome, "workspace"); } - lockFile = new File(workspace, "/.metadata/.lock"); + lockFile = new File(workspace, "/.metadata/.helplock"); hostPortFile = new File(workspace, "/.metadata/.connection"); // consume -host option @@ -158,10 +158,10 @@ public class Options { } // consume -vmargs option - vmArgs=new ArrayList(0); + vmArgs = new ArrayList(0); List passedVmArgs = extractOption(eclipseArgs, "-vmargs"); - if (passedVmArgs!=null && passedVmArgs.size() > 0) { - vmArgs=passedVmArgs; + if (passedVmArgs != null && passedVmArgs.size() > 0) { + vmArgs = passedVmArgs; } // modify the options for passing them to eclipse diff --git a/org.eclipse.help/src/org/eclipse/help/internal/standalone/StandaloneHelp.java b/org.eclipse.help/src/org/eclipse/help/internal/standalone/StandaloneHelp.java index 01926dfa7..3fb01fa73 100644 --- a/org.eclipse.help/src/org/eclipse/help/internal/standalone/StandaloneHelp.java +++ b/org.eclipse.help/src/org/eclipse/help/internal/standalone/StandaloneHelp.java @@ -31,15 +31,9 @@ import org.eclipse.help.internal.HelpPlugin; * </ul> */ public class StandaloneHelp extends EclipseController { - // timout for .hostport file to apper since starting eclipse [ms] - private static final int STARTUP_TIMEOUT = 40 * 1000; - // number of retries to connectect to webapp - private static final int CONNECTION_RETRIES = 3; - // time between retries to connectect to webapp [ms] - private static final int CONNECTION_RETRY_INTERVAL = 5 * 1000; // ID of the application to run private static final String HELP_APPLICATION_ID = - HelpPlugin.PLUGIN_ID+".helpApplication"; + HelpPlugin.PLUGIN_ID + ".helpApplication"; /** * Constructs help system @@ -75,19 +69,6 @@ public class StandaloneHelp extends EclipseController { } /** - * Overrides the initialization of the connection to pass retry parameters. - */ - protected EclipseConnection initConnection() { - int timeout = STARTUP_TIMEOUT; - if (Options.getServerTimeout() > 0) { - timeout = 1000 * Options.getServerTimeout(); - } - return new EclipseConnection( - timeout, - CONNECTION_RETRIES, - CONNECTION_RETRY_INTERVAL); - } - /** * @see org.eclipse.help.standalone.Help#displayContext(java.lang.String,int,int) */ public void displayContext(String contextId, int x, int y) { |