Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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/bidi/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ java_library(
":bidi",
"//java:auto-service",
"//java/src/org/openqa/selenium:core",
"//java/src/org/openqa/selenium/concurrent",
"//java/src/org/openqa/selenium/remote:api",
"//java/src/org/openqa/selenium/remote/http",
],
Expand Down
26 changes: 25 additions & 1 deletion java/src/org/openqa/selenium/bidi/BiDiProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,18 @@

package org.openqa.selenium.bidi;

import static java.util.logging.Level.INFO;
import static org.openqa.selenium.concurrent.Lazy.lazy;

import com.google.auto.service.AutoService;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.logging.Logger;

import org.openqa.selenium.Capabilities;
import org.openqa.selenium.concurrent.Lazy;
import org.openqa.selenium.remote.AugmenterProvider;
import org.openqa.selenium.remote.ExecuteMethod;
import org.openqa.selenium.remote.http.ClientConfig;
Expand All @@ -31,6 +37,7 @@
@SuppressWarnings({"rawtypes", "RedundantSuppression"})
@AutoService(AugmenterProvider.class)
public class BiDiProvider implements AugmenterProvider<HasBiDi> {
private static final Logger LOG = Logger.getLogger(BiDiProvider.class.getName());

@Override
public Predicate<Capabilities> isApplicable() {
Expand All @@ -44,15 +51,32 @@ public Class<HasBiDi> getDescribedInterface() {

@Override
public HasBiDi getImplementation(Capabilities caps, ExecuteMethod executeMethod) {
final Lazy<BiDi> biDi = lazy(() -> establishBiDiConnection(caps));

LOG.log(INFO, "WebDriver augmented with BiDi interface; connection will not be verified until first use.");

return new HasBiDi() {
@Override
public Optional<BiDi> maybeGetBiDi() {
return biDi.getIfInitialized();
}

@Override
public BiDi getBiDi() {
return biDi.get();
}
};
}

private BiDi establishBiDiConnection(Capabilities caps) {
URI wsUri = getBiDiUrl(caps).orElseThrow(() -> new BiDiException("BiDi not supported"));

HttpClient.Factory clientFactory = HttpClient.Factory.createDefault();
ClientConfig wsConfig = ClientConfig.defaultConfig().baseUri(wsUri);
HttpClient wsClient = clientFactory.createClient(wsConfig);
Connection connection = new Connection(wsClient, wsUri.toString());

return () -> Optional.of(new BiDi(connection));
return new BiDi(connection);
}

private Optional<URI> getBiDiUrl(Capabilities caps) {
Expand Down
4 changes: 4 additions & 0 deletions java/src/org/openqa/selenium/concurrent/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@ java_library(
name = "concurrent",
srcs = glob(["*.java"]),
visibility = [
"//java/src/org/openqa/selenium/bidi:__subpackages__",
"//java/src/org/openqa/selenium/devtools:__subpackages__",
"//java/src/org/openqa/selenium/events:__subpackages__",
"//java/src/org/openqa/selenium/grid:__subpackages__",
"//java/src/org/openqa/selenium/remote:__subpackages__",
"//java/test/org/openqa/selenium/concurrent:__subpackages__",
],
deps = [
"//java/src/org/openqa/selenium:core",
"@maven//:org_jspecify_jspecify",
],
)
54 changes: 54 additions & 0 deletions java/src/org/openqa/selenium/concurrent/Lazy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// 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.


package org.openqa.selenium.concurrent;

import static java.util.Objects.requireNonNull;

import java.util.Optional;
import java.util.function.Supplier;
import org.jspecify.annotations.Nullable;

public class Lazy<T> {

@Nullable private volatile T value;
private final Supplier<T> supplier;

private Lazy(Supplier<T> supplier) {
this.supplier = supplier;
}

public Optional<T> getIfInitialized() {
return Optional.ofNullable(value);
}

public T get() {
if (value == null) {
synchronized (this) {
if (value == null) {
value = supplier.get();
}
}
}
return requireNonNull(value);
}

public static <T> Lazy<T> lazy(Supplier<T> supplier) {
return new Lazy<>(supplier);
}
}
22 changes: 22 additions & 0 deletions java/src/org/openqa/selenium/concurrent/package-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// 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.concurrent;

import org.jspecify.annotations.NullMarked;
1 change: 1 addition & 0 deletions java/src/org/openqa/selenium/devtools/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ java_library(
":devtools",
"//java:auto-service",
"//java/src/org/openqa/selenium:core",
"//java/src/org/openqa/selenium/concurrent",
"//java/src/org/openqa/selenium/remote:api",
],
)
Expand Down
32 changes: 28 additions & 4 deletions java/src/org/openqa/selenium/devtools/DevToolsProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,25 @@

package org.openqa.selenium.devtools;

import static java.util.logging.Level.INFO;
import static org.openqa.selenium.concurrent.Lazy.lazy;

import com.google.auto.service.AutoService;
import java.net.URI;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.logging.Logger;

import org.openqa.selenium.Capabilities;
import org.openqa.selenium.concurrent.Lazy;
import org.openqa.selenium.devtools.noop.NoOpCdpInfo;
import org.openqa.selenium.remote.AugmenterProvider;
import org.openqa.selenium.remote.ExecuteMethod;

@SuppressWarnings({"rawtypes", "RedundantSuppression"})
@AutoService(AugmenterProvider.class)
public class DevToolsProvider implements AugmenterProvider<HasDevTools> {
private static final Logger LOG = Logger.getLogger(DevToolsProvider.class.getName());

@Override
public Predicate<Capabilities> isApplicable() {
Expand All @@ -42,14 +49,31 @@ public Class<HasDevTools> getDescribedInterface() {

@Override
public HasDevTools getImplementation(Capabilities caps, ExecuteMethod executeMethod) {
final Lazy<DevTools> devTools = lazy(() -> establishDevToolsConnection(caps));

LOG.log(INFO, "WebDriver augmented with DevTools interface; connection will not be verified until first use.");

return new HasDevTools() {
@Override
public Optional<DevTools> maybeGetDevTools() {
return devTools.getIfInitialized();
}

@Override
public DevTools getDevTools() {
return devTools.get();
}
};
}

private DevTools establishDevToolsConnection(Capabilities caps) {
Object cdpVersion = caps.getCapability("se:cdpVersion");
String version = cdpVersion instanceof String ? (String) cdpVersion : caps.getBrowserVersion();

CdpInfo info = new CdpVersionFinder().match(version).orElseGet(NoOpCdpInfo::new);
Optional<DevTools> devTools =
SeleniumCdpConnection.create(caps).map(conn -> new DevTools(info::getDomains, conn));

return () -> devTools;
return SeleniumCdpConnection.create(caps)
.map(conn -> new DevTools(info::getDomains, conn))
.orElseThrow(() -> new DevToolsException("Unable to create DevTools connection"));
}

private String getCdpUrl(Capabilities caps) {
Expand Down
14 changes: 14 additions & 0 deletions java/test/org/openqa/selenium/concurrent/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
load("@rules_jvm_external//:defs.bzl", "artifact")
load("//java:defs.bzl", "JUNIT5_DEPS", "java_test_suite")

java_test_suite(
name = "SmallTests",
size = "small",
srcs = glob(["*Test.java"]),
deps = [
"//java/src/org/openqa/selenium/concurrent",
"//java/test/org/openqa/selenium/testing:annotations",
artifact("org.junit.jupiter:junit-jupiter-api"),
artifact("org.assertj:assertj-core"),
] + JUNIT5_DEPS,
)
67 changes: 67 additions & 0 deletions java/test/org/openqa/selenium/concurrent/LazyTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// 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.


package org.openqa.selenium.concurrent;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.concurrent.atomic.AtomicInteger;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

@Tag("UnitTests")
public class LazyTest {
private final AtomicInteger counter = new AtomicInteger(0);

@Test
void trivialCase() {
Lazy expression = Lazy.lazy(() -> "constant");
assertThat(expression.get()).isEqualTo("constant");
assertThat(expression.getIfInitialized()).contains("constant");
}

@Test
void getIfInitialized_returnsNothing_ifNotInitializedYet() {
Lazy expression = Lazy.lazy(() -> "value#" + counter.incrementAndGet());
assertThat(expression.getIfInitialized()).isEmpty();
}

@Test
void lazyEvaluatedExpression() {
Lazy expression = Lazy.lazy(() -> "value#" + counter.incrementAndGet());
assertThat(expression.get()).isEqualTo("value#1");
assertThat(expression.get()).isEqualTo("value#1");
assertThat(expression.getIfInitialized()).contains("value#1");
assertThat(expression.getIfInitialized()).contains("value#1");
}

@Test
void differentLazyInstances_produce_differentValues() {
Lazy expression1 = Lazy.lazy(() -> "one#" + counter.incrementAndGet());
Lazy expression2 = Lazy.lazy(() -> "two#" + counter.incrementAndGet());
assertThat(expression1.get()).isEqualTo("one#1");
assertThat(expression1.getIfInitialized()).contains("one#1");
assertThat(expression2.getIfInitialized()).isEmpty();

assertThat(expression2.get()).isEqualTo("two#2");
assertThat(expression2.getIfInitialized()).contains("two#2");

assertThat(expression1.get()).isEqualTo("one#1");
assertThat(expression2.get()).isEqualTo("two#2");
}
}
Loading