Skip to content

Commit b9a4b95

Browse files
committed
extract reusable code to Lazy.java
Now we have a convenient factory method `lazy` for declaring lazy-initialized values like BiDi or DevTools. This simplifies BiDiProvider and DevToolsProvider code.
1 parent 288ec09 commit b9a4b95

File tree

9 files changed

+149
-39
lines changed

9 files changed

+149
-39
lines changed

java/src/org/openqa/selenium/bidi/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ java_library(
1414
":bidi",
1515
"//java:auto-service",
1616
"//java/src/org/openqa/selenium:core",
17+
"//java/src/org/openqa/selenium/concurrent",
1718
"//java/src/org/openqa/selenium/remote:api",
1819
"//java/src/org/openqa/selenium/remote/http",
1920
],

java/src/org/openqa/selenium/bidi/BiDiProvider.java

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,16 @@
1717

1818
package org.openqa.selenium.bidi;
1919

20+
import static org.openqa.selenium.concurrent.Lazy.lazy;
21+
2022
import com.google.auto.service.AutoService;
2123
import java.net.URI;
2224
import java.net.URISyntaxException;
2325
import java.util.Optional;
2426
import java.util.function.Predicate;
2527

2628
import org.openqa.selenium.Capabilities;
29+
import org.openqa.selenium.concurrent.Lazy;
2730
import org.openqa.selenium.remote.AugmenterProvider;
2831
import org.openqa.selenium.remote.ExecuteMethod;
2932
import org.openqa.selenium.remote.http.ClientConfig;
@@ -46,36 +49,32 @@ public Class<HasBiDi> getDescribedInterface() {
4649
@Override
4750
public HasBiDi getImplementation(Capabilities caps, ExecuteMethod executeMethod) {
4851
return new HasBiDi() {
49-
private volatile BiDi biDi;
50-
private final Object lock = new Object();
52+
private final Lazy<BiDi> biDi = lazy(() -> establishBiDiConnection(caps));
5153

5254
@Override
5355
public Optional<BiDi> maybeGetBiDi() {
54-
return Optional.ofNullable(biDi);
56+
return biDi.getIfInitialized();
5557
}
5658

5759
@Override
5860
public BiDi getBiDi() {
59-
if (biDi == null) {
60-
synchronized (lock) {
61-
if (biDi == null) {
62-
URI wsUri =
63-
getBiDiUrl(caps).orElseThrow(() -> new BiDiException("BiDi not supported"));
64-
65-
HttpClient.Factory clientFactory = HttpClient.Factory.createDefault();
66-
ClientConfig wsConfig = ClientConfig.defaultConfig().baseUri(wsUri);
67-
HttpClient wsClient = clientFactory.createClient(wsConfig);
68-
Connection connection = new Connection(wsClient, wsUri.toString());
69-
70-
biDi = new BiDi(connection);
71-
}
72-
}
73-
}
74-
return biDi;
61+
return biDi.get();
7562
}
7663
};
7764
}
7865

66+
private BiDi establishBiDiConnection(Capabilities caps) {
67+
URI wsUri = getBiDiUrl(caps)
68+
.orElseThrow(() -> new BiDiException("BiDi not supported"));
69+
70+
HttpClient.Factory clientFactory = HttpClient.Factory.createDefault();
71+
ClientConfig wsConfig = ClientConfig.defaultConfig().baseUri(wsUri);
72+
HttpClient wsClient = clientFactory.createClient(wsConfig);
73+
Connection connection = new Connection(wsClient, wsUri.toString());
74+
75+
return new BiDi(connection);
76+
}
77+
7978
private Optional<URI> getBiDiUrl(Capabilities caps) {
8079
Object biDiCapability = caps.getCapability("webSocketUrl");
8180
Optional<String> webSocketUrl = Optional.ofNullable((String) biDiCapability);

java/src/org/openqa/selenium/concurrent/BUILD.bazel

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@ java_library(
44
name = "concurrent",
55
srcs = glob(["*.java"]),
66
visibility = [
7+
"//java/src/org/openqa/selenium/bidi:__subpackages__",
8+
"//java/src/org/openqa/selenium/devtools:__subpackages__",
79
"//java/src/org/openqa/selenium/events:__subpackages__",
810
"//java/src/org/openqa/selenium/grid:__subpackages__",
911
"//java/src/org/openqa/selenium/remote:__subpackages__",
12+
"//java/test/org/openqa/selenium/concurrent:__subpackages__",
1013
],
1114
deps = [
1215
"//java/src/org/openqa/selenium:core",
16+
"@maven//:org_jspecify_jspecify",
1317
],
1418
)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package org.openqa.selenium.concurrent;
2+
3+
import static java.util.Objects.requireNonNull;
4+
5+
import org.jspecify.annotations.Nullable;
6+
7+
import java.util.Optional;
8+
import java.util.function.Supplier;
9+
10+
public class Lazy<T> {
11+
12+
@Nullable
13+
private volatile T value;
14+
private final Supplier<T> supplier;
15+
16+
private Lazy(Supplier<T> supplier) {
17+
this.supplier = supplier;
18+
}
19+
20+
@Nullable
21+
public Optional<T> getIfInitialized() {
22+
return Optional.ofNullable(value);
23+
}
24+
25+
public T get() {
26+
if (value == null) {
27+
synchronized (this) {
28+
if (value == null) {
29+
value = supplier.get();
30+
}
31+
}
32+
}
33+
return requireNonNull(value);
34+
}
35+
36+
public static <T> Lazy<T> lazy(Supplier<T> supplier) {
37+
return new Lazy<>(supplier);
38+
}
39+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
@NullMarked
2+
package org.openqa.selenium.concurrent;
3+
4+
import org.jspecify.annotations.NullMarked;

java/src/org/openqa/selenium/devtools/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ java_library(
4646
":devtools",
4747
"//java:auto-service",
4848
"//java/src/org/openqa/selenium:core",
49+
"//java/src/org/openqa/selenium/concurrent",
4950
"//java/src/org/openqa/selenium/remote:api",
5051
],
5152
)

java/src/org/openqa/selenium/devtools/DevToolsProvider.java

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,15 @@
1717

1818
package org.openqa.selenium.devtools;
1919

20+
import static org.openqa.selenium.concurrent.Lazy.lazy;
21+
2022
import com.google.auto.service.AutoService;
2123
import java.net.URI;
2224
import java.util.Optional;
2325
import java.util.function.Predicate;
2426

2527
import org.openqa.selenium.Capabilities;
28+
import org.openqa.selenium.concurrent.Lazy;
2629
import org.openqa.selenium.devtools.noop.NoOpCdpInfo;
2730
import org.openqa.selenium.remote.AugmenterProvider;
2831
import org.openqa.selenium.remote.ExecuteMethod;
@@ -43,37 +46,32 @@ public Class<HasDevTools> getDescribedInterface() {
4346

4447
@Override
4548
public HasDevTools getImplementation(Capabilities caps, ExecuteMethod executeMethod) {
46-
return new HasDevTools() {
47-
private volatile DevTools devTools;
48-
private final Object lock = new Object();
49+
final Lazy<DevTools> devTools = lazy(() -> establishDevToolsConnection(caps));
4950

51+
return new HasDevTools() {
5052
@Override
5153
public Optional<DevTools> maybeGetDevTools() {
52-
return Optional.ofNullable(devTools);
54+
return devTools.getIfInitialized();
5355
}
5456

5557
@Override
5658
public DevTools getDevTools() {
57-
if (devTools == null) {
58-
synchronized (lock) {
59-
if (devTools == null) {
60-
Object cdpVersion = caps.getCapability("se:cdpVersion");
61-
String version =
62-
cdpVersion instanceof String ? (String) cdpVersion : caps.getBrowserVersion();
63-
64-
CdpInfo info = new CdpVersionFinder().match(version).orElseGet(NoOpCdpInfo::new);
65-
this.devTools =
66-
SeleniumCdpConnection.create(caps)
67-
.map(conn -> new DevTools(info::getDomains, conn))
68-
.orElseThrow(() -> new DevToolsException("Unable to create DevTools connection"));
69-
}
70-
}
71-
}
72-
return devTools;
59+
return devTools.get();
7360
}
7461
};
7562
}
7663

64+
private DevTools establishDevToolsConnection(Capabilities caps) {
65+
Object cdpVersion = caps.getCapability("se:cdpVersion");
66+
String version =
67+
cdpVersion instanceof String ? (String) cdpVersion : caps.getBrowserVersion();
68+
69+
CdpInfo info = new CdpVersionFinder().match(version).orElseGet(NoOpCdpInfo::new);
70+
return SeleniumCdpConnection.create(caps)
71+
.map(conn -> new DevTools(info::getDomains, conn))
72+
.orElseThrow(() -> new DevToolsException("Unable to create DevTools connection"));
73+
}
74+
7775
private String getCdpUrl(Capabilities caps) {
7876
Object cdpEnabled = caps.getCapability("se:cdpEnabled");
7977
if (cdpEnabled != null && !Boolean.parseBoolean(cdpEnabled.toString())) {
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
load("@rules_jvm_external//:defs.bzl", "artifact")
2+
load("//java:defs.bzl", "JUNIT5_DEPS", "java_test_suite")
3+
4+
java_test_suite(
5+
name = "SmallTests",
6+
size = "small",
7+
srcs = glob(["*Test.java"]),
8+
deps = [
9+
"//java/src/org/openqa/selenium/concurrent",
10+
"//java/test/org/openqa/selenium/testing:annotations",
11+
artifact("org.junit.jupiter:junit-jupiter-api"),
12+
artifact("org.assertj:assertj-core"),
13+
] + JUNIT5_DEPS,
14+
)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package org.openqa.selenium.concurrent;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import org.junit.jupiter.api.Tag;
6+
import org.junit.jupiter.api.Test;
7+
8+
import java.util.concurrent.atomic.AtomicInteger;
9+
10+
@Tag("UnitTests")
11+
public class LazyTest {
12+
private final AtomicInteger counter = new AtomicInteger(0);
13+
14+
@Test
15+
void trivialCase() {
16+
Lazy expression = Lazy.lazy(() -> "constant");
17+
assertThat(expression.get()).isEqualTo("constant");
18+
assertThat(expression.getIfInitialized()).contains("constant");
19+
}
20+
21+
@Test
22+
void getIfInitialized_returnsNothing_ifNotInitializedYet() {
23+
Lazy expression = Lazy.lazy(() -> "value#" + counter.incrementAndGet());
24+
assertThat(expression.getIfInitialized()).isEmpty();
25+
}
26+
27+
@Test
28+
void lazyEvaluatedExpression() {
29+
Lazy expression = Lazy.lazy(() -> "value#" + counter.incrementAndGet());
30+
assertThat(expression.get()).isEqualTo("value#1");
31+
assertThat(expression.get()).isEqualTo("value#1");
32+
assertThat(expression.getIfInitialized()).contains("value#1");
33+
assertThat(expression.getIfInitialized()).contains("value#1");
34+
}
35+
36+
@Test
37+
void differentLazyInstances_produce_differentValues() {
38+
Lazy expression1 = Lazy.lazy(() -> "one#" + counter.incrementAndGet());
39+
Lazy expression2 = Lazy.lazy(() -> "two#" + counter.incrementAndGet());
40+
assertThat(expression1.get()).isEqualTo("one#1");
41+
assertThat(expression1.getIfInitialized()).contains("one#1");
42+
assertThat(expression2.getIfInitialized()).isEmpty();
43+
44+
assertThat(expression2.get()).isEqualTo("two#2");
45+
assertThat(expression2.getIfInitialized()).contains("two#2");
46+
47+
assertThat(expression1.get()).isEqualTo("one#1");
48+
assertThat(expression2.get()).isEqualTo("two#2");
49+
}
50+
}

0 commit comments

Comments
 (0)