I'm currently playing around with the Selenium Marionette WebDriver
. In my application, I want to open multiple Marionette drivers sequentially. Basically something like this:
MarionetteDriver driver = new MarionetteDriver();
// do some stuff
driver.quit();
// a while later
driver = new MarionetteDriver();
// do some stuff
driver.quit();
Now I'm facing the problem, that only the first Marionette instance can be successfully started and for each later attempt, I'm getting the following exception. The problem happens every time and the used port always changes, so there is obviously no port conflict.
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.48.2', revision: '41bccdd10cf2c0560f637404c2d96164b67d9d67', time: '2015-10-09 13:08:06'
System info: host: 'qqilihq.local', ip: '192.168.1.2', os.name: 'Mac OS X', os.arch: 'x86_64', os.version: '10.11.2', java.version: '1.7.0_71'
Driver info: driver.version: MarionetteDriver
at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:641)
at org.openqa.selenium.remote.RemoteWebDriver.startSession(RemoteWebDriver.java:247)
at org.openqa.selenium.remote.RemoteWebDriver.startSession(RemoteWebDriver.java:232)
at org.openqa.selenium.firefox.MarionetteDriver.run(MarionetteDriver.java:84)
at org.openqa.selenium.firefox.MarionetteDriver.<init>(MarionetteDriver.java:73)
at org.openqa.selenium.firefox.MarionetteDriver.<init>(MarionetteDriver.java:45)
at MyMainClass.main(MyMainClass.java:131)
Caused by: org.openqa.selenium.WebDriverException: org.apache.http.conn.HttpHostConnectException: Connect to localhost:41886 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: Connection refused
Build info: version: '2.48.2', revision: '41bccdd10cf2c0560f637404c2d96164b67d9d67', time: '2015-10-09 13:08:06'
System info: host: 'qqilihq.local', ip: '192.168.1.2', os.name: 'Mac OS X', os.arch: 'x86_64', os.version: '10.11.2', java.version: '1.7.0_71'
Driver info: driver.version: MarionetteDriver
at org.openqa.selenium.remote.service.DriverCommandExecutor.execute(DriverCommandExecutor.java:91)
at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:620)
... 6 more
Caused by: org.apache.http.conn.HttpHostConnectException: Connect to localhost:41886 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: Connection refused
at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:151)
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:353)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:380)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:184)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:88)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:71)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:55)
at org.openqa.selenium.remote.internal.ApacheHttpClient.fallBackExecute(ApacheHttpClient.java:143)
at org.openqa.selenium.remote.internal.ApacheHttpClient.execute(ApacheHttpClient.java:89)
at org.openqa.selenium.remote.HttpCommandExecutor.execute(HttpCommandExecutor.java:142)
at org.openqa.selenium.remote.service.DriverCommandExecutor.execute(DriverCommandExecutor.java:82)
... 7 more
Caused by: java.net.ConnectException: Connection refused
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:339)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:200)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:182)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:579)
at org.apache.http.conn.socket.PlainConnectionSocketFactory.connectSocket(PlainConnectionSocketFactory.java:74)
at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator. java:134)
... 20 more
Any pointers appreciated!
After digging a little deeper, I came to the following findings which eventually solved my issue:
Marionette resp. wires
uses two ports (see wires --help
); a marionette-port
and a webdriver-port
:
Usage:
./wires [OPTIONS]
WebDriver to marionette proxy.
optional arguments:
-h,--help show this help message and exit
-b,--binary BINARY Path to the Firefox binary
--webdriver-host WEBDRIVER_HOST
Host to run webdriver server on
--webdriver-port WEBDRIVER_PORT
Port to run webdriver on
--marionette-port MARIONETTE_PORT
Port to run marionette on
--connect-existing Connect to an existing firefox process
When running multiple MarionetteDrivers
simultaneously, both ports have to be different from the already running instance obviously. However, when using the default constructor new MarionetteDriver()
the marionette-port
remains constant (and is not determined based on some free port). We used some workaround (see below) for the GeckoDriverService.Builder
to always pick two randomly-chosen available ports.
The current (version 2.48.2) GeckoDriverService
has an empty implementation of waitUntilAvailable()
(which should check, if the WebDriver
is ready to go). Sporadically, this lead to the UnreachableBrowserException
posted above.
To circumvent these issues, we did something like this at the end:
// determine free ports for Marionette and WebDriver
final int marionettePort = PortProber.findFreePort();
final int webDriverPort = PortProber.findFreePort();
// override, as GeckoDriverService provides no direct way to set the Marionette port
GeckoDriverService.Builder builder = new GeckoDriverService.Builder() {
@Override
protected ImmutableList<String> createArgs() {
Builder<String> argsBuilder = ImmutableList.builder();
argsBuilder.addAll(super.createArgs());
argsBuilder.add(String.format("--marionette-port=%d", marionettePort));
return argsBuilder.build();
}
};
builder.usingPort(webDriverPort);
builder.usingDriverExecutable(pathToDriver);
GeckoDriverService driverService = builder.build();
try {
driverService.start();
} catch (IOException e) {
throw new IllegalStateException("Could not start the GeckoDriverService", e);
}
try {
// keep checking the WebDriver port via Socket until it's available;
// as far as I could tell, there is nothing more "high level", e.g. REST API
waitUntilReady(webDriverPort, TimeUnit.SECONDS.toMillis(30));
} catch (InterruptedException e) {
// ignore
}
return new MarionetteDriver(driverService, capabilities);
You need to handle the binary required by Marionette. You can do it by hand (download the binary on your own and export the proper variable), or you can do it automatically using WebDriverManager. Simply add the following dependency:
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<version>1.6.0</version>
</dependency>
And then, in your code call to:
FirefoxDriverManager.getInstance().setup();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With