diff --git a/src/main/java/io/appium/java_client/pagefactory/AppiumElementLocator.java b/src/main/java/io/appium/java_client/pagefactory/AppiumElementLocator.java index 5208add47..3aba5bcdc 100644 --- a/src/main/java/io/appium/java_client/pagefactory/AppiumElementLocator.java +++ b/src/main/java/io/appium/java_client/pagefactory/AppiumElementLocator.java @@ -27,9 +27,11 @@ import org.openqa.selenium.WebElement; import org.openqa.selenium.support.ui.FluentWait; +import java.lang.ref.WeakReference; import java.time.Duration; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.function.Function; import java.util.function.Supplier; @@ -46,10 +48,36 @@ class AppiumElementLocator implements CacheableLocator { private final boolean shouldCache; private final By by; private final Duration duration; + private final WeakReference searchContextReference; private final SearchContext searchContext; + private WebElement cachedElement; private List cachedElementList; + /** + * Creates a new mobile element locator. It instantiates {@link WebElement} + * using @AndroidFindBy (-s), @iOSFindBy (-s) and @FindBy (-s) annotation + * sets + * + * @param searchContextReference The context reference to use when finding the element + * @param by a By locator strategy + * @param shouldCache is the flag that signalizes that elements which + * are found once should be cached + * @param duration timeout parameter for the element to be found + */ + AppiumElementLocator( + WeakReference searchContextReference, + By by, + boolean shouldCache, + Duration duration + ) { + this.searchContextReference = searchContextReference; + this.searchContext = null; + this.shouldCache = shouldCache; + this.duration = duration; + this.by = by; + } + /** * Creates a new mobile element locator. It instantiates {@link WebElement} * using @AndroidFindBy (-s), @iOSFindBy (-s) and @FindBy (-s) annotation @@ -61,15 +89,25 @@ class AppiumElementLocator implements CacheableLocator { * are found once should be cached * @param duration timeout parameter for the element to be found */ - - public AppiumElementLocator(SearchContext searchContext, By by, boolean shouldCache, - Duration duration) { + public AppiumElementLocator( + SearchContext searchContext, + By by, + boolean shouldCache, + Duration duration + ) { + this.searchContextReference = null; this.searchContext = searchContext; this.shouldCache = shouldCache; this.duration = duration; this.by = by; } + private Optional getSearchContext() { + return searchContext == null + ? Optional.ofNullable(searchContextReference).map(WeakReference::get) + : Optional.of(searchContext); + } + /** * This methods makes sets some settings of the {@link By} according to * the given instance of {@link SearchContext}. If there is some {@link ContentMappedBy} @@ -85,8 +123,7 @@ private static By getBy(By currentBy, SearchContext currentContent) { return currentBy; } - return ContentMappedBy.class.cast(currentBy) - .useContent(getCurrentContentType(currentContent)); + return ((ContentMappedBy) currentBy).useContent(getCurrentContentType(currentContent)); } private T waitFor(Supplier supplier) { @@ -98,8 +135,7 @@ private T waitFor(Supplier supplier) { return wait.until(function); } catch (TimeoutException e) { if (function.foundStaleElementReferenceException != null) { - throw StaleElementReferenceException - .class.cast(function.foundStaleElementReferenceException); + throw (StaleElementReferenceException) function.foundStaleElementReferenceException; } throw e; } @@ -113,10 +149,15 @@ public WebElement findElement() { return cachedElement; } + SearchContext searchContext = getSearchContext() + .orElseThrow(() -> new IllegalStateException( + String.format("The element %s is not locatable anymore " + + "because its context has been garbage collected", by) + )); + By bySearching = getBy(this.by, searchContext); try { - WebElement result = waitFor(() -> - searchContext.findElement(bySearching)); + WebElement result = waitFor(() -> searchContext.findElement(bySearching)); if (shouldCache) { cachedElement = result; } @@ -134,12 +175,17 @@ public List findElements() { return cachedElementList; } + SearchContext searchContext = getSearchContext() + .orElseThrow(() -> new IllegalStateException( + String.format("Elements %s are not locatable anymore " + + "because their context has been garbage collected", by) + )); + List result; try { result = waitFor(() -> { - List list = searchContext - .findElements(getBy(by, searchContext)); - return list.size() > 0 ? list : null; + List list = searchContext.findElements(getBy(by, searchContext)); + return list.isEmpty() ? null : list; }); } catch (TimeoutException | StaleElementReferenceException e) { result = new ArrayList<>(); @@ -171,30 +217,22 @@ public T apply(Supplier supplier) { return supplier.get(); } catch (Throwable e) { boolean isRootCauseStaleElementReferenceException = false; - Throwable shouldBeThrown; boolean isRootCauseInvalidSelector = isInvalidSelectorRootCause(e); - if (!isRootCauseInvalidSelector) { isRootCauseStaleElementReferenceException = isStaleElementReferenceException(e); } - if (isRootCauseStaleElementReferenceException) { foundStaleElementReferenceException = extractReadableException(e); } + if (isRootCauseInvalidSelector || isRootCauseStaleElementReferenceException) { + return null; + } - if (!isRootCauseInvalidSelector & !isRootCauseStaleElementReferenceException) { - shouldBeThrown = extractReadableException(e); - if (shouldBeThrown != null) { - if (NoSuchElementException.class.equals(shouldBeThrown.getClass())) { - throw NoSuchElementException.class.cast(shouldBeThrown); - } else { - throw new WebDriverException(shouldBeThrown); - } - } else { - throw new WebDriverException(e); - } + Throwable excToThrow = extractReadableException(e); + if (excToThrow instanceof WebDriverException) { + throw (WebDriverException) excToThrow; } else { - return null; + throw new WebDriverException(excToThrow); } } } diff --git a/src/main/java/io/appium/java_client/pagefactory/AppiumElementLocatorFactory.java b/src/main/java/io/appium/java_client/pagefactory/AppiumElementLocatorFactory.java index 0fc8e3d38..787e34e9e 100644 --- a/src/main/java/io/appium/java_client/pagefactory/AppiumElementLocatorFactory.java +++ b/src/main/java/io/appium/java_client/pagefactory/AppiumElementLocatorFactory.java @@ -23,6 +23,7 @@ import org.openqa.selenium.SearchContext; import javax.annotation.Nullable; +import java.lang.ref.WeakReference; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Field; import java.time.Duration; @@ -32,6 +33,7 @@ public class AppiumElementLocatorFactory implements CacheableElementLocatorFactory { private final SearchContext searchContext; + private final WeakReference searchContextReference; private final Duration duration; private final AppiumByBuilder builder; @@ -39,22 +41,46 @@ public class AppiumElementLocatorFactory implements CacheableElementLocatorFacto * Creates a new mobile element locator factory. * * @param searchContext The context to use when finding the element - * @param duration timeout parameters for the elements to be found - * @param builder is handler of Appium-specific page object annotations + * @param duration timeout parameters for the elements to be found + * @param builder is handler of Appium-specific page object annotations */ - public AppiumElementLocatorFactory(SearchContext searchContext, Duration duration, - AppiumByBuilder builder) { + public AppiumElementLocatorFactory( + SearchContext searchContext, + Duration duration, + AppiumByBuilder builder + ) { this.searchContext = searchContext; + this.searchContextReference = null; this.duration = duration; this.builder = builder; } - public @Nullable CacheableLocator createLocator(Field field) { - return this.createLocator((AnnotatedElement) field); + /** + * Creates a new mobile element locator factory. + * + * @param searchContextReference The context reference to use when finding the element + * @param duration timeout parameters for the elements to be found + * @param builder is handler of Appium-specific page object annotations + */ + AppiumElementLocatorFactory( + WeakReference searchContextReference, + Duration duration, + AppiumByBuilder builder + ) { + this.searchContextReference = searchContextReference; + this.searchContext = null; + this.duration = duration; + this.builder = builder; } + @Nullable @Override + public CacheableLocator createLocator(Field field) { + return this.createLocator((AnnotatedElement) field); + } + @Nullable + @Override public CacheableLocator createLocator(AnnotatedElement annotatedElement) { Duration customDuration; if (annotatedElement.isAnnotationPresent(WithTimeout.class)) { @@ -63,14 +89,13 @@ public CacheableLocator createLocator(AnnotatedElement annotatedElement) { } else { customDuration = duration; } - builder.setAnnotated(annotatedElement); By byResult = builder.buildBy(); - return ofNullable(byResult) - .map(by -> new AppiumElementLocator(searchContext, by, builder.isLookupCached(), customDuration)) + .map(by -> searchContextReference != null + ? new AppiumElementLocator(searchContextReference, by, builder.isLookupCached(), customDuration) + : new AppiumElementLocator(searchContext, by, builder.isLookupCached(), customDuration) + ) .orElse(null); } - - } diff --git a/src/main/java/io/appium/java_client/pagefactory/AppiumFieldDecorator.java b/src/main/java/io/appium/java_client/pagefactory/AppiumFieldDecorator.java index b1119f34e..c2f11f473 100644 --- a/src/main/java/io/appium/java_client/pagefactory/AppiumFieldDecorator.java +++ b/src/main/java/io/appium/java_client/pagefactory/AppiumFieldDecorator.java @@ -30,9 +30,11 @@ import org.openqa.selenium.remote.RemoteWebElement; import org.openqa.selenium.support.pagefactory.DefaultFieldDecorator; import org.openqa.selenium.support.pagefactory.ElementLocator; +import org.openqa.selenium.support.pagefactory.ElementLocatorFactory; import org.openqa.selenium.support.pagefactory.FieldDecorator; import javax.annotation.Nullable; +import java.lang.ref.WeakReference; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; @@ -60,11 +62,13 @@ */ public class AppiumFieldDecorator implements FieldDecorator { - private static final List> availableElementClasses = ImmutableList.of(WebElement.class, - RemoteWebElement.class); + private static final List> availableElementClasses = ImmutableList.of( + WebElement.class, + RemoteWebElement.class + ); public static final Duration DEFAULT_WAITING_TIMEOUT = ofSeconds(1); - private final WebDriver webDriver; - private final DefaultFieldDecorator defaultElementFieldDecoracor; + private final WeakReference webDriverReference; + private final DefaultFieldDecorator defaultElementFieldDecorator; private final AppiumElementLocatorFactory widgetLocatorFactory; private final String platform; private final String automation; @@ -79,10 +83,45 @@ public class AppiumFieldDecorator implements FieldDecorator { * @param duration is a desired duration of the waiting for an element presence. */ public AppiumFieldDecorator(SearchContext context, Duration duration) { - this.webDriver = unpackWebDriverFromSearchContext(context); + WebDriver wd = unpackWebDriverFromSearchContext(context); + this.webDriverReference = wd == null ? null : new WeakReference<>(wd); + if (wd instanceof HasCapabilities) { + Capabilities caps = ((HasCapabilities) wd).getCapabilities(); + this.platform = CapabilityHelpers.getCapability(caps, CapabilityType.PLATFORM_NAME, String.class); + this.automation = CapabilityHelpers.getCapability(caps, MobileCapabilityType.AUTOMATION_NAME, String.class); + } else { + this.platform = null; + this.automation = null; + } + + this.duration = duration; + + defaultElementFieldDecorator = createFieldDecorator(new AppiumElementLocatorFactory( + context, duration, new DefaultElementByBuilder(platform, automation) + )); + + widgetLocatorFactory = new AppiumElementLocatorFactory( + context, duration, new WidgetByBuilder(platform, automation) + ); + } + + public AppiumFieldDecorator(SearchContext context) { + this(context, DEFAULT_WAITING_TIMEOUT); + } - if (this.webDriver instanceof HasCapabilities) { - Capabilities caps = ((HasCapabilities) this.webDriver).getCapabilities(); + /** + * Creates field decorator based on {@link SearchContext} and timeout {@code duration}. + * + * @param contextReference reference to {@link SearchContext} + * It may be the instance of {@link WebDriver} or {@link WebElement} or + * {@link Widget} or some other user's extension/implementation. + * @param duration is a desired duration of the waiting for an element presence. + */ + AppiumFieldDecorator(WeakReference contextReference, Duration duration) { + WebDriver wd = unpackWebDriverFromSearchContext(contextReference.get()); + this.webDriverReference = wd == null ? null : new WeakReference<>(wd); + if (wd instanceof HasCapabilities) { + Capabilities caps = ((HasCapabilities) wd).getCapabilities(); this.platform = CapabilityHelpers.getCapability(caps, CapabilityType.PLATFORM_NAME, String.class); this.automation = CapabilityHelpers.getCapability(caps, MobileCapabilityType.AUTOMATION_NAME, String.class); } else { @@ -92,9 +131,17 @@ public AppiumFieldDecorator(SearchContext context, Duration duration) { this.duration = duration; - defaultElementFieldDecoracor = new DefaultFieldDecorator( - new AppiumElementLocatorFactory(context, duration, new DefaultElementByBuilder(platform, automation)) - ) { + defaultElementFieldDecorator = createFieldDecorator(new AppiumElementLocatorFactory( + contextReference, duration, new DefaultElementByBuilder(platform, automation) + )); + + widgetLocatorFactory = new AppiumElementLocatorFactory( + contextReference, duration, new WidgetByBuilder(platform, automation) + ); + } + + private DefaultFieldDecorator createFieldDecorator(ElementLocatorFactory factory) { + return new DefaultFieldDecorator(factory) { @Override protected WebElement proxyForLocator(ClassLoader ignored, ElementLocator locator) { return proxyForAnElement(locator); @@ -126,14 +173,6 @@ protected boolean isDecoratableList(Field field) { .anyMatch((webElClass) -> webElClass.equals(listType) || bounds.contains(webElClass)); } }; - - widgetLocatorFactory = new AppiumElementLocatorFactory( - context, duration, new WidgetByBuilder(platform, automation) - ); - } - - public AppiumFieldDecorator(SearchContext context) { - this(context, DEFAULT_WAITING_TIMEOUT); } /** @@ -144,7 +183,7 @@ public AppiumFieldDecorator(SearchContext context) { * @return a field value or null. */ public Object decorate(ClassLoader ignored, Field field) { - Object result = defaultElementFieldDecoracor.decorate(ignored, field); + Object result = defaultElementFieldDecorator.decorate(ignored, field); return result == null ? decorateWidget(field) : result; } @@ -191,7 +230,7 @@ private Object decorateWidget(Field field) { if (isAlist) { return getEnhancedProxy( ArrayList.class, - new WidgetListInterceptor(locator, webDriver, map, widgetType, duration) + new WidgetListInterceptor(locator, webDriverReference, map, widgetType, duration) ); } @@ -200,12 +239,12 @@ private Object decorateWidget(Field field) { widgetType, new Class[]{constructor.getParameterTypes()[0]}, new Object[]{proxyForAnElement(locator)}, - new WidgetInterceptor(locator, webDriver, null, map, duration) + new WidgetInterceptor(locator, webDriverReference, null, map, duration) ); } private WebElement proxyForAnElement(ElementLocator locator) { - ElementInterceptor elementInterceptor = new ElementInterceptor(locator, webDriver); + ElementInterceptor elementInterceptor = new ElementInterceptor(locator, webDriverReference); return getEnhancedProxy(RemoteWebElement.class, elementInterceptor); } } diff --git a/src/main/java/io/appium/java_client/pagefactory/ElementInterceptor.java b/src/main/java/io/appium/java_client/pagefactory/ElementInterceptor.java index 5047fcc11..82b61990b 100644 --- a/src/main/java/io/appium/java_client/pagefactory/ElementInterceptor.java +++ b/src/main/java/io/appium/java_client/pagefactory/ElementInterceptor.java @@ -21,6 +21,7 @@ import org.openqa.selenium.WebElement; import org.openqa.selenium.support.pagefactory.ElementLocator; +import java.lang.ref.WeakReference; import java.lang.reflect.Method; import static io.appium.java_client.pagefactory.ThrowableUtil.extractReadableException; @@ -30,7 +31,7 @@ */ public class ElementInterceptor extends InterceptorOfASingleElement { - public ElementInterceptor(ElementLocator locator, WebDriver driver) { + public ElementInterceptor(ElementLocator locator, WeakReference driver) { super(locator, driver); } diff --git a/src/main/java/io/appium/java_client/pagefactory/WidgetInterceptor.java b/src/main/java/io/appium/java_client/pagefactory/WidgetInterceptor.java index 9a9d1133d..d00c6e3c9 100644 --- a/src/main/java/io/appium/java_client/pagefactory/WidgetInterceptor.java +++ b/src/main/java/io/appium/java_client/pagefactory/WidgetInterceptor.java @@ -24,6 +24,7 @@ import org.openqa.selenium.support.PageFactory; import javax.annotation.Nullable; +import java.lang.ref.WeakReference; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -34,27 +35,29 @@ import static io.appium.java_client.pagefactory.ThrowableUtil.extractReadableException; import static io.appium.java_client.pagefactory.utils.WebDriverUnpackUtility.getCurrentContentType; +import static java.util.Optional.ofNullable; public class WidgetInterceptor extends InterceptorOfASingleElement { private final Map> instantiationMap; private final Map cachedInstances = new HashMap<>(); private final Duration duration; - private WebElement cachedElement; + private WeakReference cachedElementReference; /** * Proxy interceptor class for widgets. */ public WidgetInterceptor( + @Nullable CacheableLocator locator, - WebDriver driver, + WeakReference driverReference, @Nullable - WebElement cachedElement, + WeakReference cachedElementReference, Map> instantiationMap, Duration duration ) { - super(locator, driver); - this.cachedElement = cachedElement; + super(locator, driverReference); + this.cachedElementReference = cachedElementReference; this.instantiationMap = instantiationMap; this.duration = duration; } @@ -62,24 +65,24 @@ public WidgetInterceptor( @Override protected Object getObject(WebElement element, Method method, Object[] args) throws Throwable { ContentType type = getCurrentContentType(element); - if (cachedElement == null + WebElement cachedElement = cachedElementReference == null ? null : cachedElementReference.get(); + if (cachedElement == null || !cachedInstances.containsKey(type) || (locator != null && !((CacheableLocator) locator).isLookUpCached()) - || cachedInstances.isEmpty() ) { - cachedElement = element; + cachedElementReference = new WeakReference<>(element); Constructor constructor = instantiationMap.get(type); Class clazz = constructor.getDeclaringClass(); if (Modifier.isAbstract(clazz.getModifiers())) { throw new InstantiationException( - String.format("%s is abstract so it cannot be instantiated", clazz.getName()) + String.format("%s is abstract so it cannot be instantiated", clazz.getName()) ); } - Widget widget = constructor.newInstance(cachedElement); + Widget widget = constructor.newInstance(element); cachedInstances.put(type, widget); - PageFactory.initElements(new AppiumFieldDecorator(widget, duration), widget); + PageFactory.initElements(new AppiumFieldDecorator(new WeakReference<>(widget), duration), widget); } try { method.setAccessible(true); @@ -91,8 +94,9 @@ protected Object getObject(WebElement element, Method method, Object[] args) thr @Override public Object call(Object obj, Method method, Object[] args, Callable original) throws Throwable { - return locator == null - ? getObject(cachedElement, method, args) + WebElement element = ofNullable(cachedElementReference).map(WeakReference::get).orElse(null); + return locator == null && element != null + ? getObject(element, method, args) : super.call(obj, method, args, original); } } diff --git a/src/main/java/io/appium/java_client/pagefactory/WidgetListInterceptor.java b/src/main/java/io/appium/java_client/pagefactory/WidgetListInterceptor.java index 136ce8e4d..031a0e624 100644 --- a/src/main/java/io/appium/java_client/pagefactory/WidgetListInterceptor.java +++ b/src/main/java/io/appium/java_client/pagefactory/WidgetListInterceptor.java @@ -22,12 +22,16 @@ import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; +import javax.annotation.Nullable; +import java.lang.ref.WeakReference; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; import static io.appium.java_client.pagefactory.ThrowableUtil.extractReadableException; import static io.appium.java_client.pagefactory.utils.ProxyFactory.getEnhancedProxy; @@ -39,15 +43,20 @@ public class WidgetListInterceptor extends InterceptorOfAListOfElements { private final List cachedWidgets = new ArrayList<>(); private final Class declaredType; private final Duration duration; - private final WebDriver driver; - private List cachedElements; + private final WeakReference driver; + private final List> cachedElementReferences = new ArrayList<>(); /** * Proxy interceptor class for lists of widgets. */ - public WidgetListInterceptor(CacheableLocator locator, WebDriver driver, - Map> instantiationMap, - Class declaredType, Duration duration) { + public WidgetListInterceptor( + @Nullable + CacheableLocator locator, + WeakReference driver, + Map> instantiationMap, + Class declaredType, + Duration duration + ) { super(locator); this.instantiationMap = instantiationMap; this.declaredType = declaredType; @@ -57,20 +66,27 @@ public WidgetListInterceptor(CacheableLocator locator, WebDriver driver, @Override protected Object getObject(List elements, Method method, Object[] args) throws Throwable { - if (cachedElements == null || (locator != null && !((CacheableLocator) locator).isLookUpCached())) { - cachedElements = elements; + List cachedElements = cachedElementReferences.stream() + .map(WeakReference::get) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + if (cachedElements.size() != cachedWidgets.size() + || (locator != null && !((CacheableLocator) locator).isLookUpCached())) { cachedWidgets.clear(); + cachedElementReferences.clear(); ContentType type = null; - for (WebElement element : cachedElements) { + for (WebElement element : elements) { type = ofNullable(type).orElseGet(() -> getCurrentContentType(element)); Class[] params = new Class[] {instantiationMap.get(type).getParameterTypes()[0]}; + WeakReference elementReference = new WeakReference<>(element); cachedWidgets.add( getEnhancedProxy( declaredType, params, new Object[] {element}, - new WidgetInterceptor(null, driver, element, instantiationMap, duration) + new WidgetInterceptor(null, driver, elementReference, instantiationMap, duration) ) ); + cachedElementReferences.add(elementReference); } } try { diff --git a/src/main/java/io/appium/java_client/pagefactory/interceptors/InterceptorOfAListOfElements.java b/src/main/java/io/appium/java_client/pagefactory/interceptors/InterceptorOfAListOfElements.java index d276ce616..62e1442aa 100644 --- a/src/main/java/io/appium/java_client/pagefactory/interceptors/InterceptorOfAListOfElements.java +++ b/src/main/java/io/appium/java_client/pagefactory/interceptors/InterceptorOfAListOfElements.java @@ -20,6 +20,7 @@ import org.openqa.selenium.WebElement; import org.openqa.selenium.support.pagefactory.ElementLocator; +import javax.annotation.Nullable; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; @@ -28,7 +29,7 @@ public abstract class InterceptorOfAListOfElements implements MethodCallListener { protected final ElementLocator locator; - public InterceptorOfAListOfElements(ElementLocator locator) { + public InterceptorOfAListOfElements(@Nullable ElementLocator locator) { this.locator = locator; } @@ -38,7 +39,7 @@ protected abstract Object getObject( @Override public Object call(Object obj, Method method, Object[] args, Callable original) throws Throwable { - if (Object.class.equals(method.getDeclaringClass())) { + if (locator == null || Object.class.equals(method.getDeclaringClass())) { return original.call(); } diff --git a/src/main/java/io/appium/java_client/pagefactory/interceptors/InterceptorOfASingleElement.java b/src/main/java/io/appium/java_client/pagefactory/interceptors/InterceptorOfASingleElement.java index 7ac12b3d6..7eea82233 100644 --- a/src/main/java/io/appium/java_client/pagefactory/interceptors/InterceptorOfASingleElement.java +++ b/src/main/java/io/appium/java_client/pagefactory/interceptors/InterceptorOfASingleElement.java @@ -22,22 +22,32 @@ import org.openqa.selenium.WrapsDriver; import org.openqa.selenium.support.pagefactory.ElementLocator; +import javax.annotation.Nullable; +import java.lang.ref.WeakReference; import java.lang.reflect.Method; import java.util.concurrent.Callable; public abstract class InterceptorOfASingleElement implements MethodCallListener { protected final ElementLocator locator; - protected final WebDriver driver; + private final WeakReference driverReference; - public InterceptorOfASingleElement(ElementLocator locator, WebDriver driver) { + public InterceptorOfASingleElement( + @Nullable + ElementLocator locator, + WeakReference driverReference + ) { this.locator = locator; - this.driver = driver; + this.driverReference = driverReference; } protected abstract Object getObject(WebElement element, Method method, Object[] args) throws Throwable; @Override public Object call(Object obj, Method method, Object[] args, Callable original) throws Throwable { + if (locator == null) { + return original.call(); + } + if (method.getName().equals("toString") && args.length == 0) { return locator.toString(); } @@ -48,7 +58,7 @@ public Object call(Object obj, Method method, Object[] args, Callable origina if (WrapsDriver.class.isAssignableFrom(method.getDeclaringClass()) && method.getName().equals("getWrappedDriver")) { - return driver; + return driverReference.get(); } WebElement realElement = locator.findElement();