Skip to content

When building a AppiumDriverLocalService, there is a Port number missmatch when providing --port argument #521

Closed

Description

@alexander-poulikakos

Description

When providing the port number via withArgument("--port", "8998") method, there becomes a miss match in ports, i.e. server starts on one port, while java_client tries to connect to another port. In below code example/stackTrace The Appium server is started on port 8998, but the java_client tries to connect to the default port "4723" and times out with an exception.

As can be seen in below stacktrace the port number is added twice in the node arguments.
The given Node.js executable: /usr/local/Cellar/node/5.5.0/bin/node Arguments: [/usr/local/lib/node_modules/appium/build/lib/main.js, --port, 4723, --address, 0.0.0.0, --port, 8998]

Environment

  • java client build version: java_client-4.1.2
  • Appium server version: v1.6.1-beta
  • Desktop OS/version used: MAC OSX 10.11.6
  • Node.js version: v5.5.0
  • Mobile platform/version under test:
  • Real device

Code To Reproduce Issue [ Good To Have ]

package _play;

import java.util.HashMap;
import java.util.Map;

import org.openqa.selenium.remote.DesiredCapabilities;

import io.appium.java_client.ios.IOSDriver;
import io.appium.java_client.remote.MobileCapabilityType;
import io.appium.java_client.service.local.AppiumDriverLocalService;
import io.appium.java_client.service.local.AppiumServiceBuilder;

public class _AppiumServerClientPlay {
	
	public static void main(String[] args) {
		Map<String, String> env = new HashMap<>(System.getenv());
		env.put("PATH", "/usr/local/bin:" + env.get("PATH"));
		
		AppiumDriverLocalService server = new AppiumServiceBuilder()
		.withEnvironment(env)
		.withArgument(() -> "--port", "8998")
		.build();
		
		DesiredCapabilities caps = new DesiredCapabilities();
		caps.setCapability(MobileCapabilityType.APP, "__/path/to/your.app__");
		caps.setCapability(MobileCapabilityType.UDID, "__your_UDID_here__");
		caps.setCapability(MobileCapabilityType.DEVICE_NAME, "Iphone");
		
		new IOSDriver<>(server, caps);
	}
}

Ecxeption stacktraces

[Appium] Welcome to Appium v1.6.1-beta
[Appium] Non-default server args:
[Appium]   port: 8998
[Appium] Appium REST http interface listener started on 0.0.0.0:8998
Exception in thread "main" org.openqa.selenium.remote.UnreachableBrowserException: Could not start a new session. Possible causes are invalid address of the remote server or browser start-up failure.
Build info: version: '2.53.1', revision: 'a36b8b1cd5757287168e54b817830adce9b0158d', time: '2016-06-30 19:26:09'
System info: host: '******', ip: '*********', os.name: 'Mac OS X', os.arch: 'x86_64', os.version: '10.11.6', java.version: '1.8.0_72'
Driver info: driver.version: IOSDriver
	at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:665)
	at io.appium.java_client.DefaultGenericMobileDriver.execute(DefaultGenericMobileDriver.java:40)
	at io.appium.java_client.AppiumDriver.execute(AppiumDriver.java:1)
	at io.appium.java_client.ios.IOSDriver.execute(IOSDriver.java:1)
	at org.openqa.selenium.remote.RemoteWebDriver.startSession(RemoteWebDriver.java:249)
	at org.openqa.selenium.remote.RemoteWebDriver.<init>(RemoteWebDriver.java:131)
	at org.openqa.selenium.remote.RemoteWebDriver.<init>(RemoteWebDriver.java:144)
	at io.appium.java_client.DefaultGenericMobileDriver.<init>(DefaultGenericMobileDriver.java:36)
	at io.appium.java_client.AppiumDriver.<init>(AppiumDriver.java:114)
	at io.appium.java_client.AppiumDriver.<init>(AppiumDriver.java:145)
	at io.appium.java_client.ios.IOSDriver.<init>(IOSDriver.java:108)
	at _play._AppiumServerClientPlay.main(_AppiumServerClientPlay.java:32)
Caused by: io.appium.java_client.service.local.AppiumServerHasNotBeenStartedLocallyException: The local appium server has not been started. The given Node.js executable: /usr/local/Cellar/node/5.5.0/bin/node Arguments: [/usr/local/lib/node_modules/appium/build/lib/main.js, --port, 4723, --address, 0.0.0.0, --port, 8998] 
Process output: [Appium] Welcome to Appium v1.6.1-beta
[Appium] Non-default server args:
[Appium]   port: 8998
[Appium] Appium REST http interface listener started on 0.0.0.0:8998


	at io.appium.java_client.service.local.AppiumDriverLocalService.start(AppiumDriverLocalService.java:155)
	at io.appium.java_client.remote.AppiumCommandExecutor.execute(AppiumCommandExecutor.java:65)
	at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:644)
	... 11 more
Caused by: org.openqa.selenium.net.UrlChecker$TimeoutException: Timed out waiting for [http://0.0.0.0:4723/wd/hub/status] to be available after 120003 ms
	at org.openqa.selenium.net.UrlChecker.waitUntilAvailable(UrlChecker.java:107)
	at io.appium.java_client.service.local.AppiumDriverLocalService.ping(AppiumDriverLocalService.java:115)
	at io.appium.java_client.service.local.AppiumDriverLocalService.start(AppiumDriverLocalService.java:142)
	... 13 more
Caused by: com.google.common.util.concurrent.UncheckedTimeoutException: java.util.concurrent.TimeoutException
	at com.google.common.util.concurrent.SimpleTimeLimiter.callWithTimeout(SimpleTimeLimiter.java:143)
	at org.openqa.selenium.net.UrlChecker.waitUntilAvailable(UrlChecker.java:80)
	... 15 more
Caused by: java.util.concurrent.TimeoutException
	at java.util.concurrent.FutureTask.get(FutureTask.java:205)
	at com.google.common.util.concurrent.SimpleTimeLimiter.callWithTimeout(SimpleTimeLimiter.java:130)
	... 16 more

Activity

  1. alexander-poulikakos commented on Nov 23, 2016

    @alexander-poulikakos
    ContributorAuthor

    It seems as these server flags gets a special treatment: --log, --port and --address. I think the withArgument(...) method should be able to handle those as well. The usecase in my case is I'm reading the appium server flags from a properties file and would prefer to treat them in the same generic way.

    Would modifying the withArgument(...) method to something like the below proposal be ok? If so, I can provide a pull request (with unit tests etc.) and we can continue discussing it from there.

        /**
         * @param argument is an instance which contains the argument name.
         * @param value    A non null string value. (Warn!!!) Boolean arguments have a special moment:
         *                 the presence of an arguments means "true". At this case an empty string
         *                 should be defined.
         * @return the self-reference.
         */
    	public AppiumServiceBuilder withArgument(ServerArgument argument, String value) {
    		String argName = argument.getArgument().trim();
    		if (argName.equals("--port") || argName.equals("-p")) {
    			usingPort(Integer.valueOf(value));
    		} else if (argName.equals("--address") || argName.equals("-a")) {
    			withIPAddress(value);
    		} else if (argName.equals("--log") || argName.equals("-g")) {
    			withLogFile(new File(value));
    		} else {
    			serverArguments.put(argName, value);
    		}
    		return this;
        }
  2. TikhomirovSergey commented on Nov 23, 2016

    @TikhomirovSergey
    Contributor

    @alexander-poulikakos
    Please just use these methods

    usingPort()
    withIPAddress()
    withLogFile()
    

    Thse is the set of methods provided by DriverService (except withIPAddress)

    As I can read the log there is another problem

    [Appium] Welcome to Appium v1.6.1-beta
    [Appium] Non-default server args:
    [Appium]   port: 8998
    [Appium] Appium REST http interface listener started on 0.0.0.0:8998
    

    That means that the service was started successfully (!!!).
    But

    aused by: org.openqa.selenium.net.UrlChecker$TimeoutException: Timed out waiting for [http://0.0.0.0:4723/wd/hub/status] to be available after 120003 ms
    	at org.openqa.selenium.net.UrlChecker.waitUntilAvailable(UrlChecker.java:107)
    

    Do you see the difference? This is because it considers that the port was not provided so it uses the default value (4723)

    Actually current implementation does same things as you are going to propose. It adds port, log file and ip to arguments.

  3. alexander-poulikakos commented on Nov 24, 2016

    @alexander-poulikakos
    ContributorAuthor

    thanks for your reply @TikhomirovSergey.

    My use case is that I am reading the arguments from a properties file, something like this:

    argument.--port = 8998
    argument.--log-level = info
    argument.--log = /path/to/log
    argument.--log-timestamp=
    

    and I would like to set them in a generic way for all arguments. Not make special cases for some of the arguments.

    Since the API allows any argument to be set through the ´withArgument(...)´ method, then it should work for all arguments, including --port.

    For example, a unit test like below should pass. Currently it is not.

        @Test public void checkAbilityToStartServiceWithPortUsingFlags() throws Exception {
        	String port = "8996";
        	String expectedUrl = String.format("http://0.0.0.0:%s/wd/hub", port);
        	
        	AppiumDriverLocalService service = new AppiumServiceBuilder()
        			.withArgument(() -> "--port", port)
        			.build();
    	String actualUrl = service.getUrl().toString();
    	assertEquals(expectedUrl, actualUrl);
        }

    The problem is that the ´DriverService´ does not get aware of the new port when using ´withArgument(() -> "--port", "...")´ method.

    Actually current implementation does same things as you are going to propose. It adds port, log file and ip to arguments.

    No, that is not the same thing. What I propose is to make ´DriverService´ aware of the new port when using ´withArgument(...)´ method.

  4. TikhomirovSergey commented on Nov 24, 2016

    @TikhomirovSergey
    Contributor

    @alexander-poulikakos Now I got your problem. Ok. You can propose the PR.

    My remark: Please use equalsIgnoreCase instead of equals
    Also don't forget to provide some tests.

  5. TikhomirovSergey commented on Nov 25, 2016

    @TikhomirovSergey
    Contributor

    It was improved. It is going to be published soon.

  6. alexander-poulikakos commented on Nov 25, 2016

    @alexander-poulikakos
    ContributorAuthor

    Just a minor comment: This calls the toLowerCase() method.

     String argName = argument.getArgument().trim().toLowerCase();

    So no need to call equalsIgnoreCase multiple (6) times in below code.

            String argName = argument.getArgument().trim().toLowerCase();
            if ("--port".equalsIgnoreCase(argName) || "-p".equalsIgnoreCase(argName)) {
                usingPort(Integer.valueOf(value));
            } else if ("--address".equalsIgnoreCase(argName) || "-a".equalsIgnoreCase(argName)) {
                 withIPAddress(value);
            } else if ("--log".equalsIgnoreCase(argName) || "-g".equalsIgnoreCase(argName)) {
                withLogFile(new File(value));
            } else {
                serverArguments.put(argName, value);
            }

    Would be cleaner if equals is used :)

            String argName = argument.getArgument().trim().toLowerCase();
            if ("--port".equals(argName) || "-p".equals(argName)) {
                usingPort(Integer.valueOf(value));
            } else if ("--address".equals(argName) || "-a".equals(argName)) {
                 withIPAddress(value);
            } else if ("--log".equals(argName) || "-g".equals(argName)) {
                withLogFile(new File(value));
            } else {
                serverArguments.put(argName, value);
            }
  7. TikhomirovSergey commented on Nov 25, 2016

    @TikhomirovSergey
    Contributor

    ok. Will improve that.

  8. added a commit that references this issue on Nov 25, 2016
  9. TikhomirovSergey commented on Nov 25, 2016

    @TikhomirovSergey
    Contributor

    I'm closing this ticket.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions