diff --git a/java/src/org/openqa/selenium/MutableCapabilities.java b/java/src/org/openqa/selenium/MutableCapabilities.java
index 5d9081eeb66d7..37d68ffceb868 100644
--- a/java/src/org/openqa/selenium/MutableCapabilities.java
+++ b/java/src/org/openqa/selenium/MutableCapabilities.java
@@ -74,7 +74,7 @@ public void setCapability(String capabilityName, boolean value) {
setCapability(capabilityName, (Object) value);
}
- public void setCapability(String capabilityName, String value) {
+ public void setCapability(String capabilityName, @Nullable String value) {
setCapability(capabilityName, (Object) value);
}
diff --git a/java/src/org/openqa/selenium/SessionNotCreatedException.java b/java/src/org/openqa/selenium/SessionNotCreatedException.java
index 30bb9d6c2ab21..47e397dab835f 100644
--- a/java/src/org/openqa/selenium/SessionNotCreatedException.java
+++ b/java/src/org/openqa/selenium/SessionNotCreatedException.java
@@ -17,16 +17,18 @@
package org.openqa.selenium;
+import org.jspecify.annotations.Nullable;
+
/** Indicates that a session could not be created. */
public class SessionNotCreatedException extends WebDriverException {
- public SessionNotCreatedException(String msg) {
+ public SessionNotCreatedException(@Nullable String msg) {
super(
"Could not start a new session. "
+ msg
+ (msg != null && msg.contains("Host info") ? "" : " \n" + getHostInformation()));
}
- public SessionNotCreatedException(String msg, Throwable cause) {
+ public SessionNotCreatedException(@Nullable String msg, @Nullable Throwable cause) {
super(
"Could not start a new session. "
+ msg
diff --git a/java/src/org/openqa/selenium/cli/BUILD.bazel b/java/src/org/openqa/selenium/cli/BUILD.bazel
index 663b7e81ac101..1a621c3b5000e 100644
--- a/java/src/org/openqa/selenium/cli/BUILD.bazel
+++ b/java/src/org/openqa/selenium/cli/BUILD.bazel
@@ -9,5 +9,6 @@ java_library(
deps = [
"//java/src/org/openqa/selenium:core",
"//java/src/org/openqa/selenium/grid/config",
+ "@maven//:org_jspecify_jspecify",
],
)
diff --git a/java/src/org/openqa/selenium/cli/package-info.java b/java/src/org/openqa/selenium/cli/package-info.java
index 2e3f8d91a4c4a..68be944df3d78 100644
--- a/java/src/org/openqa/selenium/cli/package-info.java
+++ b/java/src/org/openqa/selenium/cli/package-info.java
@@ -44,4 +44,7 @@
*
Ultimately, this means that flag objects have all (most?) fields annotated with JCommander's
* {@link com.beust.jcommander.Parameter} annotation as well as {@code ConfigValue}.
*/
+@NullMarked
package org.openqa.selenium.cli;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/java/src/org/openqa/selenium/edge/package-info.java b/java/src/org/openqa/selenium/edge/package-info.java
index 0ecb0d05d4dd3..5f1ac3fda669a 100644
--- a/java/src/org/openqa/selenium/edge/package-info.java
+++ b/java/src/org/openqa/selenium/edge/package-info.java
@@ -15,35 +15,6 @@
// specific language governing permissions and limitations
// under the License.
-/**
- * Mechanisms to configure and run selenium via the command line. There are two key classes {@link
- * org.openqa.selenium.cli.CliCommand} and {@link org.openqa.selenium.grid.config.HasRoles}.
- * Ultimately, these are used to build a {@link org.openqa.selenium.grid.config.Config} instance,
- * for which there are strongly-typed role-specific classes that use a {@code Config}, such as
- * {@link org.openqa.selenium.grid.node.docker.DockerOptions}.
- *
- *
Assuming your {@code CliCommand} extends {@link org.openqa.selenium.grid.TemplateGridCommand},
- * the process for building the set of flags to use is:
- *
- *
- *
The default flags are added (these are {@link org.openqa.selenium.grid.server.HelpFlags}
- * and {@link org.openqa.selenium.grid.config.ConfigFlags}
- *
{@link java.util.ServiceLoader} is used to find all implementations of {@link
- * org.openqa.selenium.grid.config.HasRoles} where {@link
- * org.openqa.selenium.grid.config.HasRoles#getRoles()} is contained within {@link
- * org.openqa.selenium.cli.CliCommand#getConfigurableRoles()}.
- *
Finally all flags returned by {@link
- * org.openqa.selenium.grid.TemplateGridCommand#getFlagObjects()} are added.
- *
- *
- *
The flags are then used by JCommander to parse the command arguments. Once that's done, the
- * raw flags are converted to a {@link org.openqa.selenium.grid.config.Config} by combining all of
- * the flag objects with system properties and environment variables. This implies that each flag
- * object has annotated each field with {@link org.openqa.selenium.grid.config.ConfigValue}.
- *
- *
Ultimately, this means that flag objects have all (most?) fields annotated with JCommander's
- * {@link com.beust.jcommander.Parameter} annotation as well as {@code ConfigValue}.
- */
@NullMarked
package org.openqa.selenium.edge;
diff --git a/java/src/org/openqa/selenium/federatedcredentialmanagement/package-info.java b/java/src/org/openqa/selenium/federatedcredentialmanagement/package-info.java
index 359e361a886f5..f2a1ac6f33779 100644
--- a/java/src/org/openqa/selenium/federatedcredentialmanagement/package-info.java
+++ b/java/src/org/openqa/selenium/federatedcredentialmanagement/package-info.java
@@ -15,35 +15,6 @@
// specific language governing permissions and limitations
// under the License.
-/**
- * Mechanisms to configure and run selenium via the command line. There are two key classes {@link
- * org.openqa.selenium.cli.CliCommand} and {@link org.openqa.selenium.grid.config.HasRoles}.
- * Ultimately, these are used to build a {@link org.openqa.selenium.grid.config.Config} instance,
- * for which there are strongly-typed role-specific classes that use a {@code Config}, such as
- * {@link org.openqa.selenium.grid.node.docker.DockerOptions}.
- *
- *
Assuming your {@code CliCommand} extends {@link org.openqa.selenium.grid.TemplateGridCommand},
- * the process for building the set of flags to use is:
- *
- *
- *
The default flags are added (these are {@link org.openqa.selenium.grid.server.HelpFlags}
- * and {@link org.openqa.selenium.grid.config.ConfigFlags}
- *
{@link java.util.ServiceLoader} is used to find all implementations of {@link
- * org.openqa.selenium.grid.config.HasRoles} where {@link
- * org.openqa.selenium.grid.config.HasRoles#getRoles()} is contained within {@link
- * org.openqa.selenium.cli.CliCommand#getConfigurableRoles()}.
- *
Finally all flags returned by {@link
- * org.openqa.selenium.grid.TemplateGridCommand#getFlagObjects()} are added.
- *
- *
- *
The flags are then used by JCommander to parse the command arguments. Once that's done, the
- * raw flags are converted to a {@link org.openqa.selenium.grid.config.Config} by combining all of
- * the flag objects with system properties and environment variables. This implies that each flag
- * object has annotated each field with {@link org.openqa.selenium.grid.config.ConfigValue}.
- *
- *
Ultimately, this means that flag objects have all (most?) fields annotated with JCommander's
- * {@link com.beust.jcommander.Parameter} annotation as well as {@code ConfigValue}.
- */
@NullMarked
package org.openqa.selenium.federatedcredentialmanagement;
diff --git a/java/src/org/openqa/selenium/grid/BUILD.bazel b/java/src/org/openqa/selenium/grid/BUILD.bazel
index 2b4e66a98c449..dcc5b7f0e2c82 100644
--- a/java/src/org/openqa/selenium/grid/BUILD.bazel
+++ b/java/src/org/openqa/selenium/grid/BUILD.bazel
@@ -89,6 +89,7 @@ java_library(
"//java/src/org/openqa/selenium/remote/http",
artifact("com.beust:jcommander"),
artifact("com.google.guava:guava"),
+ artifact("org.jspecify:jspecify"),
],
)
@@ -171,6 +172,7 @@ java_export(
":base-command",
"//java/src/org/openqa/selenium/cli",
"//java/src/org/openqa/selenium/grid/config",
+ artifact("org.jspecify:jspecify"),
],
)
diff --git a/java/src/org/openqa/selenium/grid/TemplateGridServerCommand.java b/java/src/org/openqa/selenium/grid/TemplateGridServerCommand.java
index 0c8e82b4b692a..963b445d6435b 100644
--- a/java/src/org/openqa/selenium/grid/TemplateGridServerCommand.java
+++ b/java/src/org/openqa/selenium/grid/TemplateGridServerCommand.java
@@ -26,6 +26,7 @@
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
+import org.jspecify.annotations.Nullable;
import org.openqa.selenium.grid.config.CompoundConfig;
import org.openqa.selenium.grid.config.Config;
import org.openqa.selenium.grid.config.MemoizedConfig;
@@ -91,24 +92,26 @@ protected static Routable baseRoute(String prefix, Route route) {
protected abstract Handlers createHandlers(Config config);
- public abstract static class Handlers implements Closeable {
+ protected abstract static class Handlers implements Closeable {
public final HttpHandler httpHandler;
public final BiFunction, Optional>>
websocketHandler;
/** Optional resolver for direct TCP tunnel of WebSocket connections. May be null. */
- public final Function> tcpTunnelResolver;
+ public final @Nullable Function> tcpTunnelResolver;
- public Handlers(
+ protected Handlers(
HttpHandler http,
- BiFunction, Optional>> websocketHandler) {
+ @Nullable BiFunction, Optional>>
+ websocketHandler) {
this(http, websocketHandler, null);
}
- public Handlers(
+ protected Handlers(
HttpHandler http,
- BiFunction, Optional>> websocketHandler,
- Function> tcpTunnelResolver) {
+ @Nullable BiFunction, Optional>>
+ websocketHandler,
+ @Nullable Function> tcpTunnelResolver) {
this.httpHandler = Require.nonNull("HTTP handler", http);
this.websocketHandler =
websocketHandler == null ? (str, sink) -> Optional.empty() : websocketHandler;
diff --git a/java/src/org/openqa/selenium/grid/commands/InfoCommand.java b/java/src/org/openqa/selenium/grid/commands/InfoCommand.java
index 41db14a988552..3fcbf02e2df51 100644
--- a/java/src/org/openqa/selenium/grid/commands/InfoCommand.java
+++ b/java/src/org/openqa/selenium/grid/commands/InfoCommand.java
@@ -34,7 +34,6 @@
import java.util.Collections;
import java.util.Set;
import java.util.regex.Pattern;
-import org.jspecify.annotations.NonNull;
import org.openqa.selenium.cli.CliCommand;
import org.openqa.selenium.cli.WrappedPrintWriter;
import org.openqa.selenium.grid.config.Role;
@@ -169,7 +168,6 @@ private String readContent(String path) throws IOException {
return formattedText.toString();
}
- @NonNull
private String unformattedText(String path) throws IOException {
try (InputStream in = getClass().getClassLoader().getResourceAsStream(path)) {
requireNonNull(in, () -> "Resource is not found in classpath: " + path);
diff --git a/java/src/org/openqa/selenium/grid/commands/package-info.java b/java/src/org/openqa/selenium/grid/commands/package-info.java
new file mode 100644
index 0000000000000..d57f8ca1ce6b9
--- /dev/null
+++ b/java/src/org/openqa/selenium/grid/commands/package-info.java
@@ -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.grid.commands;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/java/src/org/openqa/selenium/grid/component/BUILD.bazel b/java/src/org/openqa/selenium/grid/component/BUILD.bazel
index e4692451e6921..8160be880f20f 100644
--- a/java/src/org/openqa/selenium/grid/component/BUILD.bazel
+++ b/java/src/org/openqa/selenium/grid/component/BUILD.bazel
@@ -1,3 +1,4 @@
+load("@rules_jvm_external//:defs.bzl", "artifact")
load("//java:defs.bzl", "java_library")
java_library(
@@ -10,5 +11,6 @@ java_library(
],
deps = [
"//java/src/org/openqa/selenium:core",
+ artifact("org.jspecify:jspecify"),
],
)
diff --git a/java/src/org/openqa/selenium/grid/component/package-info.java b/java/src/org/openqa/selenium/grid/component/package-info.java
new file mode 100644
index 0000000000000..eaa2be1630be9
--- /dev/null
+++ b/java/src/org/openqa/selenium/grid/component/package-info.java
@@ -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.grid.component;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/java/src/org/openqa/selenium/grid/config/AnnotatedConfig.java b/java/src/org/openqa/selenium/grid/config/AnnotatedConfig.java
index f963f87436b7f..7476063aeab3d 100644
--- a/java/src/org/openqa/selenium/grid/config/AnnotatedConfig.java
+++ b/java/src/org/openqa/selenium/grid/config/AnnotatedConfig.java
@@ -35,6 +35,7 @@
import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import org.jspecify.annotations.Nullable;
import org.openqa.selenium.internal.Require;
/**
@@ -108,7 +109,8 @@ public AnnotatedConfig(Object obj, Set cliArgs, boolean includeCliArgs)
this.config = values;
}
- private String getSingleValue(Object value) {
+ @Nullable
+ private String getSingleValue(@Nullable Object value) {
if (value == null) {
return null;
}
diff --git a/java/src/org/openqa/selenium/grid/config/BUILD.bazel b/java/src/org/openqa/selenium/grid/config/BUILD.bazel
index e2be2a4b14de7..92344f484eb73 100644
--- a/java/src/org/openqa/selenium/grid/config/BUILD.bazel
+++ b/java/src/org/openqa/selenium/grid/config/BUILD.bazel
@@ -22,5 +22,6 @@ java_library(
artifact("com.beust:jcommander"),
artifact("com.google.guava:guava"),
artifact("org.tomlj:tomlj"),
+ artifact("org.jspecify:jspecify"),
],
)
diff --git a/java/src/org/openqa/selenium/grid/config/ConcatenatingConfig.java b/java/src/org/openqa/selenium/grid/config/ConcatenatingConfig.java
index 56e7b6e72bbd2..6f9aa1691624f 100644
--- a/java/src/org/openqa/selenium/grid/config/ConcatenatingConfig.java
+++ b/java/src/org/openqa/selenium/grid/config/ConcatenatingConfig.java
@@ -26,6 +26,7 @@
import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import org.jspecify.annotations.Nullable;
import org.openqa.selenium.internal.Require;
public class ConcatenatingConfig implements Config {
@@ -34,7 +35,7 @@ public class ConcatenatingConfig implements Config {
private final char separator;
private final Map values;
- public ConcatenatingConfig(String prefix, char separator, Map, ?> values) {
+ public ConcatenatingConfig(@Nullable String prefix, char separator, Map, ?> values) {
this.prefix = prefix == null || prefix.isEmpty() ? "" : (prefix + separator);
this.separator = separator;
diff --git a/java/src/org/openqa/selenium/grid/config/Config.java b/java/src/org/openqa/selenium/grid/config/Config.java
index c4eb7e2263519..16424bd5a7aa1 100644
--- a/java/src/org/openqa/selenium/grid/config/Config.java
+++ b/java/src/org/openqa/selenium/grid/config/Config.java
@@ -24,6 +24,7 @@
import java.util.Set;
import java.util.logging.Logger;
import java.util.stream.Collectors;
+import org.jspecify.annotations.Nullable;
import org.openqa.selenium.json.Json;
public interface Config {
@@ -34,7 +35,7 @@ public interface Config {
Optional> getAll(String section, String option);
- default Optional get(String section, String option) {
+ default Optional<@Nullable String> get(String section, String option) {
return getAll(section, option).map(items -> items.isEmpty() ? null : items.get(0));
}
diff --git a/java/src/org/openqa/selenium/grid/config/ConfigException.java b/java/src/org/openqa/selenium/grid/config/ConfigException.java
index cf4a0bcd818a7..e396ca26ae671 100644
--- a/java/src/org/openqa/selenium/grid/config/ConfigException.java
+++ b/java/src/org/openqa/selenium/grid/config/ConfigException.java
@@ -17,13 +17,15 @@
package org.openqa.selenium.grid.config;
+import org.jspecify.annotations.Nullable;
+
public class ConfigException extends RuntimeException {
public ConfigException(String message, Object... args) {
super(String.format(message, args));
}
- public ConfigException(Throwable cause) {
+ public ConfigException(@Nullable Throwable cause) {
super(cause);
}
}
diff --git a/java/src/org/openqa/selenium/grid/config/MemoizedConfig.java b/java/src/org/openqa/selenium/grid/config/MemoizedConfig.java
index a111b5af68759..34c8a34826c3a 100644
--- a/java/src/org/openqa/selenium/grid/config/MemoizedConfig.java
+++ b/java/src/org/openqa/selenium/grid/config/MemoizedConfig.java
@@ -24,6 +24,7 @@
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
+import org.jspecify.annotations.Nullable;
import org.openqa.selenium.internal.Require;
public class MemoizedConfig implements Config {
@@ -61,7 +62,7 @@ public Optional> getAll(String section, String option) {
}
@Override
- public Optional get(String section, String option) {
+ public Optional<@Nullable String> get(String section, String option) {
Require.nonNull("Section name", section);
Require.nonNull("Option", option);
@@ -94,7 +95,7 @@ public X getClass(String section, String option, Class typeOfX, String de
Require.nonNull("Type to load", typeOfX);
Require.nonNull("Default class name", defaultClassName);
- AtomicReference thrown = new AtomicReference<>();
+ AtomicReference<@Nullable Exception> thrown = new AtomicReference<>();
Object value =
seenClasses.computeIfAbsent(
new Key(section, option, typeOfX.toGenericString(), defaultClassName),
diff --git a/java/src/org/openqa/selenium/grid/config/package-info.java b/java/src/org/openqa/selenium/grid/config/package-info.java
new file mode 100644
index 0000000000000..f1393763f3149
--- /dev/null
+++ b/java/src/org/openqa/selenium/grid/config/package-info.java
@@ -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.grid.config;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/java/src/org/openqa/selenium/grid/data/BUILD.bazel b/java/src/org/openqa/selenium/grid/data/BUILD.bazel
index 761bf51d92992..175cf061f84eb 100644
--- a/java/src/org/openqa/selenium/grid/data/BUILD.bazel
+++ b/java/src/org/openqa/selenium/grid/data/BUILD.bazel
@@ -1,3 +1,4 @@
+load("@rules_jvm_external//:defs.bzl", "artifact")
load("//java:defs.bzl", "java_library")
java_library(
@@ -15,5 +16,6 @@ java_library(
"//java/src/org/openqa/selenium/grid/security",
"//java/src/org/openqa/selenium/json",
"//java/src/org/openqa/selenium/remote",
+ artifact("org.jspecify:jspecify"),
],
)
diff --git a/java/src/org/openqa/selenium/grid/data/CapabilityCount.java b/java/src/org/openqa/selenium/grid/data/CapabilityCount.java
index f04af7680fc2c..4e2e877b8831a 100644
--- a/java/src/org/openqa/selenium/grid/data/CapabilityCount.java
+++ b/java/src/org/openqa/selenium/grid/data/CapabilityCount.java
@@ -60,6 +60,7 @@ private Object toJson() {
UNORDERED));
}
+ @SuppressWarnings({"unused", "DataFlowIssue"})
private static CapabilityCount fromJson(JsonInput input) {
Map toReturn = new HashMap<>();
diff --git a/java/src/org/openqa/selenium/grid/data/CreateSessionRequest.java b/java/src/org/openqa/selenium/grid/data/CreateSessionRequest.java
index e89ba10f81886..3c811f91af481 100644
--- a/java/src/org/openqa/selenium/grid/data/CreateSessionRequest.java
+++ b/java/src/org/openqa/selenium/grid/data/CreateSessionRequest.java
@@ -56,6 +56,7 @@ public Map getMetadata() {
return metadata;
}
+ @SuppressWarnings({"unused", "DataFlowIssue"})
private static CreateSessionRequest fromJson(JsonInput input) {
Set downstreamDialects = null;
Capabilities capabilities = null;
diff --git a/java/src/org/openqa/selenium/grid/data/CreateSessionResponse.java b/java/src/org/openqa/selenium/grid/data/CreateSessionResponse.java
index 10a0e24c9f585..0cf73a231d77a 100644
--- a/java/src/org/openqa/selenium/grid/data/CreateSessionResponse.java
+++ b/java/src/org/openqa/selenium/grid/data/CreateSessionResponse.java
@@ -52,6 +52,7 @@ private Map toJson() {
return unmodifiableMap(toReturn);
}
+ @SuppressWarnings({"unused", "DataFlowIssue"})
private static CreateSessionResponse fromJson(JsonInput input) {
Session session = null;
byte[] downstreamResponse = null;
diff --git a/java/src/org/openqa/selenium/grid/data/DistributorStatus.java b/java/src/org/openqa/selenium/grid/data/DistributorStatus.java
index d989ac3c842d5..208d5d6bceba2 100644
--- a/java/src/org/openqa/selenium/grid/data/DistributorStatus.java
+++ b/java/src/org/openqa/selenium/grid/data/DistributorStatus.java
@@ -52,6 +52,7 @@ private Map toJson() {
return Collections.singletonMap("nodes", getNodes());
}
+ @SuppressWarnings({"unused", "DataFlowIssue"})
private static DistributorStatus fromJson(JsonInput input) {
Set nodes = null;
diff --git a/java/src/org/openqa/selenium/grid/data/NewSessionErrorResponse.java b/java/src/org/openqa/selenium/grid/data/NewSessionErrorResponse.java
index 1cfba61c8e111..a7e9dc363c750 100644
--- a/java/src/org/openqa/selenium/grid/data/NewSessionErrorResponse.java
+++ b/java/src/org/openqa/selenium/grid/data/NewSessionErrorResponse.java
@@ -49,6 +49,7 @@ private Map toJson() {
return unmodifiableMap(toReturn);
}
+ @SuppressWarnings({"unused", "DataFlowIssue"})
private static NewSessionErrorResponse fromJson(JsonInput input) {
String message = null;
RequestId requestId = null;
diff --git a/java/src/org/openqa/selenium/grid/data/NewSessionResponse.java b/java/src/org/openqa/selenium/grid/data/NewSessionResponse.java
index c0a20a443f97a..5e7d7599909c1 100644
--- a/java/src/org/openqa/selenium/grid/data/NewSessionResponse.java
+++ b/java/src/org/openqa/selenium/grid/data/NewSessionResponse.java
@@ -60,6 +60,7 @@ private Map toJson() {
return unmodifiableMap(toReturn);
}
+ @SuppressWarnings({"unused", "DataFlowIssue"})
private static NewSessionResponse fromJson(JsonInput input) {
RequestId requestId = null;
Session session = null;
diff --git a/java/src/org/openqa/selenium/grid/data/NodeId.java b/java/src/org/openqa/selenium/grid/data/NodeId.java
index cae18693b8fa8..f5393d356a86a 100644
--- a/java/src/org/openqa/selenium/grid/data/NodeId.java
+++ b/java/src/org/openqa/selenium/grid/data/NodeId.java
@@ -64,6 +64,7 @@ private Object toJson() {
return uuid;
}
+ @SuppressWarnings({"unused"})
private static NodeId fromJson(UUID id) {
return new NodeId(id);
}
diff --git a/java/src/org/openqa/selenium/grid/data/NodeStatus.java b/java/src/org/openqa/selenium/grid/data/NodeStatus.java
index 010ed40b81281..aabf309d09a99 100644
--- a/java/src/org/openqa/selenium/grid/data/NodeStatus.java
+++ b/java/src/org/openqa/selenium/grid/data/NodeStatus.java
@@ -69,7 +69,8 @@ public NodeStatus(
this.osInfo = Require.nonNull("Node host OS info", osInfo);
}
- public static NodeStatus fromJson(JsonInput input) {
+ @SuppressWarnings({"unused", "DataFlowIssue"})
+ private static NodeStatus fromJson(JsonInput input) {
NodeId nodeId = null;
URI externalUri = null;
int maxSessions = 0;
diff --git a/java/src/org/openqa/selenium/grid/data/RequestId.java b/java/src/org/openqa/selenium/grid/data/RequestId.java
index 1b7ddd3fb3af6..7bdae7ca2c7c5 100644
--- a/java/src/org/openqa/selenium/grid/data/RequestId.java
+++ b/java/src/org/openqa/selenium/grid/data/RequestId.java
@@ -57,6 +57,7 @@ private Object toJson() {
return uuid;
}
+ @SuppressWarnings({"unused"})
private static RequestId fromJson(UUID id) {
return new RequestId(id);
}
diff --git a/java/src/org/openqa/selenium/grid/data/Session.java b/java/src/org/openqa/selenium/grid/data/Session.java
index 061cbfb6b212e..7d51e6023f0a3 100644
--- a/java/src/org/openqa/selenium/grid/data/Session.java
+++ b/java/src/org/openqa/selenium/grid/data/Session.java
@@ -90,6 +90,7 @@ private Map toJson() {
return unmodifiableMap(toReturn);
}
+ @SuppressWarnings({"unused", "DataFlowIssue"})
private static Session fromJson(JsonInput input) {
SessionId id = null;
URI uri = null;
@@ -101,7 +102,7 @@ private static Session fromJson(JsonInput input) {
while (input.hasNext()) {
switch (input.nextName()) {
case "capabilities":
- caps = ImmutableCapabilities.copyOf(input.read(Capabilities.class));
+ caps = ImmutableCapabilities.copyOf(input.readNonNull(Capabilities.class));
break;
case "sessionId":
diff --git a/java/src/org/openqa/selenium/grid/data/SessionClosedData.java b/java/src/org/openqa/selenium/grid/data/SessionClosedData.java
index 4fdfac5f10ebc..1dff56c87b150 100644
--- a/java/src/org/openqa/selenium/grid/data/SessionClosedData.java
+++ b/java/src/org/openqa/selenium/grid/data/SessionClosedData.java
@@ -22,6 +22,7 @@
import java.time.Instant;
import java.util.Map;
import java.util.TreeMap;
+import org.jspecify.annotations.Nullable;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.internal.Require;
import org.openqa.selenium.json.JsonException;
@@ -37,10 +38,10 @@ public class SessionClosedData {
private final SessionId sessionId;
private final SessionClosedReason reason;
- private final NodeId nodeId;
- private final URI nodeUri;
- private final Capabilities capabilities;
- private final Instant startTime;
+ private final @Nullable NodeId nodeId;
+ private final @Nullable URI nodeUri;
+ private final @Nullable Capabilities capabilities;
+ private final @Nullable Instant startTime;
private final Instant endTime;
/** Backward compatible constructor for existing code. */
@@ -52,10 +53,10 @@ public SessionClosedData(SessionId sessionId, SessionClosedReason reason) {
public SessionClosedData(
SessionId sessionId,
SessionClosedReason reason,
- NodeId nodeId,
- URI nodeUri,
- Capabilities capabilities,
- Instant startTime,
+ @Nullable NodeId nodeId,
+ @Nullable URI nodeUri,
+ @Nullable Capabilities capabilities,
+ @Nullable Instant startTime,
Instant endTime) {
this.sessionId = Require.nonNull("Session ID", sessionId);
this.reason = Require.nonNull("Reason", reason);
@@ -74,18 +75,22 @@ public SessionClosedReason getReason() {
return reason;
}
+ @Nullable
public NodeId getNodeId() {
return nodeId;
}
+ @Nullable
public URI getNodeUri() {
return nodeUri;
}
+ @Nullable
public Capabilities getCapabilities() {
return capabilities;
}
+ @Nullable
public Instant getStartTime() {
return startTime;
}
@@ -99,8 +104,9 @@ public Instant getEndTime() {
*
* @return the session duration, or null if start time was not recorded
*/
+ @Nullable
public Duration getSessionDuration() {
- if (startTime == null || endTime == null) {
+ if (startTime == null) {
return null;
}
return Duration.between(startTime, endTime);
@@ -132,6 +138,7 @@ private Map toJson() {
return result;
}
+ @SuppressWarnings({"unused", "DataFlowIssue"})
private static SessionClosedData fromJson(JsonInput input) {
SessionId sessionId = null;
SessionClosedReason reason = null;
diff --git a/java/src/org/openqa/selenium/grid/data/SessionCreatedData.java b/java/src/org/openqa/selenium/grid/data/SessionCreatedData.java
index e3900efb8eea3..82b6d7630053d 100644
--- a/java/src/org/openqa/selenium/grid/data/SessionCreatedData.java
+++ b/java/src/org/openqa/selenium/grid/data/SessionCreatedData.java
@@ -104,6 +104,7 @@ private Map toJson() {
return toReturn;
}
+ @SuppressWarnings({"unused", "DataFlowIssue"})
private static SessionCreatedData fromJson(JsonInput input) {
SessionId sessionId = null;
NodeId nodeId = null;
diff --git a/java/src/org/openqa/selenium/grid/data/SessionEventData.java b/java/src/org/openqa/selenium/grid/data/SessionEventData.java
index a2a7977e19142..ad3d8cb9bc131 100644
--- a/java/src/org/openqa/selenium/grid/data/SessionEventData.java
+++ b/java/src/org/openqa/selenium/grid/data/SessionEventData.java
@@ -22,6 +22,7 @@
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
+import org.jspecify.annotations.Nullable;
import org.openqa.selenium.internal.Require;
import org.openqa.selenium.json.JsonInput;
import org.openqa.selenium.remote.SessionId;
@@ -75,18 +76,18 @@ public class SessionEventData {
private final SessionId sessionId;
private final String eventType;
- private final NodeId nodeId;
- private final URI nodeUri;
+ private final @Nullable NodeId nodeId;
+ private final @Nullable URI nodeUri;
private final Instant timestamp;
private final Map payload;
public SessionEventData(
SessionId sessionId,
String eventType,
- NodeId nodeId,
- URI nodeUri,
+ @Nullable NodeId nodeId,
+ @Nullable URI nodeUri,
Instant timestamp,
- Map payload) {
+ @Nullable Map payload) {
this.sessionId = Require.nonNull("Session ID", sessionId);
this.eventType = Require.nonNull("Event type", eventType);
if (!eventType.matches("^[a-zA-Z][a-zA-Z0-9:._-]*$")) {
@@ -133,10 +134,12 @@ public String getEventType() {
return eventType;
}
+ @Nullable
public NodeId getNodeId() {
return nodeId;
}
+ @Nullable
public URI getNodeUri() {
return nodeUri;
}
@@ -165,6 +168,7 @@ public Object get(String key) {
* @param key the key to look up
* @return the string value, or null if not present or not a string
*/
+ @Nullable
public String getString(String key) {
Object value = payload.get(key);
return value instanceof String ? (String) value : null;
@@ -212,7 +216,7 @@ private Map toJson() {
return result;
}
- @SuppressWarnings("unchecked")
+ @SuppressWarnings({"unused", "DataFlowIssue"})
private static SessionEventData fromJson(JsonInput input) {
SessionId sessionId = null;
String eventType = null;
diff --git a/java/src/org/openqa/selenium/grid/data/SessionRemovalInfo.java b/java/src/org/openqa/selenium/grid/data/SessionRemovalInfo.java
index 6cbe86e8af98b..353771724db06 100644
--- a/java/src/org/openqa/selenium/grid/data/SessionRemovalInfo.java
+++ b/java/src/org/openqa/selenium/grid/data/SessionRemovalInfo.java
@@ -20,13 +20,14 @@
import java.net.URI;
import java.time.Duration;
import java.time.Instant;
+import org.jspecify.annotations.Nullable;
public class SessionRemovalInfo {
private final Instant removedAt;
private final String reason;
- private final URI nodeUri;
+ private final @Nullable URI nodeUri;
- public SessionRemovalInfo(String reason, URI nodeUri) {
+ public SessionRemovalInfo(String reason, @Nullable URI nodeUri) {
this.removedAt = Instant.now();
this.reason = reason;
this.nodeUri = nodeUri;
diff --git a/java/src/org/openqa/selenium/grid/data/SessionRequest.java b/java/src/org/openqa/selenium/grid/data/SessionRequest.java
index aa299de2a8129..3cd4f5f7572e5 100644
--- a/java/src/org/openqa/selenium/grid/data/SessionRequest.java
+++ b/java/src/org/openqa/selenium/grid/data/SessionRequest.java
@@ -164,6 +164,7 @@ private Map toJson() {
return unmodifiableMap(toReturn);
}
+ @SuppressWarnings({"unused", "DataFlowIssue"})
private static SessionRequest fromJson(JsonInput input) {
RequestId id = null;
Instant enqueued = null;
diff --git a/java/src/org/openqa/selenium/grid/data/SessionRequestCapability.java b/java/src/org/openqa/selenium/grid/data/SessionRequestCapability.java
index 7091e57cd2b61..c86c7a3199896 100644
--- a/java/src/org/openqa/selenium/grid/data/SessionRequestCapability.java
+++ b/java/src/org/openqa/selenium/grid/data/SessionRequestCapability.java
@@ -17,11 +17,9 @@
package org.openqa.selenium.grid.data;
-import static java.util.Collections.unmodifiableMap;
import static java.util.Collections.unmodifiableSet;
import java.lang.reflect.Type;
-import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Objects;
@@ -77,12 +75,10 @@ public int hashCode() {
}
private Map toJson() {
- Map toReturn = new HashMap<>();
- toReturn.put("requestId", requestId);
- toReturn.put("capabilities", desiredCapabilities);
- return unmodifiableMap(toReturn);
+ return Map.of("requestId", requestId, "capabilities", desiredCapabilities);
}
+ @SuppressWarnings({"unused", "DataFlowIssue"})
private static SessionRequestCapability fromJson(JsonInput input) {
RequestId id = null;
Set capabilities = null;
diff --git a/java/src/org/openqa/selenium/grid/data/Slot.java b/java/src/org/openqa/selenium/grid/data/Slot.java
index 938b8debb048e..007c109de6a94 100644
--- a/java/src/org/openqa/selenium/grid/data/Slot.java
+++ b/java/src/org/openqa/selenium/grid/data/Slot.java
@@ -24,6 +24,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
+import org.jspecify.annotations.Nullable;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.ImmutableCapabilities;
import org.openqa.selenium.internal.Require;
@@ -33,16 +34,17 @@ public class Slot implements Serializable {
private final SlotId id;
private final Capabilities stereotype;
- private final Session session;
+ private final @Nullable Session session;
private final Instant lastStarted;
- public Slot(SlotId id, Capabilities stereotype, Instant lastStarted, Session session) {
+ public Slot(SlotId id, Capabilities stereotype, Instant lastStarted, @Nullable Session session) {
this.id = Require.nonNull("Slot ID", id);
this.stereotype = ImmutableCapabilities.copyOf(Require.nonNull("Stereotype", stereotype));
this.lastStarted = Require.nonNull("Last started", lastStarted);
this.session = session;
}
+ @SuppressWarnings({"unused", "DataFlowIssue"})
private static Slot fromJson(JsonInput input) {
SlotId id = null;
Capabilities stereotype = null;
@@ -79,8 +81,8 @@ private static Slot fromJson(JsonInput input) {
return new Slot(id, stereotype, lastStarted, session);
}
- private Map toJson() {
- Map toReturn = new TreeMap<>();
+ private Map toJson() {
+ Map toReturn = new TreeMap<>();
toReturn.put("id", getId());
toReturn.put("lastStarted", getLastStarted());
toReturn.put("session", getSession());
@@ -100,6 +102,7 @@ public Instant getLastStarted() {
return lastStarted;
}
+ @Nullable
public Session getSession() {
return session;
}
diff --git a/java/src/org/openqa/selenium/grid/data/SlotId.java b/java/src/org/openqa/selenium/grid/data/SlotId.java
index 8a126e4071a01..20d4bbb9ce7ab 100644
--- a/java/src/org/openqa/selenium/grid/data/SlotId.java
+++ b/java/src/org/openqa/selenium/grid/data/SlotId.java
@@ -71,6 +71,7 @@ private Object toJson() {
return unmodifiableMap(toReturn);
}
+ @SuppressWarnings({"unused", "DataFlowIssue"})
private static SlotId fromJson(JsonInput input) {
NodeId nodeId = null;
UUID id = null;
diff --git a/java/src/org/openqa/selenium/grid/data/package-info.java b/java/src/org/openqa/selenium/grid/data/package-info.java
new file mode 100644
index 0000000000000..578785a87ff4c
--- /dev/null
+++ b/java/src/org/openqa/selenium/grid/data/package-info.java
@@ -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.grid.data;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/java/src/org/openqa/selenium/grid/distributor/AddNode.java b/java/src/org/openqa/selenium/grid/distributor/AddNode.java
index 904faaab6f1ec..4ca2e6d05528c 100644
--- a/java/src/org/openqa/selenium/grid/distributor/AddNode.java
+++ b/java/src/org/openqa/selenium/grid/distributor/AddNode.java
@@ -17,8 +17,6 @@
package org.openqa.selenium.grid.distributor;
-import static org.openqa.selenium.remote.http.Contents.string;
-
import java.util.stream.Collectors;
import org.openqa.selenium.grid.data.NodeStatus;
import org.openqa.selenium.grid.data.Slot;
@@ -56,7 +54,7 @@ class AddNode implements HttpHandler {
@Override
public HttpResponse execute(HttpRequest req) {
- NodeStatus status = json.toType(string(req), NodeStatus.class);
+ NodeStatus status = json.toType(req.contentAsString(), NodeStatus.class);
Node node =
new RemoteNode(
diff --git a/java/src/org/openqa/selenium/grid/distributor/BUILD.bazel b/java/src/org/openqa/selenium/grid/distributor/BUILD.bazel
index ad61b54f26067..1e3fa9cdd4ec2 100644
--- a/java/src/org/openqa/selenium/grid/distributor/BUILD.bazel
+++ b/java/src/org/openqa/selenium/grid/distributor/BUILD.bazel
@@ -29,5 +29,6 @@ java_library(
"//java/src/org/openqa/selenium/grid/sessionmap/remote",
"//java/src/org/openqa/selenium/status",
artifact("com.google.guava:guava"),
+ artifact("org.jspecify:jspecify"),
],
)
diff --git a/java/src/org/openqa/selenium/grid/distributor/GridModel.java b/java/src/org/openqa/selenium/grid/distributor/GridModel.java
index b975a16082d94..382b030d68311 100644
--- a/java/src/org/openqa/selenium/grid/distributor/GridModel.java
+++ b/java/src/org/openqa/selenium/grid/distributor/GridModel.java
@@ -18,6 +18,7 @@
package org.openqa.selenium.grid.distributor;
import java.util.Set;
+import org.jspecify.annotations.Nullable;
import org.openqa.selenium.grid.data.Availability;
import org.openqa.selenium.grid.data.NodeId;
import org.openqa.selenium.grid.data.NodeStatus;
@@ -101,7 +102,7 @@ public abstract class GridModel {
* @param slotId The ID of the slot to update
* @param session The session to associate with the slot, or null to clear
*/
- public abstract void setSession(SlotId slotId, Session session);
+ public abstract void setSession(SlotId slotId, @Nullable Session session);
/**
* Updates the health check count for a node based on its availability.
diff --git a/java/src/org/openqa/selenium/grid/distributor/NodeRegistry.java b/java/src/org/openqa/selenium/grid/distributor/NodeRegistry.java
index ec3ae18b7ee51..d56a71363b7a9 100644
--- a/java/src/org/openqa/selenium/grid/distributor/NodeRegistry.java
+++ b/java/src/org/openqa/selenium/grid/distributor/NodeRegistry.java
@@ -20,6 +20,7 @@
import java.io.Closeable;
import java.net.URI;
import java.util.Set;
+import org.jspecify.annotations.Nullable;
import org.openqa.selenium.grid.data.Availability;
import org.openqa.selenium.grid.data.DistributorStatus;
import org.openqa.selenium.grid.data.NodeId;
@@ -137,7 +138,7 @@ public interface NodeRegistry extends HasReadyState, Closeable {
* @param slotId The slot ID.
* @param session The session to associate with the slot, or null to clear.
*/
- void setSession(SlotId slotId, Session session);
+ void setSession(SlotId slotId, @Nullable Session session);
/** Get the number of active slots. */
int getActiveSlots();
@@ -151,5 +152,5 @@ public interface NodeRegistry extends HasReadyState, Closeable {
* @param uri The node URI to look up.
* @return The node if found, null otherwise.
*/
- Node getNode(URI uri);
+ @Nullable Node getNode(URI uri);
}
diff --git a/java/src/org/openqa/selenium/grid/distributor/config/BUILD.bazel b/java/src/org/openqa/selenium/grid/distributor/config/BUILD.bazel
index f75ae55ab7720..9170f39c8f6c2 100644
--- a/java/src/org/openqa/selenium/grid/distributor/config/BUILD.bazel
+++ b/java/src/org/openqa/selenium/grid/distributor/config/BUILD.bazel
@@ -15,5 +15,6 @@ java_library(
"//java/src/org/openqa/selenium/grid/distributor/selector",
"//java/src/org/openqa/selenium/grid/server",
artifact("com.beust:jcommander"),
+ artifact("org.jspecify:jspecify"),
],
)
diff --git a/java/src/org/openqa/selenium/grid/distributor/config/package-info.java b/java/src/org/openqa/selenium/grid/distributor/config/package-info.java
new file mode 100644
index 0000000000000..7e9ae3048559a
--- /dev/null
+++ b/java/src/org/openqa/selenium/grid/distributor/config/package-info.java
@@ -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.grid.distributor.config;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/java/src/org/openqa/selenium/grid/distributor/httpd/BUILD.bazel b/java/src/org/openqa/selenium/grid/distributor/httpd/BUILD.bazel
index d538459e0b681..1d1b1c0934b0d 100644
--- a/java/src/org/openqa/selenium/grid/distributor/httpd/BUILD.bazel
+++ b/java/src/org/openqa/selenium/grid/distributor/httpd/BUILD.bazel
@@ -23,5 +23,6 @@ java_library(
"//java/src/org/openqa/selenium/netty/server",
artifact("com.beust:jcommander"),
artifact("com.google.guava:guava"),
+ artifact("org.jspecify:jspecify"),
],
)
diff --git a/java/src/org/openqa/selenium/grid/distributor/httpd/package-info.java b/java/src/org/openqa/selenium/grid/distributor/httpd/package-info.java
new file mode 100644
index 0000000000000..67ddea44d69dd
--- /dev/null
+++ b/java/src/org/openqa/selenium/grid/distributor/httpd/package-info.java
@@ -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.grid.distributor.httpd;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/java/src/org/openqa/selenium/grid/distributor/local/BUILD.bazel b/java/src/org/openqa/selenium/grid/distributor/local/BUILD.bazel
index 23552ec528410..c1af8f44e9aa1 100644
--- a/java/src/org/openqa/selenium/grid/distributor/local/BUILD.bazel
+++ b/java/src/org/openqa/selenium/grid/distributor/local/BUILD.bazel
@@ -31,5 +31,6 @@ java_library(
"//java/src/org/openqa/selenium/json",
"//java/src/org/openqa/selenium/remote",
artifact("com.google.guava:guava"),
+ artifact("org.jspecify:jspecify"),
],
)
diff --git a/java/src/org/openqa/selenium/grid/distributor/local/LocalDistributor.java b/java/src/org/openqa/selenium/grid/distributor/local/LocalDistributor.java
index 5a46acdfd93b1..c888c5a0b836e 100644
--- a/java/src/org/openqa/selenium/grid/distributor/local/LocalDistributor.java
+++ b/java/src/org/openqa/selenium/grid/distributor/local/LocalDistributor.java
@@ -50,6 +50,7 @@
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
+import org.jspecify.annotations.Nullable;
import org.openqa.selenium.Beta;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.ImmutableCapabilities;
@@ -448,6 +449,7 @@ private CreateSessionResponse startSession(
return result.right();
}
+ @Nullable
private SlotId reserveSlot(RequestId requestId, Capabilities caps) {
// Use read lock for slot selection to allow concurrent reads
// This reduces contention compared to using write lock for the entire operation
@@ -679,6 +681,7 @@ private void handleNewSessionRequest(SessionRequest sessionRequest) {
}
}
+ @Nullable
protected Node getNodeFromURI(URI uri) {
Lock readLock = this.lock.readLock();
readLock.lock();
diff --git a/java/src/org/openqa/selenium/grid/distributor/local/LocalGridModel.java b/java/src/org/openqa/selenium/grid/distributor/local/LocalGridModel.java
index cf4985b299b0a..15d271ae8f706 100644
--- a/java/src/org/openqa/selenium/grid/distributor/local/LocalGridModel.java
+++ b/java/src/org/openqa/selenium/grid/distributor/local/LocalGridModel.java
@@ -34,6 +34,7 @@
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Logger;
+import org.jspecify.annotations.Nullable;
import org.openqa.selenium.events.EventBus;
import org.openqa.selenium.grid.config.Config;
import org.openqa.selenium.grid.data.Availability;
@@ -169,7 +170,7 @@ public void refresh(NodeStatus status) {
if (node.getNodeId().equals(status.getNodeId())) {
iterator.remove();
- // if the node was marked as "down", keep it down until a healthcheck passes:
+ // if the node was marked as "down", keep it down until a health check passes:
// just because the node can hit the event bus doesn't mean it's reachable
if (node.getAvailability() == DOWN) {
nodes.add(rewrite(status, DOWN));
@@ -375,6 +376,7 @@ public Set getSnapshot() {
}
}
+ @Nullable
private NodeStatus getNode(NodeId id) {
Require.nonNull("Node ID", id);
@@ -401,7 +403,7 @@ private NodeStatus rewrite(NodeStatus status, Availability availability) {
}
@Override
- public void release(SessionId id) {
+ public void release(@Nullable SessionId id) {
if (id == null) {
return;
}
@@ -430,7 +432,7 @@ public void release(SessionId id) {
}
@Override
- public void setSession(SlotId slotId, Session session) {
+ public void setSession(SlotId slotId, @Nullable Session session) {
Require.nonNull("Slot ID", slotId);
Lock writeLock = lock.writeLock();
diff --git a/java/src/org/openqa/selenium/grid/distributor/local/LocalNodeRegistry.java b/java/src/org/openqa/selenium/grid/distributor/local/LocalNodeRegistry.java
index 77612fae8aaea..bcd21fc1675d0 100644
--- a/java/src/org/openqa/selenium/grid/distributor/local/LocalNodeRegistry.java
+++ b/java/src/org/openqa/selenium/grid/distributor/local/LocalNodeRegistry.java
@@ -46,6 +46,7 @@
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
+import org.jspecify.annotations.Nullable;
import org.openqa.selenium.HealthCheckFailedException;
import org.openqa.selenium.concurrent.GuardedRunnable;
import org.openqa.selenium.events.EventBus;
@@ -519,7 +520,7 @@ public boolean reserve(SlotId slotId) {
}
@Override
- public void setSession(SlotId slotId, Session session) {
+ public void setSession(SlotId slotId, @Nullable Session session) {
Lock writeLock = lock.writeLock();
writeLock.lock();
try {
@@ -565,6 +566,7 @@ public int getIdleSlots() {
* @param uri The URI of the node to find
* @return The node if found, null otherwise
*/
+ @Nullable
public Node getNode(URI uri) {
Lock readLock = this.lock.readLock();
readLock.lock();
diff --git a/java/src/org/openqa/selenium/grid/distributor/local/package-info.java b/java/src/org/openqa/selenium/grid/distributor/local/package-info.java
new file mode 100644
index 0000000000000..29ed00ad32e30
--- /dev/null
+++ b/java/src/org/openqa/selenium/grid/distributor/local/package-info.java
@@ -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.grid.distributor.local;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/java/src/org/openqa/selenium/grid/distributor/package-info.java b/java/src/org/openqa/selenium/grid/distributor/package-info.java
index 10f446636bfd2..afd36d731e800 100644
--- a/java/src/org/openqa/selenium/grid/distributor/package-info.java
+++ b/java/src/org/openqa/selenium/grid/distributor/package-info.java
@@ -27,4 +27,7 @@
* that dialects match, or that a converter of some sort is added. The Node may be the part of the
* system responsible for adding this converter.
*/
+@NullMarked
package org.openqa.selenium.grid.distributor;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/java/src/org/openqa/selenium/grid/distributor/remote/BUILD.bazel b/java/src/org/openqa/selenium/grid/distributor/remote/BUILD.bazel
index 53ebc5ce3f192..949a8d397b31f 100644
--- a/java/src/org/openqa/selenium/grid/distributor/remote/BUILD.bazel
+++ b/java/src/org/openqa/selenium/grid/distributor/remote/BUILD.bazel
@@ -1,3 +1,4 @@
+load("@rules_jvm_external//:defs.bzl", "artifact")
load("//java:defs.bzl", "java_library")
java_library(
@@ -17,5 +18,6 @@ java_library(
"//java/src/org/openqa/selenium/grid/sessionmap",
"//java/src/org/openqa/selenium/grid/web",
"//java/src/org/openqa/selenium/remote",
+ artifact("org.jspecify:jspecify"),
],
)
diff --git a/java/src/org/openqa/selenium/grid/distributor/remote/package-info.java b/java/src/org/openqa/selenium/grid/distributor/remote/package-info.java
new file mode 100644
index 0000000000000..7a6aaf0fa411c
--- /dev/null
+++ b/java/src/org/openqa/selenium/grid/distributor/remote/package-info.java
@@ -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.grid.distributor.remote;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/java/src/org/openqa/selenium/grid/distributor/selector/BUILD.bazel b/java/src/org/openqa/selenium/grid/distributor/selector/BUILD.bazel
index 867ab5082ec90..a1ade3769a079 100644
--- a/java/src/org/openqa/selenium/grid/distributor/selector/BUILD.bazel
+++ b/java/src/org/openqa/selenium/grid/distributor/selector/BUILD.bazel
@@ -13,5 +13,6 @@ java_library(
"//java/src/org/openqa/selenium/grid/config",
"//java/src/org/openqa/selenium/grid/data",
artifact("com.google.guava:guava"),
+ artifact("org.jspecify:jspecify"),
],
)
diff --git a/java/src/org/openqa/selenium/grid/distributor/selector/package-info.java b/java/src/org/openqa/selenium/grid/distributor/selector/package-info.java
new file mode 100644
index 0000000000000..0344710f634c2
--- /dev/null
+++ b/java/src/org/openqa/selenium/grid/distributor/selector/package-info.java
@@ -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.grid.distributor.selector;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/java/src/org/openqa/selenium/grid/graphql/BUILD.bazel b/java/src/org/openqa/selenium/grid/graphql/BUILD.bazel
index a9a9c0a7b5953..102f20a1987fe 100644
--- a/java/src/org/openqa/selenium/grid/graphql/BUILD.bazel
+++ b/java/src/org/openqa/selenium/grid/graphql/BUILD.bazel
@@ -22,5 +22,6 @@ java_library(
artifact("com.google.guava:guava"),
artifact("com.graphql-java:graphql-java"),
artifact("com.github.ben-manes.caffeine:caffeine"),
+ artifact("org.jspecify:jspecify"),
],
)
diff --git a/java/src/org/openqa/selenium/grid/graphql/SessionData.java b/java/src/org/openqa/selenium/grid/graphql/SessionData.java
index 94396fbd2f942..92bc288d58aaa 100644
--- a/java/src/org/openqa/selenium/grid/graphql/SessionData.java
+++ b/java/src/org/openqa/selenium/grid/graphql/SessionData.java
@@ -20,6 +20,7 @@
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import java.util.Set;
+import org.jspecify.annotations.Nullable;
import org.openqa.selenium.grid.data.NodeStatus;
import org.openqa.selenium.grid.data.Slot;
import org.openqa.selenium.grid.distributor.Distributor;
@@ -62,6 +63,7 @@ public org.openqa.selenium.grid.graphql.Session get(DataFetchingEnvironment envi
}
}
+ @Nullable
private SessionInSlot findSession(String sessionId, Set nodeStatuses) {
for (NodeStatus status : nodeStatuses) {
for (Slot slot : status.getSlots()) {
diff --git a/java/src/org/openqa/selenium/grid/graphql/Types.java b/java/src/org/openqa/selenium/grid/graphql/Types.java
index 5bb2a21fa2b1a..da1857d3e0113 100644
--- a/java/src/org/openqa/selenium/grid/graphql/Types.java
+++ b/java/src/org/openqa/selenium/grid/graphql/Types.java
@@ -27,6 +27,7 @@
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
+import org.jspecify.annotations.Nullable;
class Types {
@@ -53,10 +54,6 @@ public String serialize(Object o) throws CoercingSerializeException {
@Override
public URI parseValue(Object input) throws CoercingParseValueException {
- if (input == null) {
- return null;
- }
-
if (input instanceof URI) {
return (URI) input;
}
@@ -110,8 +107,9 @@ public String serialize(Object o) throws CoercingSerializeException {
throw new CoercingSerializeException("Unable to coerce " + o);
}
+ @Nullable
@Override
- public URL parseValue(Object input) throws CoercingParseValueException {
+ public URL parseValue(@Nullable Object input) throws CoercingParseValueException {
if (input == null) {
return null;
}
diff --git a/java/src/org/openqa/selenium/grid/graphql/package-info.java b/java/src/org/openqa/selenium/grid/graphql/package-info.java
new file mode 100644
index 0000000000000..df07cd66175e3
--- /dev/null
+++ b/java/src/org/openqa/selenium/grid/graphql/package-info.java
@@ -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.grid.graphql;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/java/src/org/openqa/selenium/grid/jmx/JMXHelper.java b/java/src/org/openqa/selenium/grid/jmx/JMXHelper.java
index 54b8644eec577..8dd73bd8ecd37 100644
--- a/java/src/org/openqa/selenium/grid/jmx/JMXHelper.java
+++ b/java/src/org/openqa/selenium/grid/jmx/JMXHelper.java
@@ -22,10 +22,8 @@
import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
-import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
-@NullMarked
public class JMXHelper {
private static final Logger LOG = Logger.getLogger(JMXHelper.class.getName());
@@ -44,7 +42,7 @@ public class JMXHelper {
}
}
- public void unregister(ObjectName objectName) {
+ public void unregister(@Nullable ObjectName objectName) {
if (objectName != null) {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
try {
diff --git a/java/src/org/openqa/selenium/grid/jmx/MBean.java b/java/src/org/openqa/selenium/grid/jmx/MBean.java
index dce77f1da0746..3a7c7511e9a33 100644
--- a/java/src/org/openqa/selenium/grid/jmx/MBean.java
+++ b/java/src/org/openqa/selenium/grid/jmx/MBean.java
@@ -99,7 +99,7 @@ MBeanOperationInfo getMBeanOperationInfo() {
String name = bean.getClass().getName();
String description = mBean.description();
collectAttributeInfo(bean);
- MBeanAttributeInfo[] attributes =
+ @Nullable MBeanAttributeInfo[] attributes =
attributeMap.values().stream()
.map(AttributeInfo::getMBeanAttributeInfo)
.toArray(MBeanAttributeInfo[]::new);
@@ -254,7 +254,7 @@ public void setAttribute(Attribute attribute) {
}
@Override
- public AttributeList getAttributes(String[] attributes) {
+ public AttributeList getAttributes(String @Nullable [] attributes) {
AttributeList resultList = new AttributeList();
// if attributeNames is empty, return an empty result list
diff --git a/java/src/org/openqa/selenium/grid/jmx/package-info.java b/java/src/org/openqa/selenium/grid/jmx/package-info.java
new file mode 100644
index 0000000000000..51159a8e5004d
--- /dev/null
+++ b/java/src/org/openqa/selenium/grid/jmx/package-info.java
@@ -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.grid.jmx;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/java/src/org/openqa/selenium/grid/log/BUILD.bazel b/java/src/org/openqa/selenium/grid/log/BUILD.bazel
index a8d998a81e209..68c796f96396b 100644
--- a/java/src/org/openqa/selenium/grid/log/BUILD.bazel
+++ b/java/src/org/openqa/selenium/grid/log/BUILD.bazel
@@ -14,5 +14,6 @@ java_library(
"//java/src/org/openqa/selenium/json",
"//java/src/org/openqa/selenium/remote",
artifact("com.beust:jcommander"),
+ artifact("org.jspecify:jspecify"),
],
)
diff --git a/java/src/org/openqa/selenium/grid/log/FlushingHandler.java b/java/src/org/openqa/selenium/grid/log/FlushingHandler.java
index 8adda6023719c..b556231dcbbd6 100644
--- a/java/src/org/openqa/selenium/grid/log/FlushingHandler.java
+++ b/java/src/org/openqa/selenium/grid/log/FlushingHandler.java
@@ -21,10 +21,11 @@
import java.util.Objects;
import java.util.logging.LogRecord;
import java.util.logging.StreamHandler;
+import org.jspecify.annotations.Nullable;
class FlushingHandler extends StreamHandler {
- private OutputStream out;
+ @Nullable private OutputStream out;
FlushingHandler(OutputStream out) {
setOutputStream(out);
diff --git a/java/src/org/openqa/selenium/grid/log/LoggingOptions.java b/java/src/org/openqa/selenium/grid/log/LoggingOptions.java
index 20cb5a770e68a..b812bac69f186 100644
--- a/java/src/org/openqa/selenium/grid/log/LoggingOptions.java
+++ b/java/src/org/openqa/selenium/grid/log/LoggingOptions.java
@@ -30,6 +30,7 @@
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;
+import org.jspecify.annotations.Nullable;
import org.openqa.selenium.grid.config.Config;
import org.openqa.selenium.grid.config.ConfigException;
import org.openqa.selenium.internal.Debug;
@@ -79,6 +80,7 @@ public boolean isUsingPlainLogs() {
return config.getBool(LOGGING_SECTION, "plain-logs").orElse(DEFAULT_PLAIN_LOGS);
}
+ @Nullable
public String getLogEncoding() {
return config.get(LOGGING_SECTION, "log-encoding").orElse(null);
}
@@ -169,7 +171,7 @@ public void configureLogging() {
}
}
- private void configureLogEncoding(Logger logger, String encoding, Handler handler) {
+ private void configureLogEncoding(Logger logger, @Nullable String encoding, Handler handler) {
String message;
try {
if (encoding != null) {
diff --git a/java/src/org/openqa/selenium/grid/log/package-info.java b/java/src/org/openqa/selenium/grid/log/package-info.java
new file mode 100644
index 0000000000000..fd4c74b6d998a
--- /dev/null
+++ b/java/src/org/openqa/selenium/grid/log/package-info.java
@@ -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.grid.log;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/java/src/org/openqa/selenium/grid/node/Node.java b/java/src/org/openqa/selenium/grid/node/Node.java
index 1d933e33c6c6a..8171a0f753d75 100644
--- a/java/src/org/openqa/selenium/grid/node/Node.java
+++ b/java/src/org/openqa/selenium/grid/node/Node.java
@@ -35,6 +35,7 @@
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
+import org.jspecify.annotations.Nullable;
import org.openqa.selenium.BuildInfo;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.NoSuchSessionException;
@@ -255,8 +256,10 @@ public TemporaryFilesystem getDownloadsFilesystem(SessionId id) throws IOExcepti
throw new UnsupportedOperationException();
}
+ @Nullable
public abstract HttpResponse uploadFile(HttpRequest req, SessionId id);
+ @Nullable
public abstract HttpResponse downloadFile(HttpRequest req, SessionId id);
/**
diff --git a/java/src/org/openqa/selenium/grid/node/config/BUILD.bazel b/java/src/org/openqa/selenium/grid/node/config/BUILD.bazel
index d71932bada408..6c747672982fe 100644
--- a/java/src/org/openqa/selenium/grid/node/config/BUILD.bazel
+++ b/java/src/org/openqa/selenium/grid/node/config/BUILD.bazel
@@ -19,5 +19,6 @@ java_library(
"//java/src/org/openqa/selenium/remote",
artifact("com.beust:jcommander"),
artifact("com.google.guava:guava"),
+ artifact("org.jspecify:jspecify"),
],
)
diff --git a/java/src/org/openqa/selenium/grid/node/config/DriverServiceSessionFactory.java b/java/src/org/openqa/selenium/grid/node/config/DriverServiceSessionFactory.java
index 3a1b8aa09f2de..14a74962fdc6d 100644
--- a/java/src/org/openqa/selenium/grid/node/config/DriverServiceSessionFactory.java
+++ b/java/src/org/openqa/selenium/grid/node/config/DriverServiceSessionFactory.java
@@ -35,6 +35,7 @@
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Stream;
+import org.jspecify.annotations.Nullable;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.ImmutableCapabilities;
import org.openqa.selenium.MutableCapabilities;
@@ -324,6 +325,7 @@ private Capabilities setInitialCapabilityValue(Capabilities caps, String key, Ob
return new PersistentCapabilities(caps).setCapability(key, value);
}
+ @Nullable
private String getHost() {
try {
return new NetworkUtils().getNonLoopbackAddressOfThisMachine();
diff --git a/java/src/org/openqa/selenium/grid/node/config/NodeOptions.java b/java/src/org/openqa/selenium/grid/node/config/NodeOptions.java
index 23c4616039dff..be9cf1f76d08c 100644
--- a/java/src/org/openqa/selenium/grid/node/config/NodeOptions.java
+++ b/java/src/org/openqa/selenium/grid/node/config/NodeOptions.java
@@ -803,7 +803,7 @@ private void report(Map.Entry> entry)
private String unquote(String input) {
int len = input.length();
if ((input.charAt(0) == '"') && (input.charAt(len - 1) == '"')) {
- return new Json().newInput(new StringReader(input)).read(Json.OBJECT_TYPE);
+ return new Json().newInput(new StringReader(input)).readNonNull(Json.OBJECT_TYPE);
}
return input;
}
diff --git a/java/src/org/openqa/selenium/grid/node/config/SessionCapabilitiesMutator.java b/java/src/org/openqa/selenium/grid/node/config/SessionCapabilitiesMutator.java
index 1d3431d779ab4..89fcf5c58b017 100644
--- a/java/src/org/openqa/selenium/grid/node/config/SessionCapabilitiesMutator.java
+++ b/java/src/org/openqa/selenium/grid/node/config/SessionCapabilitiesMutator.java
@@ -27,6 +27,7 @@
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.ImmutableCapabilities;
import org.openqa.selenium.PersistentCapabilities;
+import org.openqa.selenium.internal.Require;
public class SessionCapabilitiesMutator implements Function {
@@ -49,11 +50,13 @@ public Capabilities apply(Capabilities capabilities) {
return capabilities;
}
- if (slotStereotype.getCapability(SE_VNC_ENABLED) != null) {
+ Object vncEnabled = slotStereotype.getCapability(SE_VNC_ENABLED);
+ if (vncEnabled != null) {
+ Object vncPort = slotStereotype.getCapability(SE_NO_VNC_PORT);
capabilities =
new PersistentCapabilities(capabilities)
- .setCapability(SE_VNC_ENABLED, slotStereotype.getCapability(SE_VNC_ENABLED))
- .setCapability(SE_NO_VNC_PORT, slotStereotype.getCapability(SE_NO_VNC_PORT));
+ .setCapability(SE_VNC_ENABLED, vncEnabled)
+ .setCapability(SE_NO_VNC_PORT, Require.nonNull(SE_NO_VNC_PORT, vncPort));
}
String browserName = capabilities.getBrowserName().toLowerCase(Locale.ENGLISH);
diff --git a/java/src/org/openqa/selenium/grid/node/config/package-info.java b/java/src/org/openqa/selenium/grid/node/config/package-info.java
new file mode 100644
index 0000000000000..51df08ff26d59
--- /dev/null
+++ b/java/src/org/openqa/selenium/grid/node/config/package-info.java
@@ -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.grid.node.config;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/java/src/org/openqa/selenium/grid/node/docker/BUILD.bazel b/java/src/org/openqa/selenium/grid/node/docker/BUILD.bazel
index 6cc6de13f6695..a4e1467776f4c 100644
--- a/java/src/org/openqa/selenium/grid/node/docker/BUILD.bazel
+++ b/java/src/org/openqa/selenium/grid/node/docker/BUILD.bazel
@@ -22,5 +22,6 @@ java_library(
"//java/src/org/openqa/selenium/support",
artifact("com.beust:jcommander"),
artifact("com.google.guava:guava"),
+ artifact("org.jspecify:jspecify"),
],
)
diff --git a/java/src/org/openqa/selenium/grid/node/docker/DockerOptions.java b/java/src/org/openqa/selenium/grid/node/docker/DockerOptions.java
index d95363d8e9502..ce422300835a2 100644
--- a/java/src/org/openqa/selenium/grid/node/docker/DockerOptions.java
+++ b/java/src/org/openqa/selenium/grid/node/docker/DockerOptions.java
@@ -37,6 +37,7 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
+import org.jspecify.annotations.Nullable;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.Platform;
import org.openqa.selenium.docker.ContainerId;
@@ -115,6 +116,7 @@ private Duration getServerStartTimeout() {
config.getInt(DOCKER_SECTION, "server-start-timeout").orElse(DEFAULT_SERVER_START_TIMEOUT));
}
+ @Nullable
private String getApiVersion() {
return config.get(DOCKER_SECTION, "api-version").orElse(null);
}
@@ -238,6 +240,7 @@ protected List getDevicesMapping() {
return deviceMapping;
}
+ @Nullable
private Image getVideoImage(Docker docker) {
String videoImage = config.get(DOCKER_SECTION, "video-image").orElse(DEFAULT_VIDEO_IMAGE);
if (videoImage.equalsIgnoreCase("false")) {
@@ -280,6 +283,7 @@ private Map getGroupingLabels(Optional info) {
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
+ @Nullable
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
private DockerAssetsPath getAssetsPath(Optional info) {
if (info.isPresent()) {
diff --git a/java/src/org/openqa/selenium/grid/node/docker/DockerSession.java b/java/src/org/openqa/selenium/grid/node/docker/DockerSession.java
index 6608e7cb7bdca..464be03e2ab5d 100644
--- a/java/src/org/openqa/selenium/grid/node/docker/DockerSession.java
+++ b/java/src/org/openqa/selenium/grid/node/docker/DockerSession.java
@@ -25,6 +25,7 @@
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
+import org.jspecify.annotations.Nullable;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.docker.Container;
import org.openqa.selenium.grid.node.DefaultActiveSession;
@@ -38,12 +39,12 @@ public class DockerSession extends DefaultActiveSession {
private static final Logger LOG = Logger.getLogger(DockerSession.class.getName());
private final Container container;
- private final Container videoContainer;
+ private final @Nullable Container videoContainer;
private final DockerAssetsPath assetsPath;
DockerSession(
Container container,
- Container videoContainer,
+ @Nullable Container videoContainer,
Tracer tracer,
HttpClient client,
SessionId id,
diff --git a/java/src/org/openqa/selenium/grid/node/docker/DockerSessionFactory.java b/java/src/org/openqa/selenium/grid/node/docker/DockerSessionFactory.java
index 9aef964df9246..189275b166240 100644
--- a/java/src/org/openqa/selenium/grid/node/docker/DockerSessionFactory.java
+++ b/java/src/org/openqa/selenium/grid/node/docker/DockerSessionFactory.java
@@ -20,7 +20,6 @@
import static java.util.Optional.ofNullable;
import static org.openqa.selenium.docker.ContainerConfig.image;
import static org.openqa.selenium.remote.Dialect.W3C;
-import static org.openqa.selenium.remote.http.Contents.string;
import static org.openqa.selenium.remote.http.HttpMethod.GET;
import static org.openqa.selenium.remote.tracing.Tags.EXCEPTION;
@@ -44,6 +43,7 @@
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.logging.Logger;
+import org.jspecify.annotations.Nullable;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.ImmutableCapabilities;
@@ -98,8 +98,8 @@ public class DockerSessionFactory implements SessionFactory {
private final Image browserImage;
private final Capabilities stereotype;
private final List devices;
- private final Image videoImage;
- private final DockerAssetsPath assetsPath;
+ private final @Nullable Image videoImage;
+ private final @Nullable DockerAssetsPath assetsPath;
private final String networkName;
private final boolean runningInDocker;
private final Predicate predicate;
@@ -117,8 +117,8 @@ public DockerSessionFactory(
Image browserImage,
Capabilities stereotype,
List devices,
- Image videoImage,
- DockerAssetsPath assetsPath,
+ @Nullable Image videoImage,
+ @Nullable DockerAssetsPath assetsPath,
String networkName,
boolean runningInDocker,
Predicate predicate,
@@ -162,7 +162,7 @@ public Either apply(CreateSessionRequest sess
// Generate unique identifier for consistent naming between browser and recorder containers
// Using browserName-timestamp-UUID to avoid conflicts in concurrent session creation
String browserName = sessionRequest.getDesiredCapabilities().getBrowserName();
- if (browserName != null && !browserName.isEmpty()) {
+ if (!browserName.isEmpty()) {
browserName = browserName.toLowerCase();
} else {
browserName = "unknown";
@@ -374,6 +374,7 @@ private void setCapsToEnvVars(
timeZone.ifPresent(zone -> envVars.put("TZ", zone.getID()));
}
+ @Nullable
private Container startVideoContainer(
Capabilities sessionCapabilities,
String browserContainerIp,
@@ -438,6 +439,7 @@ private Map getVideoContainerEnvVars(
return envVars;
}
+ @Nullable
private String getVideoFileName(Capabilities sessionRequestCapabilities, String capabilityName) {
Optional