Updater: automatically restart if possible

This commit is contained in:
Anton Makeev
2011-11-26 17:53:53 +01:00
parent bc347a812d
commit f2ce5df79f
9 changed files with 412 additions and 253 deletions
BIN
View File
Binary file not shown.
+10 -55
View File
@@ -2,65 +2,20 @@
#import <AppKit/AppKit.h>
@interface TerminationListener : NSObject
{
const char *executablePath;
pid_t parentProcessId;
}
int main(int argc, const char *argv[]) {
if (argc != 2) return EXIT_FAILURE;
- (void) relaunch;
unsigned int interval = 500; // check every 0.5 second
unsigned int slept = 0;
while (getppid() != 1) {
usleep(interval * 1000);
@end
@implementation TerminationListener
- (id) initWithExecutablePath:(const char *)execPath parentProcessId:(pid_t)ppid
{
self = [super init];
if (self != nil) {
executablePath = execPath;
parentProcessId = ppid;
// This adds the input source required by the run loop
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(applicationDidTerminate:) name:NSWorkspaceDidTerminateApplicationNotification object:nil];
if (getppid() == 1) {
// ppid is launchd (1) => parent terminated already
[self relaunch];
}
slept += interval;
if (slept > 10 * 1000 /* wait for maximum 10 seconds */) return EXIT_FAILURE;
}
return self;
}
- (void) applicationDidTerminate:(NSNotification *)notification
{
if (parentProcessId == [[[notification userInfo] valueForKey:@"NSApplicationProcessIdentifier"] intValue]) {
// parent just terminated
[self relaunch];
}
}
- (void) relaunch
{
// [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(actualLaunch) userInfo:nil repeats:NO];
// NSDate *future = [NSDate dateWithTimeIntervalSinceNow: 5.0 ];
// [NSThread sleepUntilDate:future];
[[NSWorkspace sharedWorkspace] launchApplication:[NSString stringWithUTF8String:executablePath]];
exit(0);
}
@end
int main (int argc, const char * argv[])
{
if (argc != 3) return EXIT_FAILURE;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[[[TerminationListener alloc] initWithExecutablePath:argv[1] parentProcessId:atoi(argv[2])] autorelease];
[[NSApplication sharedApplication] run];
[pool release];
char const *pathToRelaunch = argv[1];
[[NSWorkspace sharedWorkspace] launchApplication:[NSString stringWithUTF8String:pathToRelaunch]];
return EXIT_SUCCESS;
}
@@ -0,0 +1,209 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
3A6C10B4148122790074811C /* relaunch.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A6C10B3148122790074811C /* relaunch.m */; };
3A6C10B7148122940074811C /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A6C10B6148122940074811C /* AppKit.framework */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
CF2F99674C3F98D5739C1B44 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = /usr/share/man/man1/;
dstSubfolderSpec = 0;
files = (
);
runOnlyForDeploymentPostprocessing = 1;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
3A6C10AE148122660074811C /* relaunch */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = relaunch; sourceTree = BUILT_PRODUCTS_DIR; };
3A6C10B3148122790074811C /* relaunch.m */ = {isa = PBXFileReference; fileEncoding = 11; lastKnownFileType = sourcecode.c.objc; path = relaunch.m; sourceTree = "<group>"; };
3A6C10B6148122940074811C /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
CF2F99674C3F98D5739C1B43 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
3A6C10B7148122940074811C /* AppKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
CF2F99674C3F98D5739C1B39 = {
isa = PBXGroup;
children = (
3A6C10B3148122790074811C /* relaunch.m */,
CF2F99674C3F98D5739C1B3A /* Products */,
CF2F99674C3F98D5739C1B45 /* Frameworks */,
3A6C10B6148122940074811C /* AppKit.framework */,
);
sourceTree = "<group>";
};
CF2F99674C3F98D5739C1B3A /* Products */ = {
isa = PBXGroup;
children = (
3A6C10AE148122660074811C /* relaunch */,
);
name = Products;
sourceTree = "<group>";
};
CF2F99674C3F98D5739C1B45 /* Frameworks */ = {
isa = PBXGroup;
children = (
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
CF2F99674C3F98D5739C1B3E /* relaunch */ = {
isa = PBXNativeTarget;
buildConfigurationList = CF2F99674C3F98D5739C1B3F /* Build configuration list for PBXNativeTarget "relaunch" */;
buildPhases = (
CF2F99674C3F98D5739C1B42 /* Sources */,
CF2F99674C3F98D5739C1B43 /* Frameworks */,
CF2F99674C3F98D5739C1B44 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = relaunch;
productName = relaunch;
productReference = 3A6C10AE148122660074811C /* relaunch */;
productType = "com.apple.product-type.tool";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
CF2F99674C3F98D5739C1B37 /* Project object */ = {
isa = PBXProject;
buildConfigurationList = CF2F99674C3F98D5739C1B38 /* Build configuration list for PBXProject "relaunch" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = CF2F99674C3F98D5739C1B39;
productRefGroup = CF2F99674C3F98D5739C1B3A /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
CF2F99674C3F98D5739C1B3E /* relaunch */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
CF2F99674C3F98D5739C1B42 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
3A6C10B4148122790074811C /* relaunch.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
CF2F99674C3F98D5739C1B3B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_VERSION = 4.0;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.4;
ONLY_ACTIVE_ARCH = NO;
SDKROOT = macosx10.4;
};
name = Release;
};
CF2F99674C3F98D5739C1B3C /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
COPY_PHASE_STRIP = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_VERSION = 4.0;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.4;
ONLY_ACTIVE_ARCH = NO;
SDKROOT = macosx10.4;
};
name = Debug;
};
CF2F99674C3F98D5739C1B40 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_PRECOMPILE_PREFIX_HEADER = NO;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
CF2F99674C3F98D5739C1B41 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_PRECOMPILE_PREFIX_HEADER = NO;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
CF2F99674C3F98D5739C1B38 /* Build configuration list for PBXProject "relaunch" */ = {
isa = XCConfigurationList;
buildConfigurations = (
CF2F99674C3F98D5739C1B3B /* Release */,
CF2F99674C3F98D5739C1B3C /* Debug */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
CF2F99674C3F98D5739C1B3F /* Build configuration list for PBXNativeTarget "relaunch" */ = {
isa = XCConfigurationList;
buildConfigurations = (
CF2F99674C3F98D5739C1B40 /* Release */,
CF2F99674C3F98D5739C1B41 /* Debug */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = CF2F99674C3F98D5739C1B37 /* Project object */;
}
@@ -19,6 +19,7 @@ package com.intellij.idea;
import com.intellij.ide.Bootstrap;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.util.Restarter;
import org.jetbrains.annotations.NonNls;
import javax.swing.*;
@@ -39,12 +40,38 @@ public class Main {
public static void main(final String[] args) {
if (installPatch()) {
boolean restarted = false;
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
restarted = Restarter.restart();
}
catch (Exception ignore) { }
JOptionPane.showMessageDialog(null, "The application cannot start right away since some critical files have been changed, " +
"please restart it manually.");
catch (Restarter.CannotRestartException e) {
// noinspection CallToPrintStackTrace
e.printStackTrace();
}
final int restartCode = Restarter.getRestartCode();
if (!restarted && restartCode == 0) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
catch (Exception ignore) {
}
JOptionPane.showMessageDialog(null,
"The application cannot start right away since some critical files have been changed.\n" +
"Please restart it manually.",
"Update",
JOptionPane.INFORMATION_MESSAGE);
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
System.exit(restartCode);
}
});
return;
}
@@ -69,11 +96,11 @@ public class Main {
return false;
}
final String firstArg = args[0];
return forceEnabledHeadlessMode ||
Comparing.strEqual(firstArg, antAppCode) ||
Comparing.strEqual(firstArg, duplocateCode) ||
Comparing.strEqual(firstArg, traverseUI) ||
(firstArg.length() < 20 && firstArg.endsWith("inspect"));
return forceEnabledHeadlessMode ||
Comparing.strEqual(firstArg, antAppCode) ||
Comparing.strEqual(firstArg, duplocateCode) ||
Comparing.strEqual(firstArg, traverseUI) ||
(firstArg.length() < 20 && firstArg.endsWith("inspect"));
}
public static boolean isUITraverser(final String[] args) {
@@ -59,6 +59,7 @@ import com.intellij.ui.Splash;
import com.intellij.util.Consumer;
import com.intellij.util.EventDispatcher;
import com.intellij.util.ReflectionCache;
import com.intellij.util.Restarter;
import com.intellij.util.concurrency.ReentrantWriterPreferenceReadWriteLock;
import com.intellij.util.containers.Stack;
import com.intellij.util.io.storage.HeavyProcessLatch;
@@ -246,13 +247,7 @@ public class ApplicationImpl extends ComponentManagerImpl implements Application
});
}
final String s = System.getProperty("jb.restart.code");
if (s != null) {
try {
myRestartCode = Integer.parseInt(s);
} catch (NumberFormatException ignore) {
}
}
myRestartCode = Restarter.getRestartCode();
registerFont("/fonts/Inconsolata.ttf");
}
@@ -1297,23 +1292,23 @@ public class ApplicationImpl extends ComponentManagerImpl implements Application
}
public boolean isRestartCapable() {
return SystemInfo.isWindows || SystemInfo.isMacOSSnowLeopard || myRestartCode > 0;
return Restarter.isSupported() || myRestartCode > 0;
}
public void restart() {
if (SystemInfo.isWindows) {
Win32Restarter.restart();
boolean restarted = false;
try {
restarted = Restarter.restart();
}
else if (SystemInfo.isMacOSSnowLeopard) {
MacRestarter.restart();
catch (Restarter.CannotRestartException e) {
LOG.warn(e);
}
else if (myRestartCode > 0) {
if (!restarted) {
myExitCode = myRestartCode;
exit(true);
}
else {
exit(true);
}
exit(true);
}
public boolean isSaving() {
@@ -1,71 +0,0 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.application.impl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ex.ApplicationEx;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.ui.mac.foundation.Foundation;
import com.intellij.ui.mac.foundation.ID;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
/**
* User: spLeaner
*/
public class MacRestarter {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.application.impl.MacRestarter");
private MacRestarter() {
}
@SuppressWarnings({"RedundantArrayCreation"})
public static void restart() {
final ID autoReleasePool = Foundation.invoke("NSAutoreleasePool", "new");
final ID app = Foundation.invoke("NSRunningApplication", "currentApplication");
final ID executableURL = Foundation.invoke(app, Foundation.createSelector("executableURL"));
final ID stringURL = Foundation.invoke(executableURL, Foundation.createSelector("absoluteString"));
try {
final String executablePath = Foundation.toStringViaUTF8(stringURL);
final URL url = new URL(executablePath);
final String path = url.getPath().replace("%20", " ");
if (path.contains(".app")) {
final int appIndex = path.indexOf(".app");
final String appPath = path.substring(0, appIndex + 4);
final String relaunchPath = path.substring(0, path.lastIndexOf('/')) + "/../../bin/relaunch";
if (new File(relaunchPath).exists()) {
final long processId = Foundation.invoke(app, Foundation.createSelector("processIdentifier")).longValue();
final ID args = Foundation.invoke(Foundation.getClass("NSArray"), Foundation.createSelector("arrayWithObjects:"),
new Object[]{Foundation.nsString(appPath), Foundation.nsString(String.valueOf(processId))});
Foundation.invoke(Foundation.getClass("NSTask"), Foundation.createSelector("launchedTaskWithLaunchPath:arguments:"),
Foundation.nsString(relaunchPath), args);
}
}
}
catch (MalformedURLException e) {
LOG.error(e);
}
finally {
Foundation.invoke(autoReleasePool, Foundation.createSelector("release"));
((ApplicationEx)ApplicationManager.getApplication()).exit(true);
}
}
}
@@ -1,70 +0,0 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.application.impl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.application.ex.ApplicationEx;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.ui.Messages;
import com.sun.jna.Native;
import com.sun.jna.WString;
import com.sun.jna.win32.StdCallLibrary;
import java.io.File;
import java.io.IOException;
/**
* @author yole
*/
public class Win32Restarter {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.application.impl.Win32Restarter");
private Win32Restarter() {
}
public static void restart() {
Kernel32 kernel32 = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class);
WString cline = kernel32.GetCommandLineW();
int pid = kernel32.GetCurrentProcessId();
try {
String command = "restarter " + Integer.toString(pid) + " " + cline;
LOG.info("Restarter command: " + command);
Runtime.getRuntime().exec(command, null, new File(PathManager.getBinPath()));
}
catch (IOException ex) {
Messages.showMessageDialog("Restart failed: " + ex.getMessage(), "Restart", Messages.getErrorIcon());
return;
}
// Since the process ID is passed through the command line, we want to make sure that we don't exit before the "restarter"
// process has a chance to open the handle to our process, and that it doesn't wait for the termination of an unrelated
// process which happened to have the same process ID.
try {
Thread.sleep(500);
}
catch (InterruptedException e1) {
// ignore
}
((ApplicationEx)ApplicationManager.getApplication()).exit(true);
}
private interface Kernel32 extends StdCallLibrary {
WString GetCommandLineW();
int GetCurrentProcessId();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,8 +15,6 @@
*/
package com.intellij.execution.process;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.intellij.openapi.util.SystemInfo;
import com.sun.jna.Library;
import com.sun.jna.Native;
@@ -25,16 +23,8 @@ import org.jetbrains.annotations.NotNull;
import java.io.*;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.StringTokenizer;
import java.util.*;
/**
* Utility class to terminate unix processes.
*
* @author traff
*/
public class UnixProcessManager {
public static final int SIGINT = 2;
public static final int SIGKILL = 9;
@@ -56,6 +46,25 @@ public class UnixProcessManager {
private UnixProcessManager() {
}
public static int getProcessPid() {
checkCLib();
return C_LIB.getpid();
}
public static int getProcessPid(Process process) {
try {
Field f = process.getClass().getDeclaredField("pid");
f.setAccessible(true);
return ((Number)f.get(process)).intValue();
}
catch (NoSuchFieldException e) {
throw new IllegalStateException("system is not unix", e);
}
catch (IllegalAccessException e) {
throw new IllegalStateException("system is not unix", e);
}
}
public static void sendSignal(Process process, int signal) {
int process_pid = getProcessPid(process);
sendSignal(process_pid, signal);
@@ -99,11 +108,11 @@ public class UnixProcessManager {
@SuppressWarnings({"IOResourceOpenedButNotSafelyClosed"})
BufferedReader stdInput = new BufferedReader(new
InputStreamReader(p.getInputStream()));
InputStreamReader(p.getInputStream()));
BufferedReader stdError = new BufferedReader(new
InputStreamReader(p.getErrorStream()));
InputStreamReader(p.getErrorStream()));
List<Integer> childrenPids = Lists.newArrayList();
List<Integer> childrenPids = new ArrayList<Integer>();
boolean result;
try {
@@ -162,20 +171,6 @@ public class UnixProcessManager {
}
}
public static int getProcessPid(Process process) {
try {
Field f = process.getClass().getDeclaredField("pid");
f.setAccessible(true);
return ((Number)f.get(process)).intValue();
}
catch (NoSuchFieldException e) {
throw new IllegalStateException("system is not unix", e);
}
catch (IllegalAccessException e) {
throw new IllegalStateException("system is not unix", e);
}
}
public static String[] getPSCmd(boolean commandLineOnly) {
if (SystemInfo.isLinux) {
return new String[]{"ps", "-e", "e", "--format", commandLineOnly ? "%a" : "%P%p%a"};
@@ -210,11 +205,11 @@ public class UnixProcessManager {
}
private static class ProcessInfo {
private Map<Integer, List<Integer>> BY_PARENT = Maps.newTreeMap(); // pid -> list of children pids
private Map<Integer, List<Integer>> BY_PARENT = new TreeMap<Integer, List<Integer>>(); // pid -> list of children pids
public void register(Integer pid, Integer parentPid) {
List<Integer> children = BY_PARENT.get(parentPid);
if (children == null) children = Lists.newLinkedList();
if (children == null) children = new LinkedList<Integer>();
children.add(pid);
BY_PARENT.put(parentPid, children);
}
@@ -0,0 +1,119 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.util;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.util.SystemInfo;
import com.sun.jna.Native;
import com.sun.jna.WString;
import com.sun.jna.win32.StdCallLibrary;
import java.io.File;
import java.io.IOException;
public class Restarter {
private Restarter() {
}
public static int getRestartCode() {
String s = System.getProperty("jb.restart.code");
if (s != null) {
try {
return Integer.parseInt(s);
}
catch (NumberFormatException ignore) {
}
}
return 0;
}
public static boolean isSupported() {
return SystemInfo.isWindows || SystemInfo.isMac;
}
public static boolean restart() throws CannotRestartException {
try {
if (SystemInfo.isWindows) {
return restartOnWindows();
}
else if (SystemInfo.isMac) {
return restartOnMac();
}
}
catch (CannotRestartException e) {
throw e;
}
catch (Throwable e) {
throw new CannotRestartException(e);
}
return false;
}
private static boolean restartOnWindows() throws CannotRestartException {
Kernel32 kernel32 = (Kernel32)Native.loadLibrary("kernel32", Kernel32.class);
WString cline = kernel32.GetCommandLineW();
int pid = kernel32.GetCurrentProcessId();
try {
String command = "restarter " + Integer.toString(pid) + " " + cline;
Runtime.getRuntime().exec(command, null, new File(PathManager.getBinPath()));
}
catch (IOException ex) {
throw new CannotRestartException(ex);
}
// Since the process ID is passed through the command line, we want to make sure that we don't exit before the "restarter"
// process has a chance to open the handle to our process, and that it doesn't wait for the termination of an unrelated
// process which happened to have the same process ID.
try {
Thread.sleep(500);
}
catch (InterruptedException e1) {
// ignore
}
return true;
}
private interface Kernel32 extends StdCallLibrary {
WString GetCommandLineW();
int GetCurrentProcessId();
}
private static boolean restartOnMac() throws CannotRestartException {
String binPath = PathManager.getBinPath();
if (!binPath.contains(".app")) return false;
int appIndex = binPath.indexOf(".app");
String appPath = binPath.substring(0, appIndex + 4);
try {
Runtime.getRuntime().exec(new String[]{new File(PathManager.getBinPath(), "relaunch").getPath(), appPath});
}
catch (IOException e) {
throw new CannotRestartException(e);
}
return true;
}
public static class CannotRestartException extends Exception {
public CannotRestartException(Throwable cause) {
super(cause);
}
}
}