1717package io .appium .java_client .service .local ;
1818
1919import com .google .common .annotations .VisibleForTesting ;
20- import io .appium .java_client .internal .ReflectionHelpers ;
2120import lombok .SneakyThrows ;
2221import org .openqa .selenium .net .UrlChecker ;
23- import org .openqa .selenium .os .CommandLine ;
22+ import org .openqa .selenium .os .ExternalProcess ;
2423import org .openqa .selenium .remote .service .DriverService ;
2524import org .slf4j .Logger ;
2625import org .slf4j .LoggerFactory ;
4948import static io .appium .java_client .service .local .AppiumServiceBuilder .BROADCAST_IP4_ADDRESS ;
5049import static io .appium .java_client .service .local .AppiumServiceBuilder .BROADCAST_IP6_ADDRESS ;
5150import static java .util .Objects .requireNonNull ;
51+ import static java .util .Optional .ofNullable ;
5252import static org .slf4j .event .Level .DEBUG ;
5353import static org .slf4j .event .Level .INFO ;
5454
@@ -70,7 +70,7 @@ public final class AppiumDriverLocalService extends DriverService {
7070 private final URL url ;
7171 private String basePath ;
7272
73- private CommandLine process = null ;
73+ private ExternalProcess process = null ;
7474
7575 AppiumDriverLocalService (String ipAddress , File nodeJSExec ,
7676 int nodeJSPort , Duration startupTimeout ,
@@ -126,7 +126,7 @@ public URL getUrl() {
126126 public boolean isRunning () {
127127 lock .lock ();
128128 try {
129- if (process == null || !process .isRunning ()) {
129+ if (process == null || !process .isAlive ()) {
130130 return false ;
131131 }
132132
@@ -172,17 +172,15 @@ public void start() throws AppiumServerHasNotBeenStartedLocallyException {
172172 }
173173
174174 try {
175- process = new CommandLine (
176- this .nodeJSExec .getCanonicalPath (),
177- nodeJSArgs .toArray (new String []{})
178- );
179- process .setEnvironmentVariables (nodeJSEnvironment );
180- process .copyOutputTo (stream );
181- process .executeAsync ();
175+ ExternalProcess .Builder processBuilder = ExternalProcess .builder ()
176+ .command (this .nodeJSExec .getCanonicalPath (), nodeJSArgs )
177+ .copyOutputTo (stream );
178+ nodeJSEnvironment .forEach (processBuilder ::environment );
179+ process = processBuilder .start ();
182180 ping (startupTimeout );
183181 } catch (Exception e ) {
184- final Optional <String > output = Optional . ofNullable (process )
185- .map (CommandLine :: getStdOut )
182+ final Optional <String > output = ofNullable (process )
183+ .map (ExternalProcess :: getOutput )
186184 .filter (o -> !isNullOrEmpty (o ));
187185 destroyProcess ();
188186 List <String > errorLines = new ArrayList <>();
@@ -227,47 +225,16 @@ public void stop() {
227225 }
228226 }
229227
230- /**
231- * Destroys the service if it is running.
232- *
233- * @param timeout The maximum time to wait before the process will be force-killed.
234- * @return The exit code of the process or zero if the process was not running.
235- */
236- private int destroyProcess (Duration timeout ) {
237- if (process == null || !process .isRunning ()) {
238- return 0 ;
239- }
240-
241- // This all magic is necessary, because Selenium does not publicly expose
242- // process killing timeouts. By default a process is killed forcibly if
243- // it does not exit after two seconds, which is in most cases not enough for
244- // Appium
245- try {
246- Object osProcess = ReflectionHelpers .getPrivateFieldValue (
247- process .getClass (), process , "process" , Object .class
248- );
249- Object watchdog = ReflectionHelpers .getPrivateFieldValue (
250- osProcess .getClass (), osProcess , "executeWatchdog" , Object .class
251- );
252- Process nativeProcess = ReflectionHelpers .getPrivateFieldValue (
253- watchdog .getClass (), watchdog , "process" , Process .class
254- );
255- nativeProcess .destroy ();
256- nativeProcess .waitFor (timeout .toMillis (), TimeUnit .MILLISECONDS );
257- } catch (Exception e ) {
258- LOG .warn ("No explicit timeout could be applied to the process termination" , e );
259- }
260-
261- return process .destroy ();
262- }
263-
264228 /**
265229 * Destroys the service.
266- * This methods waits up to `DESTROY_TIMEOUT` seconds for the Appium service
230+ * This method waits up to `DESTROY_TIMEOUT` seconds for the Appium service
267231 * to exit gracefully.
268232 */
269233 private void destroyProcess () {
270- destroyProcess (DESTROY_TIMEOUT );
234+ if (process == null || !process .isAlive ()) {
235+ return ;
236+ }
237+ process .shutdown (DESTROY_TIMEOUT );
271238 }
272239
273240 /**
@@ -277,11 +244,7 @@ private void destroyProcess() {
277244 */
278245 @ Nullable
279246 public String getStdOut () {
280- if (process != null ) {
281- return process .getStdOut ();
282- }
283-
284- return null ;
247+ return ofNullable (process ).map (ExternalProcess ::getOutput ).orElse (null );
285248 }
286249
287250 /**
0 commit comments