3535import java .net .MalformedURLException ;
3636import java .net .URL ;
3737import java .time .Duration ;
38+ import java .util .ArrayList ;
3839import java .util .List ;
3940import java .util .Map ;
4041import java .util .Optional ;
4647import java .util .regex .Pattern ;
4748
4849import static com .google .common .base .Preconditions .checkNotNull ;
49- import static io .appium .java_client .service .local .AppiumServiceBuilder .BROADCAST_IP_ADDRESS ;
50+ import static io .appium .java_client .service .local .AppiumServiceBuilder .BROADCAST_IP4_ADDRESS ;
51+ import static io .appium .java_client .service .local .AppiumServiceBuilder .BROADCAST_IP6_ADDRESS ;
5052import static org .slf4j .event .Level .DEBUG ;
5153import static org .slf4j .event .Level .INFO ;
5254
@@ -57,6 +59,7 @@ public final class AppiumDriverLocalService extends DriverService {
5759 private static final Pattern LOGGER_CONTEXT_PATTERN = Pattern .compile ("^(\\ [debug\\ ] )?\\ [(.+?)\\ ]" );
5860 private static final String APPIUM_SERVICE_SLF4J_LOGGER_PREFIX = "appium.service" ;
5961 private static final Duration DESTROY_TIMEOUT = Duration .ofSeconds (60 );
62+ private static final Duration IS_RUNNING_PING_TIMEOUT = Duration .ofMillis (1500 );
6063
6164 private final File nodeJSExec ;
6265 private final List <String > nodeJSArgs ;
@@ -106,7 +109,7 @@ private static URL addSuffix(URL url, String suffix) {
106109 @ SneakyThrows
107110 @ SuppressWarnings ("SameParameterValue" )
108111 private static URL replaceHost (URL source , String oldHost , String newHost ) {
109- return new URL (source .toString ().replace (oldHost , newHost ));
112+ return new URL (source .toString ().replaceFirst (oldHost , newHost ));
110113 }
111114
112115 /**
@@ -128,7 +131,7 @@ public boolean isRunning() {
128131 }
129132
130133 try {
131- ping (Duration . ofMillis ( 1500 ) );
134+ ping (IS_RUNNING_PING_TIMEOUT );
132135 return true ;
133136 } catch (UrlChecker .TimeoutException e ) {
134137 return false ;
@@ -142,8 +145,15 @@ public boolean isRunning() {
142145 }
143146
144147 private void ping (Duration timeout ) throws UrlChecker .TimeoutException , MalformedURLException {
145- // The operating system might block direct access to the universal broadcast IP address
146- URL status = addSuffix (replaceHost (getUrl (), BROADCAST_IP_ADDRESS , "127.0.0.1" ), "/status" );
148+ URL url = getUrl ();
149+ String host = url .getHost ();
150+ // The operating system will block direct access to the universal broadcast IP address
151+ if (host .equals (BROADCAST_IP4_ADDRESS )) {
152+ url = replaceHost (url , BROADCAST_IP4_ADDRESS , "127.0.0.1" );
153+ } else if (host .equals (BROADCAST_IP6_ADDRESS )) {
154+ url = replaceHost (url , BROADCAST_IP6_ADDRESS , "::1" );
155+ }
156+ URL status = addSuffix (url , "/status" );
147157 new UrlChecker ().waitUntilAvailable (timeout .toMillis (), TimeUnit .MILLISECONDS , status );
148158 }
149159
@@ -161,25 +171,36 @@ public void start() throws AppiumServerHasNotBeenStartedLocallyException {
161171 }
162172
163173 try {
164- process = new CommandLine (this .nodeJSExec .getCanonicalPath (),
165- nodeJSArgs .toArray (new String []{}));
174+ process = new CommandLine (
175+ this .nodeJSExec .getCanonicalPath (),
176+ nodeJSArgs .toArray (new String []{})
177+ );
166178 process .setEnvironmentVariables (nodeJSEnvironment );
167179 process .copyOutputTo (stream );
168180 process .executeAsync ();
169181 ping (startupTimeout );
170- } catch (Throwable e ) {
182+ } catch (Exception e ) {
183+ final Optional <String > output = Optional .ofNullable (process )
184+ .map (CommandLine ::getStdOut )
185+ .filter ((o ) -> !StringUtils .isBlank (o ));
171186 destroyProcess ();
172- String msgTxt = "The local appium server has not been started. "
173- + "The given Node.js executable: " + this . nodeJSExec . getAbsolutePath ()
174- + " Arguments: " + nodeJSArgs . toString () + " " + " \n " ;
175- if (process != null ) {
176- String processStream = process . getStdOut ();
177- if (! StringUtils . isBlank ( processStream )) {
178- msgTxt = msgTxt + "Process output: " + processStream + " \n " ;
179- }
187+ List < String > errorLines = new ArrayList <>();
188+ errorLines . add ( "The local appium server has not been started" );
189+ errorLines . add ( String . format ( "Reason: %s" , e . getMessage ())) ;
190+ if (e instanceof UrlChecker . TimeoutException ) {
191+ errorLines . add ( String . format (
192+ "Consider increasing the server startup timeout value (currently %sms)" ,
193+ startupTimeout . toMillis ()
194+ ));
180195 }
181-
182- throw new AppiumServerHasNotBeenStartedLocallyException (msgTxt , e );
196+ errorLines .add (
197+ String .format ("Node.js executable path: %s" , nodeJSExec .getAbsolutePath ())
198+ );
199+ errorLines .add (String .format ("Arguments: %s" , nodeJSArgs ));
200+ output .ifPresent ((o ) -> errorLines .add (String .format ("Output: %s" , o )));
201+ throw new AppiumServerHasNotBeenStartedLocallyException (
202+ StringUtils .joinWith ("\n " , errorLines ), e
203+ );
183204 }
184205 } finally {
185206 lock .unlock ();
0 commit comments