Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions java/src/org/openqa/selenium/docker/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ java_library(
"//java/src/org/openqa/selenium/json",
"//java/src/org/openqa/selenium/remote/http",
artifact("com.google.guava:guava"),
artifact("org.jspecify:jspecify"),
],
)
6 changes: 4 additions & 2 deletions java/src/org/openqa/selenium/docker/ContainerConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.jspecify.annotations.Nullable;
import org.openqa.selenium.Beta;
import org.openqa.selenium.internal.Multimap;
import org.openqa.selenium.internal.Require;
Expand All @@ -44,7 +45,7 @@ public class ContainerConfig {
private final long shmSize;
private final Map<String, Object> hostConfig;
private final Map<String, String> labels;
private final String name;
private final @Nullable String name;

public ContainerConfig(
Image image,
Expand Down Expand Up @@ -120,7 +121,7 @@ public ContainerConfig(
long shmSize,
Map<String, Object> hostConfig,
Map<String, String> labels,
String name) {
@Nullable String name) {
this.image = image;
this.portBindings = portBindings;
this.envVars = envVars;
Expand All @@ -134,6 +135,7 @@ public ContainerConfig(
this.name = name;
}

@Nullable
public String getName() {
return this.name;
}
Expand Down
4 changes: 2 additions & 2 deletions java/src/org/openqa/selenium/docker/ContainerInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ public class ContainerInfo {
private final ContainerId id;
private final List<Map<String, Object>> mountedVolumes;
private final String networkName;
private Map<String, Object> hostConfig;
private Map<String, String> labels;
private final Map<String, Object> hostConfig;
private final Map<String, String> labels;

public ContainerInfo(
ContainerId id,
Expand Down
2 changes: 1 addition & 1 deletion java/src/org/openqa/selenium/docker/ContainerLogs.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@ public ContainerId getId() {

@Override
public String toString() {
return "ContainerInfo{" + "containerLogs=" + logLines.toString() + ", id=" + id + '}';
return String.format("ContainerInfo{containerLogs=%s, id=%s}", logLines, id);
}
}
6 changes: 4 additions & 2 deletions java/src/org/openqa/selenium/docker/Device.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package org.openqa.selenium.docker;

import java.util.Objects;
import org.jspecify.annotations.Nullable;

public class Device {

Expand All @@ -33,8 +34,9 @@ private Device(String pathOnHost, String pathInContainer, String cgroupPermissio
this.cgroupPermissions = cgroupPermissions;
}

public static Device device(String pathOnHost, String pathInContainer, String cgroupPermissions) {
if (Objects.isNull(cgroupPermissions) || cgroupPermissions.trim().isEmpty()) {
public static Device device(
String pathOnHost, String pathInContainer, @Nullable String cgroupPermissions) {
if (cgroupPermissions == null || cgroupPermissions.trim().isEmpty()) {
cgroupPermissions = "crw";
}
return new Device(pathOnHost, pathInContainer, cgroupPermissions);
Expand Down
5 changes: 3 additions & 2 deletions java/src/org/openqa/selenium/docker/Docker.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import java.util.Optional;
import java.util.logging.Logger;
import org.jspecify.annotations.Nullable;
import org.openqa.selenium.internal.Require;
import org.openqa.selenium.remote.http.HttpHandler;

Expand All @@ -27,7 +28,7 @@ public class Docker {
private static final Logger LOG = Logger.getLogger(Docker.class.getName());
protected final HttpHandler client;
private volatile Optional<DockerProtocol> dockerClient;
private final String apiVersion;
private final @Nullable String apiVersion;

public Docker(HttpHandler client) {
this(client, null);
Expand All @@ -40,7 +41,7 @@ public Docker(HttpHandler client) {
* @param apiVersion Optional API version to use (e.g., "1.40" or "1.44"). If null, the version
* will be auto-detected.
*/
public Docker(HttpHandler client, String apiVersion) {
public Docker(HttpHandler client, @Nullable String apiVersion) {
this.client = Require.nonNull("HTTP client", client);
this.apiVersion = apiVersion;
this.dockerClient = Optional.empty();
Expand Down
7 changes: 0 additions & 7 deletions java/src/org/openqa/selenium/docker/Version.java
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,7 @@ private int compare(String[] ours, String[] theirs, int index) {
return Long.compare(mine, others);
} catch (NumberFormatException e) {
String mine = index < ours.length ? ours[index] : "";
if (mine == null) {
mine = "";
}
String others = index < theirs.length ? theirs[index] : "";
if (others == null) {
others = "";
}

return mine.compareTo(others);
}
}
Expand Down
3 changes: 2 additions & 1 deletion java/src/org/openqa/selenium/docker/VersionCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import org.jspecify.annotations.Nullable;
import org.openqa.selenium.docker.client.DockerClient;
import org.openqa.selenium.internal.Require;
import org.openqa.selenium.json.Json;
Expand Down Expand Up @@ -62,7 +63,7 @@ public VersionCommand(HttpHandler handler) {
* @param requestedVersion The API version to use (e.g., "1.40" or "1.44")
* @return Optional containing the DockerProtocol implementation if the version is supported
*/
public Optional<DockerProtocol> getDockerProtocol(String requestedVersion) {
public Optional<DockerProtocol> getDockerProtocol(@Nullable String requestedVersion) {
if (requestedVersion == null || requestedVersion.isEmpty()) {
return getDockerProtocol();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package org.openqa.selenium.docker.client;

import java.util.logging.Logger;
import org.openqa.selenium.internal.Require;

/**
* Factory for creating API version-specific adapters.
Expand Down Expand Up @@ -45,9 +46,7 @@ class AdapterFactory {
* @throws IllegalArgumentException if apiVersion is null or empty
*/
public static ApiVersionAdapter createAdapter(String apiVersion) {
if (apiVersion == null || apiVersion.trim().isEmpty()) {
throw new IllegalArgumentException("API version cannot be null or empty");
}
Require.nonBlank("API version", apiVersion);

// API v1.48+ uses the latest adapter with multi-platform and gateway priority support
if (compareVersions(apiVersion, "1.48") >= 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package org.openqa.selenium.docker.client;

import java.util.Map;
import org.jspecify.annotations.Nullable;

/**
* Adapter interface for handling API version-specific differences in Docker Engine API.
Expand Down
14 changes: 4 additions & 10 deletions java/src/org/openqa/selenium/docker/client/CreateContainer.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,13 @@

package org.openqa.selenium.docker.client;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.openqa.selenium.json.Json.JSON_UTF_8;
import static org.openqa.selenium.json.Json.MAP_TYPE;
import static org.openqa.selenium.remote.http.Contents.asJson;
import static org.openqa.selenium.remote.http.HttpMethod.POST;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Map;
import java.util.logging.Logger;
Expand All @@ -37,7 +36,6 @@
import org.openqa.selenium.internal.Require;
import org.openqa.selenium.json.Json;
import org.openqa.selenium.json.JsonException;
import org.openqa.selenium.remote.http.Contents;
import org.openqa.selenium.remote.http.HttpHandler;
import org.openqa.selenium.remote.http.HttpRequest;
import org.openqa.selenium.remote.http.HttpResponse;
Expand Down Expand Up @@ -69,12 +67,8 @@ public Container apply(ContainerConfig info) {
String url = String.format("/v%s/containers/create", apiVersion);
if (info.getName() != null && !info.getName().trim().isEmpty()) {
String containerName = info.getName().trim();
try {
String encodedName = URLEncoder.encode(containerName, StandardCharsets.UTF_8.toString());
url += "?name=" + encodedName;
} catch (UnsupportedEncodingException e) {
throw new DockerException("Failed to encode container name: " + containerName, e);
}
String encodedName = URLEncoder.encode(containerName, UTF_8);
url += "?name=" + encodedName;
}

HttpResponse res =
Expand All @@ -87,7 +81,7 @@ public Container apply(ContainerConfig info) {
info);

try {
Map<String, Object> rawContainer = JSON.toType(Contents.string(res), MAP_TYPE);
Map<String, Object> rawContainer = JSON.toType(res.contentAsString(), MAP_TYPE);

if (!(rawContainer.get("Id") instanceof String)) {
throw new DockerException("Unable to read container id: " + rawContainer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ public ContainerInfo apply(ContainerId id) {
if (res.getStatus() != HTTP_OK) {
LOG.warning("Unable to inspect container " + id);
}
Map<String, Object> rawInspectInfo = JSON.toType(Contents.string(res), MAP_TYPE);
Map<String, Object> inspectInfoMap = JSON.toType(Contents.string(res), MAP_TYPE);

// Adapt response to handle API version differences (e.g., deprecated fields in v1.44+)
rawInspectInfo = adapter.adaptContainerInspectResponse(rawInspectInfo);
Map<String, Object> rawInspectInfo = adapter.adaptContainerInspectResponse(inspectInfoMap);
Map<String, Object> networkSettings =
(Map<String, Object>) rawInspectInfo.get("NetworkSettings");
Map<String, Object> networks = (Map<String, Object>) networkSettings.get("Networks");
Expand Down
13 changes: 4 additions & 9 deletions java/src/org/openqa/selenium/docker/client/V140Adapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import org.openqa.selenium.internal.Require;

/**
* Adapter for Docker API versions 1.40 through 1.43.
Expand All @@ -46,9 +47,7 @@ public V140Adapter(String apiVersion) {

@Override
public Map<String, Object> adaptImageResponse(Map<String, Object> response) {
if (response == null) {
return response;
}
Require.nonNull("Image response", response);

Map<String, Object> adapted = new HashMap<>(response);

Expand All @@ -64,9 +63,7 @@ public Map<String, Object> adaptImageResponse(Map<String, Object> response) {

@Override
public Map<String, Object> adaptContainerCreateRequest(Map<String, Object> request) {
if (request == null) {
return request;
}
Require.nonNull("Container Create Request", request);

// v1.40-1.43 supports single network endpoint
// If multiple networks are specified, log a warning and use only the first one
Expand Down Expand Up @@ -94,9 +91,7 @@ public Map<String, Object> adaptContainerCreateRequest(Map<String, Object> reque

@Override
public Map<String, Object> adaptContainerInspectResponse(Map<String, Object> response) {
if (response == null) {
return response;
}
Require.nonNull("Container Inspect Response", response);

// v1.40-1.43 responses are already in the expected format
// No adaptation needed for container inspection
Expand Down
13 changes: 4 additions & 9 deletions java/src/org/openqa/selenium/docker/client/V144Adapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import org.openqa.selenium.internal.Require;

/**
* Adapter for Docker API version 1.44 and later.
Expand Down Expand Up @@ -48,9 +49,7 @@ public V144Adapter(String apiVersion) {

@Override
public Map<String, Object> adaptImageResponse(Map<String, Object> response) {
if (response == null) {
return response;
}
Require.nonNull("Image response", response);

Map<String, Object> adapted = new HashMap<>(response);

Expand All @@ -75,9 +74,7 @@ public Map<String, Object> adaptImageResponse(Map<String, Object> response) {

@Override
public Map<String, Object> adaptContainerCreateRequest(Map<String, Object> request) {
if (request == null) {
return request;
}
Require.nonNull("Container Create Request", request);

// v1.44+ supports multiple network endpoints
// No adaptation needed, but we can log for debugging
Expand Down Expand Up @@ -106,9 +103,7 @@ public Map<String, Object> adaptContainerCreateRequest(Map<String, Object> reque

@Override
public Map<String, Object> adaptContainerInspectResponse(Map<String, Object> response) {
if (response == null) {
return response;
}
Require.nonNull("Container Inspect Response", response);

Map<String, Object> adapted = new HashMap<>(response);

Expand Down
14 changes: 4 additions & 10 deletions java/src/org/openqa/selenium/docker/client/V148Adapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import org.openqa.selenium.internal.Require;

/**
* Adapter for Docker API version 1.48 and later.
Expand Down Expand Up @@ -49,9 +50,7 @@ public V148Adapter(String apiVersion) {

@Override
public Map<String, Object> adaptImageResponse(Map<String, Object> response) {
if (response == null) {
return response;
}
Require.nonNull("Image response", response);

Map<String, Object> adapted = new HashMap<>(response);

Expand Down Expand Up @@ -109,9 +108,7 @@ public Map<String, Object> adaptImageResponse(Map<String, Object> response) {

@Override
public Map<String, Object> adaptContainerCreateRequest(Map<String, Object> request) {
if (request == null) {
return request;
}
Require.nonNull("Container Create Request", request);

Map<String, Object> adapted = new HashMap<>(request);

Expand All @@ -121,7 +118,6 @@ public Map<String, Object> adaptContainerCreateRequest(Map<String, Object> reque
Map<String, Object> hostConfig = (Map<String, Object>) adapted.get("HostConfig");

if (hostConfig != null) {
@SuppressWarnings("unchecked")
Object mounts = hostConfig.get("Mounts");

if (mounts instanceof Iterable) {
Expand Down Expand Up @@ -206,9 +202,7 @@ public Map<String, Object> adaptContainerCreateRequest(Map<String, Object> reque

@Override
public Map<String, Object> adaptContainerInspectResponse(Map<String, Object> response) {
if (response == null) {
return response;
}
Require.nonNull("Container Inspect Response", response);

Map<String, Object> adapted = new HashMap<>(response);

Expand Down
21 changes: 21 additions & 0 deletions java/src/org/openqa/selenium/docker/client/package-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

@NullMarked
package org.openqa.selenium.docker.client;

import org.jspecify.annotations.NullMarked;
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public Set<String> getRepoTags() {
return repoTags;
}

@SuppressWarnings({"unused", "DataFlowIssue"})
static ImageSummary fromJson(JsonInput input) {
input.beginObject();

Expand Down
Loading
Loading