Java-client: IllegalArgumentException when calling PageFactory.initElements(new AppiumFieldDecorator(androidDriver), this)

Created on 5 Feb 2019  路  11Comments  路  Source: appium/java-client

The problem

When trying to init PageObjects classes with annotations like AndroidFindBy, i am unable to get past the PageFactory.initElements(new AppiumFieldDecorator(androidDriver), this). Test will fail with illegal argument exception and not much details as to which argument is invalid and why.

Environment

appium version: 1.10.1
appium java client: 7.0.0
node ver: v9.10.1
android im: pixel 2 xl api 27
desktop: mac (latest)

Link to Appium logs

Appium log: https://gist.github.com/bill2605/583f868e59f7852f4aebfc42f942c6cd

Code To Reproduce Issue

Note that i am using Spring for my framework. That being said below is the code to initialize an AndroidDriver to be used by the pageObjects.

`@Service
@Profile("android")
public class AndroidDriverLoader {

private final Logger logger = LoggerFactory.getLogger(AndroidDriverLoader.class);

@Resource
protected ConfigProperty configProperty;

public AndroidDriver<AndroidElement> driver;
private AppiumDriverLocalService appiumDriverLocalService;

@PostConstruct
public void androidDriverLoaderInit() {
    loadDriver();
}

/**
 * load the specific android driver based on reference
 */
private void loadDriver() {
    final DesiredCapabilities desiredCapabilities = new DesiredCapabilities();

    if (Objects.equals(configProperty.getMobileType(), "emulator")) {
        desiredCapabilities.setCapability(AndroidMobileCapabilityType.AVD, configProperty.getAndroidAvd());
        desiredCapabilities.setCapability(MobileCapabilityType.APP, configProperty.getAndroidApp());

        appiumDriverLocalService = getDefaultAppiumLocalService();
        appiumDriverLocalService.start();

        driver = new AndroidDriver<>(appiumDriverLocalService, getDefaultAndroidCapabilities().merge(desiredCapabilities));
    } 
}

private DesiredCapabilities getDefaultAndroidCapabilities() {
    final DesiredCapabilities desiredCapabilities = new DesiredCapabilities();
    desiredCapabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, AutomationName.ANDROID_UIAUTOMATOR2);
    desiredCapabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, Platform.ANDROID);
    desiredCapabilities.setCapability(MobileCapabilityType.DEVICE_NAME, configProperty.getAndroidDeviceName());
    desiredCapabilities.setCapability(AndroidMobileCapabilityType.NO_SIGN, true);
    desiredCapabilities.setCapability(MobileCapabilityType.FULL_RESET, false);
    desiredCapabilities.setCapability(MobileCapabilityType.NO_RESET, true);
    desiredCapabilities
            .setCapability(MobileCapabilityType.NEW_COMMAND_TIMEOUT, Integer.parseInt(configProperty.getCommandTimeOut()));

    return desiredCapabilities;
}

/**
 * return default appium local service
 *
 * @return {@link AppiumDriverLocalService}
 */
private AppiumDriverLocalService getDefaultAppiumLocalService() {
    return new AppiumServiceBuilder()
            .usingDriverExecutable(Paths.get(configProperty.getAppiumDriverExecutable()).toFile())
            .withAppiumJS(Paths.get(configProperty.getAppiumJs()).toFile())
            .withIPAddress(configProperty.getServiceIPAddress())
            .usingPort(Integer.parseInt(configProperty.getAppiumPort()))
            .withArgument(GeneralServerFlag.LOCAL_TIMEZONE)
            .build();
}

public AndroidDriver<AndroidElement> getDriver() {
    return driver;
}`

Each PageObject extends AndroidAbstractView
which holds the following information:
`@Service
@Profile("android")
public abstract class AndroidAbstractView implements RemoteElementUtils {

private final Logger logger = LoggerFactory.getLogger(AndroidAbstractView.class);

@Resource
protected AndroidDriverLoader androidDriverLoader;
@Resource
protected ConfigProperty configProperty;

protected static AppiumDriver androidDriver;

private String iconLoadingId = "item_loading_view";
private String loadingViewRefreshXpath = "//android.view.ViewGroup[contains(@resource-id, 'id/swipe_refresh_layout')]/android.widget.ImageView";

@PostConstruct
protected abstract void init();

Note that RemoteElementUtils is just an interface for all findElementby, waitForVisibible to be implemented for Web, Android and IOS (nothing fancy there)

PageObject is as follow:
`@Service
@Profile("android")
public class LoginView extends AndroidAbstractView {

@AndroidFindBy(xpath = "//*[contains(@resource-id, 'login_username_auto_complete_text_view')]")
protected AndroidElement inputUserName;
@AndroidFindBy(xpath = "//*[contains(@resource-id, 'login_next_button')]")
protected AndroidElement buttonContinue;
@AndroidFindBy(xpath = "//*[contains(@resource-id, 'login_password_edit_text')]")
protected AndroidElement inputPassword;
@AndroidFindBy(xpath = "//*[contains(@resource-id, 'login_password_log_in_button')]")
protected AndroidElement buttonLogin;
@AndroidFindBy(id = "login_username_dev_stuff_button")
protected AndroidElement buttonDevStuff;
@AndroidFindBy(id = "def_stuff_api_base_url_edit_text")
protected AndroidElement inputApiEndPointUrl;
@AndroidFindBy(id = "def_stuff_api_base_url_button")
protected AndroidElement buttonApply;

@PostConstruct
public void init()
{
    this.androidDriver = androidDriverLoader.getDriver();
    PageFactory.initElements(new AppiumFieldDecorator(androidDriver), this);
}

public void login(final String userName, final String password) {...}`

When running a test for android, using debugger i see that the driver is created and ready to be used. but whenever it reaches PageFactory.initElements(new AppiumFieldDecorator(androidDriver), this);

the following error is returned:
Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'loginView': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException Exception in thread "main" java.lang.IllegalStateException: Failed to load ApplicationContext at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124) at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83) at cucumber.runtime.java.spring.CucumberTestContextManager.getContext(SpringFactory.java:247) at cucumber.runtime.java.spring.CucumberTestContextManager.<init>(SpringFactory.java:239) at cucumber.runtime.java.spring.SpringFactory.start(SpringFactory.java:132) at cucumber.runtime.java.JavaBackend.buildWorld(JavaBackend.java:114) at cucumber.runner.Runner.buildBackendWorlds(Runner.java:120) at cucumber.runner.Runner.runPickle(Runner.java:38) at cucumber.runtime.Runtime$1.run(Runtime.java:84) at cucumber.runtime.Runtime$SameThreadExecutorService.execute(Runtime.java:220) at cucumber.runtime.Runtime.run(Runtime.java:81) at cucumber.api.cli.Main.run(Main.java:26) at cucumber.api.cli.Main.main(Main.java:8) Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'loginView': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:139) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:419) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1736) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:576) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:498) at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:848) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:865) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:548) at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:128) at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:60) at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:108) at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:251) at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98) at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116) ... 12 more Caused by: java.lang.IllegalArgumentException at net.sf.cglib.asm.ClassReader.<init>(Unknown Source) at net.sf.cglib.asm.ClassReader.<init>(Unknown Source) at net.sf.cglib.asm.ClassReader.<init>(Unknown Source) at net.sf.cglib.proxy.BridgeMethodResolver.resolveAll(BridgeMethodResolver.java:61) at net.sf.cglib.proxy.Enhancer.emitMethods(Enhancer.java:911) at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:498) at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25) at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216) at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377) at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:304) at io.appium.java_client.pagefactory.utils.ProxyFactory.getEnhancedProxy(ProxyFactory.java:55) at io.appium.java_client.pagefactory.utils.ProxyFactory.getEnhancedProxy(ProxyFactory.java:33) at io.appium.java_client.pagefactory.AppiumFieldDecorator.proxyForAnElement(AppiumFieldDecorator.java:217) at io.appium.java_client.pagefactory.AppiumFieldDecorator.access$0(AppiumFieldDecorator.java:215) at io.appium.java_client.pagefactory.AppiumFieldDecorator$1.proxyForLocator(AppiumFieldDecorator.java:107) at org.openqa.selenium.support.pagefactory.DefaultFieldDecorator.decorate(DefaultFieldDecorator.java:62) at io.appium.java_client.pagefactory.AppiumFieldDecorator.decorate(AppiumFieldDecorator.java:155) at org.openqa.selenium.support.PageFactory.proxyFields(PageFactory.java:113) at org.openqa.selenium.support.PageFactory.initElements(PageFactory.java:105) at com.workjam.platform.frontend.android.view.LoginView.init(LoginView.java:35) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:363) at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:307) at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:136) ... 29 more

When attempting to debug code i made it all the way to method from io.appium.java_client.pagefactory.utils.ProxyFactory;

public static <T> T getEnhancedProxy(Class<T> requiredClazz, Class<?>[] params, Object[] values, MethodInterceptor interceptor) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(requiredClazz); enhancer.setCallback(interceptor); return (T) enhancer.create(params, values); }

and crash occurs at return (T) enhancer.create(params, values);

bug

Most helpful comment

I encountered a similar issue as well on version 6.1.0 of the java-client. I spent some time looking into the root cause, and it seems as though this particular use of the AppiumFieldDecorator with @AndroidFindBy or @iOSFindBy may have been broken as of this dependency change: #832

I was unable to get the cglib Enhancer class to instantiate on line 52 of the ProxyFactory class.
Enhancer enhancer = new Enhancer();

java.lang.NoClassDefFoundError: Could not initialize class net.sf.cglib.proxy.Enhancer

A work-around for now is to exclude the cglib version from the appium java-client dependency in favor of specifying an older version.



io.appium
java-client
6.1.0


cglib
cglib


    <!-- This is required for page factory use with Appium -->
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.2.5</version>
    </dependency>

I'm not sure if this will resolve the exact issue that @bill2605 is seeing, but it fixed my issue. Is there a specific reason why the java-client relies on cglib 3.2.5+?

All 11 comments

Moved to java client

What's also interesting to note is that if i switch my elements from AndroidElement or MobileElement to WebElement with @FindBy over @AndroidFindByand change the page init to PageFactory.initElements(this.driver, this), i am able to access the elements. However this defeats the purpose or AndroidFindBy x AndroidElement

I encountered a similar issue as well on version 6.1.0 of the java-client. I spent some time looking into the root cause, and it seems as though this particular use of the AppiumFieldDecorator with @AndroidFindBy or @iOSFindBy may have been broken as of this dependency change: #832

I was unable to get the cglib Enhancer class to instantiate on line 52 of the ProxyFactory class.
Enhancer enhancer = new Enhancer();

java.lang.NoClassDefFoundError: Could not initialize class net.sf.cglib.proxy.Enhancer

A work-around for now is to exclude the cglib version from the appium java-client dependency in favor of specifying an older version.



io.appium
java-client
6.1.0


cglib
cglib


    <!-- This is required for page factory use with Appium -->
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.2.5</version>
    </dependency>

I'm not sure if this will resolve the exact issue that @bill2605 is seeing, but it fixed my issue. Is there a specific reason why the java-client relies on cglib 3.2.5+?

@canaangifford this fixed my issue thanks! but yes i think your question is valid and should be answered or investigated

@canaangifford @bill2605

  1. which Java version do you use?
  2. could you please share short example (http://www.sscce.org/) to reproduce the issue? I've tried the code snippets provided, the issue is not reproduced for me (openjdk 12.0.1 2019-04-16, tried cglib of versions 3.2.5,3.2.6,3.2.7, 3.2.8, 3.2.9, 3.2.10)
  3. have you tried upgrading cglib?
  • The line numbers from exception stack trace don't match to actual ones, compare:

    • net.sf.cglib.proxy.Enhancer.emitMethods(Enhancer.java:911)-> https://github.com/cglib/cglib/blob/RELEASE_3_2_6/cglib/src/main/java/net/sf/cglib/proxy/Enhancer.java#L911

    • net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:498) -> https://github.com/cglib/cglib/blob/RELEASE_3_2_6/cglib/src/main/java/net/sf/cglib/proxy/Enhancer.java#L498

  • Also taking into account presence of classes like net.sf.cglib.asm.ClassReader I can conclude that classpath contains one more conflicting dependency cglib-nodep of old version (maybe 3.1 -> https://github.com/cglib/cglib/issues/72?).
    Fix via dependency exclusion/inclusion just changes the order of jars in classpath, as result cglib is loaded first, I assume that excluding cglib and adding exactly the same version will also fix the problem.
  1. which Java version do you use?
  2. could you please share short example (http://www.sscce.org/) to reproduce the issue? I've tried the code snippets provided, the issue is not reproduced for me (openjdk 12.0.1 2019-04-16, tried cglib of versions 3.2.5,3.2.6,3.2.7, 3.2.8, 3.2.9, 3.2.10)
  3. have you tried upgrading cglib?

Keep in mind my issue was _slightly_ different than the OP. That said:

  1. I am seeing the issue with java8 primarily. Perhaps it would work with java12, I can try that.

  2. I will put together something, but I was unable to get anything to run when creating an AppiumFieldDecorator and passing it into initElements() while creating elements with @iOSFindBy or the android equivalent. Note that the standard @FindBy you'd use with the webview elements _did_ work.

  3. Upgrading cglib was the issue itself, using the version included with the java-client or any version later than 3.2.5 would not work.

I completely agree with what you said here. I almost think it's a bad interaction with cglib itself, unfortunately no version after 3.2.5 worked successfully for me. I opened the pr just to generate some discussion as it looks like I am not the only one who has run into this issue. Thanks for your time!

@canaangifford what about this https://github.com/appium/java-client/issues/1100#issuecomment-505208545 ?
I was able to reproduce your issue by adding conflicting dependency cglib:cglib-nodep:3.1 to classpath:

Caused by: java.lang.IllegalArgumentException
        at net.sf.cglib.asm.ClassReader.<init>(Unknown Source)
        at net.sf.cglib.asm.ClassReader.<init>(Unknown Source)
        at net.sf.cglib.asm.ClassReader.<init>(Unknown Source)
        at net.sf.cglib.proxy.BridgeMethodResolver.resolveAll(BridgeMethodResolver.java:61)
        at net.sf.cglib.proxy.Enhancer.emitMethods(Enhancer.java:911)
        at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:498)
        at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
        at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216)
        at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
        at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:304)
        at io.appium.java_client.pagefactory.utils.ProxyFactory.getEnhancedProxy(ProxyFactory.java:55)
        at io.appium.java_client.pagefactory.utils.ProxyFactory.getEnhancedProxy(ProxyFactory.java:33)
        at io.appium.java_client.pagefactory.AppiumFieldDecorator.proxyForAnElement(AppiumFieldDecorator.java:217)
        at io.appium.java_client.pagefactory.AppiumFieldDecorator.access$0(AppiumFieldDecorator.java:215)
        at io.appium.java_client.pagefactory.AppiumFieldDecorator$1.proxyForLocator(AppiumFieldDecorator.java:107)
        at org.openqa.selenium.support.pagefactory.DefaultFieldDecorator.decorate(DefaultFieldDecorator.java:62)
        at io.appium.java_client.pagefactory.AppiumFieldDecorator.decorate(AppiumFieldDecorator.java:155)
        at org.openqa.selenium.support.PageFactory.proxyFields(PageFactory.java:113)
        at org.openqa.selenium.support.PageFactory.initElements(PageFactory.java:105)
...

So I'm 99% sure that issue is caused by conflicting dependencies in the classpath.

@canaangifford @bill2605 do you have any updates?

@valfirst That makes the most sense to me, though I tried this suggestion:

Fix via dependency exclusion/inclusion just changes the order of jars in classpath, as result cglib is loaded first, I assume that excluding cglib and adding exactly the same version will also fix the problem.

and it did not work the same way. That said, there is no reason to leave the pr open so I've closed it for now.

I encountered a similar issue as well on version 6.1.0 of the java-client. I spent some time looking into the root cause, and it seems as though this particular use of the AppiumFieldDecorator with @AndroidFindBy or @iOSFindBy may have been broken as of this dependency change: #832

I was unable to get the cglib Enhancer class to instantiate on line 52 of the ProxyFactory class.
Enhancer enhancer = new Enhancer();

java.lang.NoClassDefFoundError: Could not initialize class net.sf.cglib.proxy.Enhancer

A work-around for now is to exclude the cglib version from the appium java-client dependency in favor of specifying an older version.

    <dependency>
        <groupId>io.appium</groupId>
        <artifactId>java-client</artifactId>
        <version>6.1.0</version>
        <exclusions>
            <exclusion>
                <groupId>cglib</groupId>
                <artifactId>cglib</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

    <!-- This is required for page factory use with Appium -->
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.2.5</version>
    </dependency>

I'm not sure if this will resolve the exact issue that @bill2605 is seeing, but it fixed my issue. Is there a specific reason why the java-client relies on cglib 3.2.5+?

Even adding exclusion to "java-client" and including same version of cglig dependency 3.2.12 solved the issue.

Was this page helpful?
0 / 5 - 0 ratings