From 6ee097f5a2cc84eb5a20ad9c92c9c132c5c373aa Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Wed, 27 May 2020 12:21:22 +0200 Subject: [PATCH 1/2] fix: Increase the timeout for graceful service termination --- .../local/AppiumDriverLocalService.java | 48 +++++++++++++++++-- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/appium/java_client/service/local/AppiumDriverLocalService.java b/src/main/java/io/appium/java_client/service/local/AppiumDriverLocalService.java index c3f9e0389..2cae3e8c9 100644 --- a/src/main/java/io/appium/java_client/service/local/AppiumDriverLocalService.java +++ b/src/main/java/io/appium/java_client/service/local/AppiumDriverLocalService.java @@ -37,8 +37,10 @@ import java.io.File; import java.io.IOException; import java.io.OutputStream; +import java.lang.reflect.Field; import java.net.MalformedURLException; import java.net.URL; +import java.time.Duration; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; @@ -56,6 +58,8 @@ public final class AppiumDriverLocalService extends DriverService { private static final Pattern LOG_MESSAGE_PATTERN = Pattern.compile("^(.*)\\R"); private static final Pattern LOGGER_CONTEXT_PATTERN = Pattern.compile("^(\\[debug\\] )?\\[(.+?)\\]"); private static final String APPIUM_SERVICE_SLF4J_LOGGER_PREFIX = "appium.service"; + private static final Duration DESTROY_TIMEOUT = Duration.ofSeconds(60); + private final File nodeJSExec; private final ImmutableList nodeJSArgs; private final ImmutableMap nodeJSEnvironment; @@ -65,6 +69,7 @@ public final class AppiumDriverLocalService extends DriverService { private final ListOutputStream stream = new ListOutputStream().add(System.out); private final URL url; + private CommandLine process = null; AppiumDriverLocalService(String ipAddress, File nodeJSExec, int nodeJSPort, @@ -187,10 +192,47 @@ public void stop() { } } - private void destroyProcess() { - if (process.isRunning()) { - process.destroy(); + /** + * Destroys the service if it is running. + * + * @param timeout The maximum time to wait before the process will be force-killed. + * @return The exit code of the process or zero if the process was not running. + */ + private int destroyProcess(Duration timeout) { + if (!process.isRunning()) { + return 0; } + + // This all magic is necessary, because Selenium does not publicly expose + // process killing timeouts. By default a process is killed forcibly if + // it does not exit after two seconds, which is in most cases not enough for + // Appium + try { + Field processField = process.getClass().getDeclaredField("process"); + processField.setAccessible(true); + Object osProcess = processField.get(process); + Field watchdogField = osProcess.getClass().getDeclaredField("executeWatchdog"); + watchdogField.setAccessible(true); + Object watchdog = watchdogField.get(osProcess); + Field nativeProcessField = watchdog.getClass().getDeclaredField("process"); + nativeProcessField.setAccessible(true); + Process nativeProcess = (Process) nativeProcessField.get(watchdog); + nativeProcess.destroy(); + nativeProcess.waitFor(timeout.toMillis(), TimeUnit.MILLISECONDS); + } catch (Exception e) { + LOG.warn("No explicit timeout could be applied to the process termination", e); + } + + return process.destroy(); + } + + /** + * Destroys the service. + * This methods waits up to `DESTROY_TIMEOUT` seconds for the Appium service + * to exit gracefully. + */ + private void destroyProcess() { + destroyProcess(DESTROY_TIMEOUT); } /** From 85be4ad18c4fdb75ec9053f18481816dab5d8cc7 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Wed, 27 May 2020 12:24:22 +0200 Subject: [PATCH 2/2] Delete extra line break --- .../java_client/service/local/AppiumDriverLocalService.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/io/appium/java_client/service/local/AppiumDriverLocalService.java b/src/main/java/io/appium/java_client/service/local/AppiumDriverLocalService.java index 2cae3e8c9..3e82e0de4 100644 --- a/src/main/java/io/appium/java_client/service/local/AppiumDriverLocalService.java +++ b/src/main/java/io/appium/java_client/service/local/AppiumDriverLocalService.java @@ -68,8 +68,7 @@ public final class AppiumDriverLocalService extends DriverService { private final ReentrantLock lock = new ReentrantLock(true); //uses "fair" thread ordering policy private final ListOutputStream stream = new ListOutputStream().add(System.out); private final URL url; - - + private CommandLine process = null; AppiumDriverLocalService(String ipAddress, File nodeJSExec, int nodeJSPort,