Skip to content

Commit

Permalink
[java] Implementing ability to specify command execution timeout in R…
Browse files Browse the repository at this point in the history
…emoteWebDriver
  • Loading branch information
barancev committed Dec 7, 2020
1 parent 77c187a commit 8be1107
Show file tree
Hide file tree
Showing 10 changed files with 188 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,15 @@
package org.openqa.selenium.remote;

import java.io.IOException;
import java.time.Duration;

@FunctionalInterface
public interface CommandExecutor {

Response execute(Command command) throws IOException;

default void setCommandExecutionTimeout(Duration timeout) {
throw new UnsupportedOperationException(
"This command executor does not allow to change command execution timeout");
}
}
31 changes: 20 additions & 11 deletions java/client/src/org/openqa/selenium/remote/HttpCommandExecutor.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import java.util.Map;

import static java.util.Collections.emptyMap;
Expand Down Expand Up @@ -68,19 +69,22 @@ public HttpCommandExecutor(URL addressOfRemoteServer) {
* @param addressOfRemoteServer URL of remote end Selenium server
*/
public HttpCommandExecutor(
Map<String, CommandInfo> additionalCommands,
URL addressOfRemoteServer) {
Map<String, CommandInfo> additionalCommands,
URL addressOfRemoteServer)
{
this(additionalCommands, addressOfRemoteServer, defaultClientFactory);
}

public HttpCommandExecutor(
Map<String, CommandInfo> additionalCommands,
URL addressOfRemoteServer,
HttpClient.Factory httpClientFactory) {
Map<String, CommandInfo> additionalCommands,
URL addressOfRemoteServer,
HttpClient.Factory httpClientFactory)
{
try {
remoteServer = addressOfRemoteServer == null
? new URL(System.getProperty("webdriver.remote.server", "http://localhost:4444/"))
: addressOfRemoteServer;
? new URL(System.getProperty("webdriver.remote.server",
"http://localhost:4444/"))
: addressOfRemoteServer;
} catch (MalformedURLException e) {
throw new WebDriverException(e);
}
Expand Down Expand Up @@ -126,7 +130,7 @@ public Response execute(Command command) throws IOException {
if (!GET_ALL_SESSIONS.equals(command.getName())
&& !NEW_SESSION.equals(command.getName())) {
throw new NoSuchSessionException(
"Session ID is null. Using WebDriver after calling quit()?");
"Session ID is null. Using WebDriver after calling quit()?");
}
}

Expand All @@ -149,7 +153,7 @@ public Response execute(Command command) throws IOException {

if (commandCodec == null || responseCodec == null) {
throw new WebDriverException(
"No command or response codec has been defined. Unable to proceed");
"No command or response codec has been defined. Unable to proceed");
}

HttpRequest httpRequest = commandCodec.encode(command);
Expand Down Expand Up @@ -180,10 +184,15 @@ public Response execute(Command command) throws IOException {
} catch (UnsupportedCommandException e) {
if (e.getMessage() == null || "".equals(e.getMessage())) {
throw new UnsupportedOperationException(
"No information from server. Command name was: " + command.getName(),
e.getCause());
"No information from server. Command name was: " + command.getName(),
e.getCause());
}
throw e;
}
}

@Override
public synchronized void setCommandExecutionTimeout(Duration timeout) {
client.setReadTimeout(timeout);
}
}
97 changes: 52 additions & 45 deletions java/client/src/org/openqa/selenium/remote/RemoteWebDriver.java
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,10 @@ protected void setCommandExecutor(CommandExecutor executor) {
this.executor = executor;
}

public synchronized void setCommandExecutionTimeout(Duration timeout) {
executor.setCommandExecutionTimeout(timeout);
}

@Override
public Capabilities getCapabilities() {
return capabilities;
Expand Down Expand Up @@ -316,9 +320,10 @@ public <X> X getScreenshotAs(OutputType<X> outputType) throws WebDriverException
String base64EncodedPng = new String((byte[]) result, UTF_8);
return outputType.convertFromBase64Png(base64EncodedPng);
} else {
throw new RuntimeException(String.format("Unexpected result for %s command: %s",
DriverCommand.SCREENSHOT,
result == null ? "null" : result.getClass().getName() + " instance"));
throw new RuntimeException(String.format(
"Unexpected result for %s command: %s",
DriverCommand.SCREENSHOT,
result == null ? "null" : result.getClass().getName() + " instance"));
}
}

Expand Down Expand Up @@ -438,14 +443,15 @@ public String getWindowHandle() {
public Object executeScript(String script, Object... args) {
if (!isJavascriptEnabled()) {
throw new UnsupportedOperationException(
"You must be using an underlying instance of WebDriver that supports executing javascript");
"You must be using an underlying instance of WebDriver that supports executing javascript");
}

// Escape the quote marks
script = script.replaceAll("\"", "\\\"");

List<Object> convertedArgs = Stream.of(args).map(new WebElementToJsonConverter()).collect(
Collectors.toList());
List<Object> convertedArgs = Stream.of(args)
.map(new WebElementToJsonConverter())
.collect(Collectors.toList());

return execute(DriverCommand.EXECUTE_SCRIPT(script, convertedArgs)).getValue();
}
Expand All @@ -454,14 +460,15 @@ public Object executeScript(String script, Object... args) {
public Object executeAsyncScript(String script, Object... args) {
if (!isJavascriptEnabled()) {
throw new UnsupportedOperationException("You must be using an underlying instance of " +
"WebDriver that supports executing javascript");
"WebDriver that supports executing javascript");
}

// Escape the quote marks
script = script.replaceAll("\"", "\\\"");

List<Object> convertedArgs = Stream.of(args).map(new WebElementToJsonConverter()).collect(
Collectors.toList());
List<Object> convertedArgs = Stream.of(args)
.map(new WebElementToJsonConverter())
.collect(Collectors.toList());

return execute(DriverCommand.EXECUTE_ASYNC_SCRIPT(script, convertedArgs)).getValue();
}
Expand Down Expand Up @@ -596,15 +603,15 @@ public Mouse getMouse() {

@Override
public VirtualAuthenticator addVirtualAuthenticator(VirtualAuthenticatorOptions options) {
String authenticatorId = (String)
execute(DriverCommand.ADD_VIRTUAL_AUTHENTICATOR, options.toMap()).getValue();
String authenticatorId = (String) execute(
DriverCommand.ADD_VIRTUAL_AUTHENTICATOR, options.toMap()).getValue();
return new RemoteVirtualAuthenticator(authenticatorId);
}

@Override
public void removeVirtualAuthenticator(VirtualAuthenticator authenticator) {
execute(DriverCommand.REMOVE_VIRTUAL_AUTHENTICATOR,
ImmutableMap.of("authenticatorId", authenticator.getId()));
ImmutableMap.of("authenticatorId", authenticator.getId()));
}

/**
Expand Down Expand Up @@ -687,24 +694,24 @@ public Set<Cookie> getCookies() {
}

((Collection<?>) returned).stream()
.map(o -> (Map<String, Object>) o)
.map(rawCookie -> {
// JSON object keys are defined in
// https://w3c.github.io/webdriver/#dfn-table-for-cookie-conversion.
Cookie.Builder builder =
new Cookie.Builder((String) rawCookie.get("name"), (String) rawCookie.get("value"))
.path((String) rawCookie.get("path"))
.domain((String) rawCookie.get("domain"))
.isSecure(rawCookie.containsKey("secure") && (Boolean) rawCookie.get("secure"))
.isHttpOnly(
rawCookie.containsKey("httpOnly") && (Boolean) rawCookie.get("httpOnly"))
.sameSite((String) rawCookie.get("sameSite"));

Number expiryNum = (Number) rawCookie.get("expiry");
builder.expiresOn(expiryNum == null ? null : new Date(SECONDS.toMillis(expiryNum.longValue())));
return builder.build();
})
.forEach(toReturn::add);
.map(o -> (Map<String, Object>) o)
.map(rawCookie -> {
// JSON object keys are defined in
// https://w3c.github.io/webdriver/#dfn-table-for-cookie-conversion.
Cookie.Builder builder =
new Cookie.Builder((String) rawCookie.get("name"), (String) rawCookie.get("value"))
.path((String) rawCookie.get("path"))
.domain((String) rawCookie.get("domain"))
.isSecure(rawCookie.containsKey("secure") && (Boolean) rawCookie.get("secure"))
.isHttpOnly(
rawCookie.containsKey("httpOnly") && (Boolean) rawCookie.get("httpOnly"))
.sameSite((String) rawCookie.get("sameSite"));

Number expiryNum = (Number) rawCookie.get("expiry");
builder.expiresOn(expiryNum == null ? null : new Date(SECONDS.toMillis(expiryNum.longValue())));
return builder.build();
})
.forEach(toReturn::add);

return toReturn;
}
Expand Down Expand Up @@ -903,10 +910,10 @@ public WebDriver frame(int frameIndex) {
public WebDriver frame(String frameName) {
String name = frameName.replaceAll("(['\"\\\\#.:;,!?+<>=~*^$|%&@`{}\\-/\\[\\]\\(\\)])", "\\\\$1");
List<WebElement> frameElements = RemoteWebDriver.this.findElements(
By.cssSelector("frame[name='" + name + "'],iframe[name='" + name + "']"));
By.cssSelector("frame[name='" + name + "'],iframe[name='" + name + "']"));
if (frameElements.size() == 0) {
frameElements = RemoteWebDriver.this.findElements(
By.cssSelector("frame#" + name + ",iframe#" + name));
By.cssSelector("frame#" + name + ",iframe#" + name));
}
if (frameElements.size() == 0) {
throw new NoSuchFrameException("No frame element found by name or id " + frameName);
Expand Down Expand Up @@ -1028,16 +1035,16 @@ public String getId() {
@Override
public void addCredential(Credential credential) {
execute(DriverCommand.ADD_CREDENTIAL,
new ImmutableMap.Builder<String, Object>()
.putAll(credential.toMap())
.put("authenticatorId", id)
.build());
new ImmutableMap.Builder<String, Object>()
.putAll(credential.toMap())
.put("authenticatorId", id)
.build());
}

@Override
public List<Credential> getCredentials() {
List<Map<String, Object>> response = (List<Map<String, Object>>)
execute(DriverCommand.GET_CREDENTIALS, ImmutableMap.of("authenticatorId", id)).getValue();
List<Map<String, Object>> response = (List<Map<String, Object>>) execute(
DriverCommand.GET_CREDENTIALS, ImmutableMap.of("authenticatorId", id)).getValue();
return response.stream().map(Credential::fromMap).collect(Collectors.toList());
}

Expand All @@ -1049,7 +1056,7 @@ public void removeCredential(byte[] credentialId) {
@Override
public void removeCredential(String credentialId) {
execute(DriverCommand.REMOVE_CREDENTIAL,
ImmutableMap.of("authenticatorId", id, "credentialId", credentialId));
ImmutableMap.of("authenticatorId", id, "credentialId", credentialId));
}

@Override
Expand All @@ -1060,7 +1067,7 @@ public void removeAllCredentials() {
@Override
public void setUserVerified(boolean verified) {
execute(DriverCommand.SET_USER_VERIFIED,
ImmutableMap.of("authenticatorId", id, "isUserVerified", verified));
ImmutableMap.of("authenticatorId", id, "isUserVerified", verified));
}
}

Expand All @@ -1087,10 +1094,10 @@ public String toString() {
}

return String.format(
"%s: %s on %s (%s)",
getClass().getSimpleName(),
caps.getBrowserName(),
platform,
getSessionId());
"%s: %s on %s (%s)",
getClass().getSimpleName(),
caps.getBrowserName(),
platform,
getSessionId());
}
}
13 changes: 9 additions & 4 deletions java/client/src/org/openqa/selenium/remote/http/HttpClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package org.openqa.selenium.remote.http;

import java.net.URL;
import java.time.Duration;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.stream.Collectors;
Expand All @@ -34,6 +35,10 @@ public interface HttpClient extends HttpHandler {

WebSocket openSocket(HttpRequest request, WebSocket.Listener listener);

default void setReadTimeout(Duration timeout) {
throw new UnsupportedOperationException("This client does not allow to change read timeout");
}

interface Factory {

/**
Expand All @@ -47,15 +52,15 @@ interface Factory {
static Factory create(String name) {
ServiceLoader<HttpClient.Factory> loader = ServiceLoader.load(HttpClient.Factory.class);
Set<Factory> factories = StreamSupport.stream(loader.spliterator(), true)
.filter(p -> p.getClass().isAnnotationPresent(HttpClientName.class))
.filter(p -> name.equals(p.getClass().getAnnotation(HttpClientName.class).value()))
.collect(Collectors.toSet());
.filter(p -> p.getClass().isAnnotationPresent(HttpClientName.class))
.filter(p -> name.equals(p.getClass().getAnnotation(HttpClientName.class).value()))
.collect(Collectors.toSet());
if (factories.isEmpty()) {
throw new IllegalArgumentException("Unknown HttpClient factory " + name);
}
if (factories.size() > 1) {
throw new IllegalStateException(String.format(
"There are multiple HttpClient factories by name %s, check your classpath", name));
"There are multiple HttpClient factories by name %s, check your classpath", name));
}
return factories.iterator().next();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package org.openqa.selenium.remote.http;

import java.io.UncheckedIOException;
import java.time.Duration;

@FunctionalInterface
public interface HttpHandler {
Expand Down
Loading

0 comments on commit 8be1107

Please sign in to comment.