Skip to content

Commit fe5a4d3

Browse files
pujaganidiemol
andauthored
[java] Replace dynamic proxy with ByteBuddy for webdriverdecorator (#10271)
* [java] Replace Dynamic Proxy with ByteBuddy for Webdriverdecorator * [java] Add tests to check if augmenter can be decorated and vice-versa Co-authored-by: Diego Molina <[email protected]>
1 parent b07eb4b commit fe5a4d3

5 files changed

Lines changed: 105 additions & 18 deletions

File tree

java/src/org/openqa/selenium/support/decorators/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
load("@rules_jvm_external//:defs.bzl", "artifact")
12
load("//java:defs.bzl", "java_library")
23

34
java_library(
@@ -6,5 +7,6 @@ java_library(
67
visibility = ["//visibility:public"],
78
deps = [
89
"//java/src/org/openqa/selenium:core",
10+
artifact("net.bytebuddy:byte-buddy"),
911
],
1012
)

java/src/org/openqa/selenium/support/decorators/WebDriverDecorator.java

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@
1717

1818
package org.openqa.selenium.support.decorators;
1919

20+
import net.bytebuddy.ByteBuddy;
21+
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
22+
import net.bytebuddy.implementation.InvocationHandlerAdapter;
23+
import net.bytebuddy.matcher.ElementMatchers;
24+
2025
import org.openqa.selenium.Alert;
2126
import org.openqa.selenium.Beta;
2227
import org.openqa.selenium.WebDriver;
@@ -29,7 +34,6 @@
2934
import java.lang.reflect.InvocationHandler;
3035
import java.lang.reflect.InvocationTargetException;
3136
import java.lang.reflect.Method;
32-
import java.lang.reflect.Proxy;
3337
import java.util.HashMap;
3438
import java.util.HashSet;
3539
import java.util.List;
@@ -183,7 +187,7 @@ public final WebDriver decorate(WebDriver original) {
183187
Require.nonNull("WebDriver", original);
184188

185189
decorated = createDecorated(original);
186-
return createProxy(decorated);
190+
return createProxy(decorated, WebDriver.class);
187191
}
188192

189193
public Decorated<WebDriver> getDecoratedDriver() {
@@ -244,31 +248,31 @@ public Object onError(
244248

245249
private Object decorateResult(Object toDecorate) {
246250
if (toDecorate instanceof WebDriver) {
247-
return createProxy(getDecoratedDriver());
251+
return createProxy(getDecoratedDriver(), WebDriver.class);
248252
}
249253
if (toDecorate instanceof WebElement) {
250-
return createProxy(createDecorated((WebElement) toDecorate));
254+
return createProxy(createDecorated((WebElement) toDecorate), WebElement.class);
251255
}
252256
if (toDecorate instanceof Alert) {
253-
return createProxy(createDecorated((Alert) toDecorate));
257+
return createProxy(createDecorated((Alert) toDecorate), Alert.class);
254258
}
255259
if (toDecorate instanceof VirtualAuthenticator) {
256-
return createProxy(createDecorated((VirtualAuthenticator) toDecorate));
260+
return createProxy(createDecorated((VirtualAuthenticator) toDecorate), VirtualAuthenticator.class);
257261
}
258262
if (toDecorate instanceof WebDriver.Navigation) {
259-
return createProxy(createDecorated((WebDriver.Navigation) toDecorate));
263+
return createProxy(createDecorated((WebDriver.Navigation) toDecorate), WebDriver.Navigation.class);
260264
}
261265
if (toDecorate instanceof WebDriver.Options) {
262-
return createProxy(createDecorated((WebDriver.Options) toDecorate));
266+
return createProxy(createDecorated((WebDriver.Options) toDecorate), WebDriver.Options.class);
263267
}
264268
if (toDecorate instanceof WebDriver.TargetLocator) {
265-
return createProxy(createDecorated((WebDriver.TargetLocator) toDecorate));
269+
return createProxy(createDecorated((WebDriver.TargetLocator) toDecorate), WebDriver.TargetLocator.class);
266270
}
267271
if (toDecorate instanceof WebDriver.Timeouts) {
268-
return createProxy(createDecorated((WebDriver.Timeouts) toDecorate));
272+
return createProxy(createDecorated((WebDriver.Timeouts) toDecorate), WebDriver.Timeouts.class);
269273
}
270274
if (toDecorate instanceof WebDriver.Window) {
271-
return createProxy(createDecorated((WebDriver.Window) toDecorate));
275+
return createProxy(createDecorated((WebDriver.Window) toDecorate), WebDriver.Window.class);
272276
}
273277
if (toDecorate instanceof List) {
274278
return ((List<?>) toDecorate).stream()
@@ -278,7 +282,7 @@ private Object decorateResult(Object toDecorate) {
278282
return toDecorate;
279283
}
280284

281-
protected final <Z> Z createProxy(final Decorated<Z> decorated) {
285+
protected final <Z> Z createProxy(final Decorated<Z> decorated, Class<Z> clazz) {
282286
Set<Class<?>> decoratedInterfaces = extractInterfaces(decorated);
283287
Set<Class<?>> originalInterfaces = extractInterfaces(decorated.getOriginal());
284288
Map<Class<?>, InvocationHandler> derivedInterfaces = deriveAdditionalInterfaces(decorated.getOriginal());
@@ -311,8 +315,21 @@ protected final <Z> Z createProxy(final Decorated<Z> decorated) {
311315
allInterfaces.addAll(derivedInterfaces.keySet());
312316
Class<?>[] allInterfacesArray = allInterfaces.toArray(new Class<?>[0]);
313317

314-
return (Z) Proxy.newProxyInstance(
315-
this.getClass().getClassLoader(), allInterfacesArray, handler);
318+
Class<? extends Z> proxy = new ByteBuddy()
319+
.subclass(Object.class)
320+
.implement(allInterfacesArray)
321+
.method(ElementMatchers.any())
322+
.intercept(InvocationHandlerAdapter.of(handler))
323+
.make()
324+
.load(clazz.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
325+
.getLoaded()
326+
.asSubclass(clazz);
327+
328+
try {
329+
return proxy.newInstance();
330+
} catch (ReflectiveOperationException e) {
331+
throw new IllegalStateException("Unable to create new proxy", e);
332+
}
316333
}
317334

318335
static Set<Class<?>> extractInterfaces(final Object object) {
@@ -381,7 +398,7 @@ private Map<Class<?>, InvocationHandler> deriveAdditionalInterfaces(Object objec
381398
}
382399

383400
@FunctionalInterface
384-
interface JsonSerializer {
401+
protected interface JsonSerializer {
385402
Object toJson();
386403
}
387404
}

java/test/org/openqa/selenium/remote/AugmenterTest.java

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,15 @@
3030
import org.openqa.selenium.SearchContext;
3131
import org.openqa.selenium.WebDriver;
3232
import org.openqa.selenium.WebElement;
33-
import org.openqa.selenium.devtools.HasDevTools;
3433
import org.openqa.selenium.firefox.FirefoxOptions;
3534
import org.openqa.selenium.html5.LocationContext;
3635
import org.openqa.selenium.html5.WebStorage;
3736
import org.openqa.selenium.internal.Require;
37+
import org.openqa.selenium.support.decorators.Decorated;
38+
import org.openqa.selenium.support.decorators.WebDriverDecorator;
3839
import org.openqa.selenium.testing.UnitTests;
3940

41+
import java.lang.reflect.Method;
4042
import java.util.ArrayList;
4143
import java.util.Collections;
4244
import java.util.HashMap;
@@ -225,6 +227,55 @@ public void shouldAugmentMultipleInterfaces() {
225227
assertThat(number).isEqualTo(42);
226228
}
227229

230+
@Test
231+
public void shouldAugmentWebDriverDecorator() {
232+
final Capabilities caps = new ImmutableCapabilities(CapabilityType.SUPPORTS_LOCATION_CONTEXT, true);
233+
WebDriver driver = new RemoteWebDriver(new StubExecutor(caps), caps);
234+
235+
WebDriver decorated = new ModifyTitleWebDriverDecorator().decorate(driver);
236+
237+
assertThat(decorated).isNotSameAs(driver);
238+
239+
WebDriver returned = getAugmenter().augment(decorated);
240+
241+
assertThat(returned).isNotSameAs(driver);
242+
assertThat(returned).isNotSameAs(decorated);
243+
assertThat(returned).isInstanceOf(LocationContext.class);
244+
245+
String title = returned.getTitle();
246+
247+
assertThat(title).isEqualTo("title");
248+
}
249+
250+
@Test
251+
public void shouldDecorateAugmentedWebDriver() {
252+
final Capabilities caps = new ImmutableCapabilities("magic.numbers", true,
253+
"numbers", true);
254+
WebDriver driver = new RemoteWebDriver(new StubExecutor(caps), caps);
255+
256+
WebDriver augmented = getAugmenter()
257+
.addDriverAugmentation("magic.numbers", HasMagicNumbers.class, (c, exe) -> () -> 42)
258+
.addDriverAugmentation("numbers", HasNumbers.class, (c, exe) -> webDriver -> {
259+
Require.precondition(webDriver instanceof HasMagicNumbers, "Driver must implement HasMagicNumbers");
260+
return ((HasMagicNumbers)webDriver).getMagicNumber();
261+
})
262+
.augment(driver);
263+
264+
WebDriver decorated = new ModifyTitleWebDriverDecorator().decorate(augmented);
265+
266+
assertThat(decorated).isNotSameAs(driver);
267+
268+
assertThat(augmented).isNotSameAs(decorated);
269+
assertThat(decorated).isInstanceOf(HasNumbers.class);
270+
271+
String title = decorated.getTitle();
272+
273+
assertThat(title).isEqualTo("title");
274+
275+
int number = ((HasNumbers)decorated).getNumbers(decorated);
276+
assertThat(number).isEqualTo(42);
277+
}
278+
228279
private static class ByMagic extends By {
229280
private final String magicWord;
230281

@@ -377,4 +428,20 @@ public Capabilities getCapabilities() {
377428
return new ImmutableCapabilities();
378429
}
379430
}
431+
432+
private static class ModifyTitleWebDriverDecorator extends WebDriverDecorator {
433+
434+
@Override
435+
public Object call(Decorated<?> target, Method method, Object[] args) throws Throwable {
436+
if (method.getDeclaringClass().equals(HasCapabilities.class)) {
437+
return new ImmutableCapabilities(CapabilityType.SUPPORTS_LOCATION_CONTEXT, true);
438+
}
439+
440+
if (method.getName().equals("getTitle")) {
441+
return "title";
442+
}
443+
444+
return super.call(target, method, args);
445+
}
446+
}
380447
}

java/test/org/openqa/selenium/remote/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ java_test_suite(
2828
"//java/src/org/openqa/selenium/ie",
2929
"//java/src/org/openqa/selenium/json",
3030
"//java/src/org/openqa/selenium/remote",
31+
"//java/src/org/openqa/selenium/support/decorators",
3132
"//java/test/org/openqa/selenium/testing:annotations",
3233
artifact("org.assertj:assertj-core"),
3334
artifact("com.google.guava:guava"),

java/test/org/openqa/selenium/support/decorators/InterfacesTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@
2828
@Category(UnitTests.class)
2929
public class InterfacesTest {
3030

31-
private interface SomeOtherInterface {}
31+
protected interface SomeOtherInterface {}
3232

33-
private interface ExtendedDriver extends WebDriver, SomeOtherInterface {}
33+
protected interface ExtendedDriver extends WebDriver, SomeOtherInterface {}
3434

3535
@Test
3636
public void shouldNotAddInterfacesNotAvailableInTheOriginalDriver() {

0 commit comments

Comments
 (0)