diff --git a/bin/mac/relaunch b/bin/mac/relaunch index f66c7b8b54f3..a28ad266c756 100755 Binary files a/bin/mac/relaunch and b/bin/mac/relaunch differ diff --git a/native/macrestarter/relaunch.m b/native/macrestarter/relaunch.m index 4b596895a9fa..766d70bff8e7 100644 --- a/native/macrestarter/relaunch.m +++ b/native/macrestarter/relaunch.m @@ -2,65 +2,20 @@ #import -@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; } \ No newline at end of file diff --git a/native/macrestarter/relaunch.xcodeproj/project.pbxproj b/native/macrestarter/relaunch.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..8cc758f70181 --- /dev/null +++ b/native/macrestarter/relaunch.xcodeproj/project.pbxproj @@ -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 = ""; }; + 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 = ""; + }; + CF2F99674C3F98D5739C1B3A /* Products */ = { + isa = PBXGroup; + children = ( + 3A6C10AE148122660074811C /* relaunch */, + ); + name = Products; + sourceTree = ""; + }; + CF2F99674C3F98D5739C1B45 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* 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 */; +} diff --git a/platform/bootstrap/src/com/intellij/idea/Main.java b/platform/bootstrap/src/com/intellij/idea/Main.java index 3cd1ab049f45..aade22a072d8 100644 --- a/platform/bootstrap/src/com/intellij/idea/Main.java +++ b/platform/bootstrap/src/com/intellij/idea/Main.java @@ -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) { diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java b/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java index 8b3ab3ec0eaa..8408e01bdaf6 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java @@ -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() { diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/MacRestarter.java b/platform/platform-impl/src/com/intellij/openapi/application/impl/MacRestarter.java deleted file mode 100644 index 73b76980be65..000000000000 --- a/platform/platform-impl/src/com/intellij/openapi/application/impl/MacRestarter.java +++ /dev/null @@ -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); - } - } -} diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/Win32Restarter.java b/platform/platform-impl/src/com/intellij/openapi/application/impl/Win32Restarter.java deleted file mode 100644 index 74cd447b3a0e..000000000000 --- a/platform/platform-impl/src/com/intellij/openapi/application/impl/Win32Restarter.java +++ /dev/null @@ -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(); - } -} diff --git a/platform/platform-impl/src/com/intellij/execution/process/UnixProcessManager.java b/platform/util/src/com/intellij/execution/process/UnixProcessManager.java similarity index 89% rename from platform/platform-impl/src/com/intellij/execution/process/UnixProcessManager.java rename to platform/util/src/com/intellij/execution/process/UnixProcessManager.java index 2a799ae83ffd..4a85fe333337 100644 --- a/platform/platform-impl/src/com/intellij/execution/process/UnixProcessManager.java +++ b/platform/util/src/com/intellij/execution/process/UnixProcessManager.java @@ -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 childrenPids = Lists.newArrayList(); + List childrenPids = new ArrayList(); 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> BY_PARENT = Maps.newTreeMap(); // pid -> list of children pids + private Map> BY_PARENT = new TreeMap>(); // pid -> list of children pids public void register(Integer pid, Integer parentPid) { List children = BY_PARENT.get(parentPid); - if (children == null) children = Lists.newLinkedList(); + if (children == null) children = new LinkedList(); children.add(pid); BY_PARENT.put(parentPid, children); } diff --git a/platform/util/src/com/intellij/util/Restarter.java b/platform/util/src/com/intellij/util/Restarter.java new file mode 100644 index 000000000000..423d1eb7c872 --- /dev/null +++ b/platform/util/src/com/intellij/util/Restarter.java @@ -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); + } + } +}