...25import org.openqa.selenium.TimeoutException;26import org.openqa.selenium.docker.Container;27import org.openqa.selenium.docker.Docker;28import org.openqa.selenium.docker.Image;29import org.openqa.selenium.docker.Port;30import org.openqa.selenium.grid.data.CreateSessionRequest;31import org.openqa.selenium.grid.node.ActiveSession;32import org.openqa.selenium.grid.node.SessionFactory;33import org.openqa.selenium.net.PortProber;34import org.openqa.selenium.remote.Command;35import org.openqa.selenium.remote.Dialect;36import org.openqa.selenium.remote.DriverCommand;37import org.openqa.selenium.remote.ProtocolHandshake;38import org.openqa.selenium.remote.Response;39import org.openqa.selenium.remote.SessionId;40import org.openqa.selenium.remote.http.HttpClient;41import org.openqa.selenium.remote.http.HttpRequest;42import org.openqa.selenium.remote.http.HttpResponse;43import org.openqa.selenium.support.ui.FluentWait;44import org.openqa.selenium.support.ui.Wait;45import java.io.IOException;46import java.io.UncheckedIOException;47import java.net.MalformedURLException;48import java.net.URL;49import java.time.Duration;50import java.util.Map;51import java.util.Objects;52import java.util.Optional;53import java.util.logging.Level;54import java.util.logging.Logger;55public class DockerSessionFactory implements SessionFactory {56 public static final Logger LOG = Logger.getLogger(DockerSessionFactory.class.getName());57 private final HttpClient.Factory clientFactory;58 private final Docker docker;59 private final Image image;60 private final Capabilities stereotype;61 public DockerSessionFactory(62 HttpClient.Factory clientFactory,63 Docker docker,64 Image image,65 Capabilities stereotype) {66 this.clientFactory = Objects.requireNonNull(clientFactory, "HTTP client must be set.");67 this.docker = Objects.requireNonNull(docker, "Docker command must be set.");68 this.image = Objects.requireNonNull(image, "Docker image to use must be set.");69 this.stereotype = ImmutableCapabilities.copyOf(70 Objects.requireNonNull(stereotype, "Stereotype must be set."));71 }72 @Override73 public boolean test(Capabilities capabilities) {74 return stereotype.getCapabilityNames().stream()75 .map(name -> Objects.equals(stereotype.getCapability(name), capabilities.getCapability(name)))76 .reduce(Boolean::logicalAnd)77 .orElse(false);78 }79 @Override80 public Optional<ActiveSession> apply(CreateSessionRequest sessionRequest) {81 LOG.info("Starting session for " + sessionRequest.getCapabilities());82 int port = PortProber.findFreePort();83 URL remoteAddress = getUrl(port);84 HttpClient client = clientFactory.createClient(remoteAddress);85 LOG.info("Creating container, mapping container port 4444 to " + port);86 Container container = docker.create(image(image).map(Port.tcp(4444), Port.tcp(port)));87 container.start();88 LOG.info(String.format("Waiting for server to start (container id: %s)", container.getId()));89 try {90 waitForServerToStart(client, Duration.ofMinutes(1));91 } catch (TimeoutException e) {92 container.stop(Duration.ofMinutes(1));93 container.delete();94 LOG.warning(String.format(95 "Unable to connect to docker server (container id: %s)", container.getId()));96 return Optional.empty();97 }98 LOG.info(String.format("Server is ready (container id: %s)", container.getId()));99 Command command = new Command(100 null,...