Selenium Grid is a distributed testing infrastructure that allows running WebDriver tests in parallel across multiple machines and browsers.
—
Selenium Grid nodes execute WebDriver commands and manage browser sessions. They provide the actual browser automation capabilities and can run locally or remotely across the grid infrastructure.
The main node interface defines the contract for WebDriver session execution and management.
/**
* Abstract base class for all grid nodes
*/
abstract class Node {
/** Protected constructor with core dependencies */
protected Node(Tracer tracer, NodeId id, URI uri, Secret registrationSecret, Duration sessionTimeout);
/** Create a new WebDriver session on this node */
abstract Either<WebDriverException, CreateSessionResponse> newSession(CreateSessionRequest sessionRequest);
/** Execute a WebDriver command for an existing session */
abstract HttpResponse executeWebDriverCommand(HttpRequest req);
/** Get session information by ID */
abstract Session getSession(SessionId id);
/** Stop and cleanup a session */
abstract void stop(SessionId id);
/** Check if this node owns the specified session */
abstract boolean isSessionOwner(SessionId id);
/** Check if this node supports the given capabilities */
abstract boolean isSupporting(Capabilities capabilities);
/** Get current node status including available slots */
abstract NodeStatus getStatus();
/** Get health check for monitoring node availability */
abstract HealthCheck getHealthCheck();
/** Begin draining this node (stop accepting new sessions) */
abstract void drain();
/** Get the node ID (concrete implementation) */
NodeId getId();
/** Get the node URI (concrete implementation) */
URI getUri();
}Default implementation for running browser sessions in the same process as the node.
/**
* Local node implementation that manages browser sessions directly
*/
class LocalNode extends Node {
/** Builder class for constructing LocalNode instances */
static Builder builder(Tracer tracer, EventBus eventBus, URI uri, URI gridUri, Secret registrationSecret);
/** Create a LocalNode from configuration */
static Node create(Config config);
// Builder pattern for complex construction
static class Builder {
/** Add a session factory for specific capabilities */
Builder add(Capabilities stereotype, SessionFactory factory);
/** Set maximum number of concurrent sessions */
Builder maximumConcurrentSessions(int maxSessions);
/** Set session timeout */
Builder sessionTimeout(Duration timeout);
/** Set heartbeat period for status updates */
Builder heartbeatPeriod(Duration period);
/** Build the LocalNode instance */
LocalNode build();
}
// Implement all abstract methods
Either<WebDriverException, CreateSessionResponse> newSession(CreateSessionRequest sessionRequest);
HttpResponse executeWebDriverCommand(HttpRequest req);
Session getSession(SessionId id);
void stop(SessionId id);
boolean isSessionOwner(SessionId id);
boolean isSupporting(Capabilities capabilities);
NodeStatus getStatus();
HealthCheck getHealthCheck();
void drain();
}Usage Example:
// Create session factories for different browsers
SessionFactory chromeFactory = new ChromeSessionFactory();
SessionFactory firefoxFactory = new FirefoxSessionFactory();
// Build the local node
LocalNode node = LocalNode.builder(tracer, eventBus, nodeUri, gridUri, registrationSecret)
.add(new ImmutableCapabilities("browserName", "chrome"), chromeFactory)
.add(new ImmutableCapabilities("browserName", "firefox"), firefoxFactory)
.maximumConcurrentSessions(10)
.sessionTimeout(Duration.ofMinutes(5))
.heartbeatPeriod(Duration.ofSeconds(30))
.build();
// Register with distributor
Distributor distributor = LocalDistributor.create(config);
distributor = distributor.add(node);Proxy for accessing nodes running in remote processes via HTTP.
/**
* Remote node proxy for distributed deployments
*/
class RemoteNode extends Node {
RemoteNode(Tracer tracer, HttpClient.Factory httpClientFactory, NodeId id, URI uri, Secret registrationSecret);
// All methods implemented via HTTP calls to remote node
Either<WebDriverException, CreateSessionResponse> newSession(CreateSessionRequest sessionRequest);
HttpResponse executeWebDriverCommand(HttpRequest req);
Session getSession(SessionId id);
void stop(SessionId id);
boolean isSessionOwner(SessionId id);
boolean isSupporting(Capabilities capabilities);
NodeStatus getStatus();
HealthCheck getHealthCheck();
void drain();
}Special node type for integrating external WebDriver services.
/**
* Relay node for external WebDriver service integration
*/
class RelayNode extends Node {
RelayNode(Tracer tracer, NodeId id, URI uri, Secret registrationSecret,
Duration sessionTimeout, HttpClient.Factory httpClientFactory,
Capabilities stereotype, URI serviceUri);
// Relays commands to external service
Either<WebDriverException, CreateSessionResponse> newSession(CreateSessionRequest sessionRequest);
HttpResponse executeWebDriverCommand(HttpRequest req);
Session getSession(SessionId id);
void stop(SessionId id);
boolean isSessionOwner(SessionId id);
boolean isSupporting(Capabilities capabilities);
NodeStatus getStatus();
HealthCheck getHealthCheck();
void drain();
}Active session interface and default implementation for tracking browser sessions.
/**
* Interface representing an active WebDriver session
*/
interface ActiveSession {
/** Get the session ID */
SessionId getId();
/** Get the session capabilities */
Capabilities getCapabilities();
/** Get the WebDriver instance for this session */
WebDriver getWrappedDriver();
/** Execute a WebDriver command */
Response execute(Command command) throws IOException;
/** Stop the session and cleanup resources */
void stop();
}
/**
* Default implementation of ActiveSession
*/
class DefaultActiveSession implements ActiveSession {
DefaultActiveSession(WebDriver driver, SessionId id, Capabilities capabilities);
SessionId getId();
Capabilities getCapabilities();
WebDriver getWrappedDriver();
Response execute(Command command) throws IOException;
void stop();
}
/**
* Factory for creating WebDriver sessions
*/
interface SessionFactory {
/** Check if this factory can create sessions with the given capabilities */
boolean test(Capabilities capabilities);
/** Create a new WebDriver session */
Either<WebDriverException, ActiveSession> apply(CreateSessionRequest sessionRequest);
}Docker-based session management for containerized browser execution.
/**
* Docker-based WebDriver session implementation
*/
class DockerSession extends DefaultActiveSession {
DockerSession(Container container, WebDriver driver, SessionId id, Capabilities caps);
/** Get the Docker container for this session */
Container getContainer();
@Override
void stop(); // Includes container cleanup
}
/**
* Factory for creating Docker-based sessions
*/
class DockerSessionFactory implements SessionFactory {
DockerSessionFactory(Tracer tracer, HttpClient.Factory clientFactory, DockerOptions dockerOptions);
boolean test(Capabilities capabilities);
Either<WebDriverException, ActiveSession> apply(CreateSessionRequest sessionRequest);
/** Get available Docker images */
Set<String> getAvailableImages();
}Node-specific configuration settings and command-line flags.
/**
* Configuration options for node behavior
*/
class NodeOptions {
static final String NODE_SECTION = "node";
/** Get the public-facing URI for this node */
URI getPublicUri(Config config);
/** Get the grid registration URI */
URI getGridUri(Config config);
/** Get maximum number of concurrent sessions */
int getMaxSessions(Config config);
/** Get session timeout duration */
Duration getSessionTimeout(Config config);
/** Get heartbeat period for status updates */
Duration getHeartbeatPeriod(Config config);
/** Get node-specific capabilities */
Map<String, Object> getCapabilities(Config config);
}
/**
* Command-line flags for node configuration
*/
class NodeFlags {
@Parameter(names = {"--detect-drivers"},
description = "Autodetect available WebDriver executables")
Boolean detectDrivers = true;
@Parameter(names = {"--max-sessions"},
description = "Maximum number of concurrent sessions")
int maxSessions = Runtime.getRuntime().availableProcessors();
@Parameter(names = {"--session-timeout"},
description = "Session timeout in seconds")
int sessionTimeout = 300;
@Parameter(names = {"--heartbeat-period"},
description = "Heartbeat period in seconds")
int heartbeatPeriod = 60;
@Parameter(names = {"--override-max-sessions"},
description = "Allow overriding max sessions calculation")
Boolean overrideMaxSessions = false;
}
/**
* Docker-specific configuration options
*/
class DockerOptions {
static final String DOCKER_SECTION = "docker";
/** Get Docker host URI */
URI getDockerUri(Config config);
/** Get available Docker images mapping */
Map<String, String> getDockerImages(Config config);
/** Get video recording settings */
boolean getVideoRecording(Config config);
/** Get Docker network settings */
String getDockerNetwork(Config config);
}Health check interface for monitoring node availability and status.
/**
* Functional interface for node health checks
*/
@FunctionalInterface
interface HealthCheck {
/** Perform health check and return result */
Result check();
/** Health check result with availability status and message */
class Result {
Result(Availability availability, String message);
/** Get the availability status */
Availability getAvailability();
/** Get descriptive message about health status */
String getMessage();
}
}// Create and start a node
LocalNode node = LocalNode.builder(tracer, eventBus, nodeUri, gridUri, secret)
.add(chromeCapabilities, chromeFactory)
.build();
// Node automatically registers with grid and starts accepting sessions
// Graceful shutdown
node.drain(); // Stop accepting new sessions
// Wait for existing sessions to complete
node.stop(); // Cleanup resources// Session creation errors
Either<WebDriverException, CreateSessionResponse> result = node.newSession(request);
if (result.isLeft()) {
WebDriverException error = result.left();
// Common errors:
// - UnsupportedCapabilitiesException: Capabilities not supported
// - SessionNotCreatedException: Browser startup failed
// - ResourceUnavailableException: Node at capacity
System.err.println("Session creation failed: " + error.toString());
}
// Command execution errors
try {
HttpResponse response = node.executeWebDriverCommand(webdriverRequest);
// Process successful response
} catch (Exception e) {
// Handle communication errors, timeouts, etc.
System.err.println("Command execution failed: " + e.getMessage());
}Install with Tessl CLI
npx tessl i tessl/maven-org-seleniumhq-selenium--selenium-grid