Selenium Grid server that enables distributed test execution across multiple browsers and operating systems.
—
The Selenium Grid server provides comprehensive extension points through public interfaces and abstract classes, enabling customization of Grid behavior for enterprise requirements.
Interface for providing custom WebDriver implementations that can be discovered and used by the Grid server.
package org.openqa.selenium.remote.server;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.WebDriver;
interface DriverProvider {
/**
* Get capabilities provided by this driver
* @return Capabilities that this provider can handle
*/
Capabilities getProvidedCapabilities();
/**
* Check if provider can create driver for given capabilities
* @param capabilities Desired capabilities to check
* @return true if provider supports these capabilities
*/
boolean canCreateDriverInstanceFor(Capabilities capabilities);
/**
* Create new WebDriver instance
* @param capabilities Capabilities for driver creation
* @return New WebDriver instance
*/
WebDriver newInstance(Capabilities capabilities);
}Usage Example:
public class CustomDriverProvider implements DriverProvider {
@Override
public Capabilities getProvidedCapabilities() {
return new ImmutableCapabilities("browserName", "customBrowser");
}
@Override
public boolean canCreateDriverInstanceFor(Capabilities capabilities) {
return "customBrowser".equals(capabilities.getBrowserName());
}
@Override
public WebDriver newInstance(Capabilities capabilities) {
return new CustomWebDriver(capabilities);
}
}Fundamental interface for handling HTTP requests in the Grid server architecture.
@FunctionalInterface
interface CommandHandler {
/**
* Execute HTTP request and generate response
* @param req HTTP request to process
* @param resp HTTP response to populate
* @throws IOException if request processing fails
*/
void execute(HttpRequest req, HttpResponse resp) throws IOException;
}Usage Example:
public class CustomCommandHandler implements CommandHandler {
@Override
public void execute(HttpRequest req, HttpResponse resp) throws IOException {
// Custom request processing logic
resp.setStatus(200);
resp.setContent(asJson(ImmutableMap.of("status", "custom response")));
}
}Interface for creating new WebDriver sessions with custom logic and browser support.
interface SessionFactory {
/**
* Check if this factory can create sessions for given capabilities
* @param capabilities Desired browser capabilities
* @return true if factory supports these capabilities
*/
boolean isSupporting(Capabilities capabilities);
/**
* Create new active session instance
* @param downstreamDialects WebDriver protocol dialects supported
* @param capabilities Desired browser capabilities
* @return ActiveSession if creation successful, empty otherwise
*/
Optional<ActiveSession> apply(Set<Dialect> downstreamDialects, Capabilities capabilities);
}Usage Example:
public class CustomSessionFactory implements SessionFactory {
@Override
public boolean isSupporting(Capabilities capabilities) {
return "customBrowser".equals(capabilities.getBrowserName());
}
@Override
public Optional<ActiveSession> apply(Set<Dialect> downstreamDialects, Capabilities capabilities) {
// Create custom session implementation
return Optional.of(new CustomActiveSession(capabilities));
}
}Interface representing an active WebDriver session that can handle commands and provide driver access.
interface ActiveSession extends CommandHandler, WrapsDriver {
/**
* Get unique session identifier
* @return SessionId for this session
*/
SessionId getId();
/**
* Get session capabilities
* @return Capabilities used to create this session
*/
Capabilities getCapabilities();
/**
* Get session creation timestamp
* @return Instant when session was created
*/
Instant getStartTime();
/**
* Get underlying WebDriver instance
* @return WebDriver instance for this session
*/
WebDriver getWrappedDriver();
}Interface for creating custom CLI commands that integrate with the Grid server's command system.
interface CliCommand {
/**
* Get command name used in CLI
* @return Command name string
*/
String getName();
/**
* Get command description for help
* @return Human-readable description
*/
String getDescription();
/**
* Configure command with arguments and return executable
* @param args Command line arguments
* @return Executable that can be run
*/
Executable configure(String... args);
/**
* Executable interface for running configured commands
*/
interface Executable {
void run();
}
}Usage Example:
@AutoService(CliCommand.class)
public class CustomCommand implements CliCommand {
@Override
public String getName() {
return "custom";
}
@Override
public String getDescription() {
return "Custom Grid command for specialized functionality";
}
@Override
public Executable configure(String... args) {
return () -> {
// Custom command logic
System.out.println("Executing custom command");
};
}
}Base class for implementing custom session mapping strategies.
abstract class SessionMap implements Predicate<HttpRequest>, CommandHandler {
/**
* Add session to the session map
* @param session Session to add
* @return true if session was added successfully
*/
public abstract boolean add(Session session);
/**
* Retrieve session by ID
* @param id Session identifier
* @return Session instance
* @throws NoSuchSessionException if session not found
*/
public abstract Session get(SessionId id) throws NoSuchSessionException;
/**
* Remove session from map
* @param id Session identifier to remove
*/
public abstract void remove(SessionId id);
/**
* Check if request should be handled by this session map
* @param req HTTP request
* @return true if this session map should handle the request
*/
@Override
public abstract boolean test(HttpRequest req);
}Base class for implementing custom session distribution strategies.
abstract class Distributor implements Predicate<HttpRequest>, CommandHandler {
/**
* Create new session using available nodes
* @param payload New session request payload
* @return Created session
* @throws SessionNotCreatedException if session cannot be created
*/
public abstract Session newSession(NewSessionPayload payload) throws SessionNotCreatedException;
/**
* Register node with distributor
* @param node Node to add to available nodes
*/
public abstract void add(Node node);
/**
* Remove node from distributor
* @param nodeId Unique identifier of node to remove
*/
public abstract void remove(UUID nodeId);
/**
* Get current distributor status
* @return DistributorStatus with current state information
*/
public abstract DistributorStatus getStatus();
}Base class for implementing custom node behavior and session management.
abstract class Node implements Predicate<HttpRequest>, CommandHandler {
/**
* Create new session on this node
* @param capabilities Desired browser capabilities
* @return Session if created successfully, empty otherwise
*/
public abstract Optional<Session> newSession(Capabilities capabilities);
/**
* Execute WebDriver command for existing session
* @param req HTTP request containing WebDriver command
* @param resp HTTP response to populate
*/
public abstract void executeWebDriverCommand(HttpRequest req, HttpResponse resp);
/**
* Get existing session by ID
* @param id Session identifier
* @return Session instance
* @throws NoSuchSessionException if session not found on this node
*/
public abstract Session getSession(SessionId id) throws NoSuchSessionException;
/**
* Stop session and clean up resources
* @param id Session identifier to stop
* @throws NoSuchSessionException if session not found
*/
public abstract void stop(SessionId id) throws NoSuchSessionException;
/**
* Check if this node owns the specified session
* @param id Session identifier to check
* @return true if this node owns the session
*/
protected abstract boolean isSessionOwner(SessionId id);
/**
* Check if node supports given capabilities
* @param capabilities Browser capabilities to check
* @return true if node can handle these capabilities
*/
public abstract boolean isSupporting(Capabilities capabilities);
/**
* Get current node status including capacity and active sessions
* @return NodeStatus with current state
*/
public abstract NodeStatus getStatus();
}The Grid server uses Java ServiceLoader for automatic discovery of extensions.
Use Google AutoService annotation for automatic service registration:
// Automatic service registration
@AutoService(CliCommand.class)
public class CustomCommand implements CliCommand { ... }
@AutoService(SessionFactory.class)
public class CustomSessionFactory implements SessionFactory { ... }The Grid server automatically discovers registered services:
// CLI command discovery (in Main.java)
Set<CliCommand> commands = new TreeSet<>(comparing(CliCommand::getName));
ServiceLoader.load(CliCommand.class).forEach(commands::add);
// Session factory discovery
ServiceLoader<SessionFactory> factories = ServiceLoader.load(SessionFactory.class);Add support for a custom browser by implementing SessionFactory:
@AutoService(SessionFactory.class)
public class CustomBrowserFactory implements SessionFactory {
@Override
public boolean isSupporting(Capabilities capabilities) {
return "myCustomBrowser".equals(capabilities.getBrowserName());
}
@Override
public Optional<ActiveSession> apply(Set<Dialect> downstreamDialects, Capabilities capabilities) {
WebDriver driver = new CustomBrowserDriver(capabilities);
return Optional.of(new CustomActiveSession(driver, capabilities));
}
}Implement custom session distribution logic:
public class LoadBalancedDistributor extends Distributor {
@Override
public Session newSession(NewSessionPayload payload) throws SessionNotCreatedException {
Node selectedNode = selectNodeByLoad(payload.getDesiredCapabilities());
return selectedNode.newSession(payload.getDesiredCapabilities())
.orElseThrow(() -> new SessionNotCreatedException("No capacity available"));
}
private Node selectNodeByLoad(Capabilities capabilities) {
// Custom load balancing logic
return nodes.stream()
.filter(node -> node.isSupporting(capabilities))
.min(Comparator.comparing(this::getCurrentLoad))
.orElse(null);
}
}Add custom administrative commands:
@AutoService(CliCommand.class)
public class HealthCheckCommand implements CliCommand {
@Override
public String getName() {
return "healthcheck";
}
@Override
public String getDescription() {
return "Perform health check on Grid components";
}
@Override
public Executable configure(String... args) {
return () -> {
// Health check implementation
performHealthCheck();
};
}
}Extensions can access the same configuration system as core components:
// Access configuration in extensions
Config config = new CompoundConfig(
new EnvConfig(),
new SystemPropertyConfig(),
new DefaultConfig()
);
String customValue = config.get("custom", "setting");
int customPort = config.getInt("custom", "port");
boolean customFlag = config.getBool("custom", "enabled");Install with Tessl CLI
npx tessl i tessl/maven-org-seleniumhq-selenium--selenium-server