繁体   English   中英

如何在第三方Maven依赖项中使用执行切入点?

[英]How to use execution pointcut with third-party maven dependency?

我正在尝试开发一个AspectJ方面,该方面将自动吞噬Selenium-Java的WebDriver实例(包括WebDriver多个子类ChromeDriverFirefoxDriver等)引发的任何ElementNotVisibleExceptionStaleElementReferenceException s( RuntimeException子类)。

基本上,在非AOP上下文中使用Selenium处理ENVESERE异常的标准建议解决方案是简单地重试。 然后再次。 再有必要的话。

这样的事情将在功能范例中起作用:

public void tryWhileStale(Runnable r)
{
        int n = 0;
        while(n < 5)
        {
            try
            {
                r.run();
                break;
            }
            catch(StaleElementReferenceException | ElementNotVisibleException e){}
            n++;
            Thread.sleep(2000);
        }
        throw new RuntimeException("Timed out retrying");
}

然后,在以后使用WebDriver时:

tryWhileStale(() -> driver.findElement(By.xpath(...)).click());

但是,这会增加很多我想避免的额外类型(以及很可能会真正忘记tryWhileStale()包装器的真正可能性)。

我不想下载selenium-java的副本,编辑源代码并重建,因为我是直接从公共Maven存储库中提取Selenium的。

我希望AspectJ能够弄清楚如何做到这一点,所以我做了一些研究,意识到我需要一个带有execution切入点的around建议。 如果我使用call而不是execution ,它将成功触发,但是不会吞下异常。 这对我来说是个神秘的原因,因为从我编写的方面来看,从我的代码流看来,它会捕获在proceed()调用中抛出的所有内容。

但是, execution()切入点也不起作用! 这是因为即使我的pom.xml中具有weaveDependencyweaveDependency编织我的类,但并未编织Selenium-Java! 踢球是call()只有您的类梭织,同时execution()如果你调用类编织才起作用。 显然,如果同时编织了您的课程和第三方课程,那么任何一个都可以使用。

有什么方法可以完全放弃AOP或Maven吗? 如果能够编织selenium-java,下面的代码应该可以正常工作:

@Aspect
class MyAspect {

    @Around("execution (WebElement *.findElement(By))")
    public Object around(ProceedingJoinPoint pjp) 
    {
        Object f = null;
        int n = 0;
        do
        {
            try
            {
                System.err.println("Before " + this.toString());
                f = pjp.proceed();
                System.err.println("After " + this.toString());
                return f;
            }
            catch(Throwable t)
            {
                try { Thread.sleep(5000); } catch(InterruptedException ie) { break; }
                System.err.println("Waiting 5 seconds because of " + t.getClass().getSimpleName());
            }
            n++;
        } while(n < 5);
        System.err.println("Gave up waiting");
        return null;
    }
}

我有点好奇,并设置了一个示例项目,该项目具有方面拦截call(WebElement WebDriver+.findElement(*)) (类似于您的方法)并且还call(void WebElement+.click()) 为了模拟一些WebDriver异常(例如NoSuchElementExceptionStaleElementReferenceException ),我使用了W3schools的带有内联框架(iframe)示例页面 如果仅将焦点从主框架切换到iframe并尝试从前一个框架访问元素(反之亦然),则这很容易。

我的示例方面不等待n秒,而是遍历main和所有iframe,以便在该情况下重新发出原始调用。 使示例代码适应您的需求应该非常容易。

哦,顺便说一句,我使用的是本机AspectJ语法,而不是基于注释的语法。 希望您不要介意,我发现本机语法更富表现力和优雅。

Maven POM构建项目:

pom.xml包含一些额外的插件,用于

  • 构建一个包含AspectJ运行时和所有其他依赖项的单个可执行JAR( 一个jar )。 分发和运行已编译程序非常方便。
  • 我还包括exec-maven ,以便使您能够通过mvn clean compile exec:java轻松运行程序。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>de.scrum-master.stackoverflow</groupId>
    <artifactId>selenium-aspectj-retry</artifactId>
    <version>1.0-SNAPSHOT</version>

    <name>Selenium auto-retry via AspectJ</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.source-target.version>1.8</java.source-target.version>
        <aspectj.version>1.8.7</aspectj.version>
        <main-class>de.scrum_master.app.Application</main-class>
    </properties>

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.3</version>
                    <configuration>
                        <source>${java.source-target.version}</source>
                        <target>${java.source-target.version}</target>
                        <!-- IMPORTANT -->
                        <useIncrementalCompilation>false</useIncrementalCompilation>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>aspectj-maven-plugin</artifactId>
                    <version>1.8</version>
                    <configuration>
                        <!--<showWeaveInfo>true</showWeaveInfo> -->
                        <source>${java.source-target.version}</source>
                        <target>${java.source-target.version}</target>
                        <Xlint>ignore</Xlint>
                        <complianceLevel>${java.source-target.version}</complianceLevel>
                        <encoding>${project.build.sourceEncoding}</encoding>
                        <!--<verbose>true</verbose> -->
                        <!--<warn>constructorName,packageDefaultMethod,deprecation,maskedCatchBlocks,unusedLocals,unusedArguments,unusedImport</warn> -->
                    </configuration>
                    <executions>
                        <execution>
                            <!-- IMPORTANT -->
                            <phase>process-sources</phase>
                            <goals>
                                <goal>compile</goal>
                                <goal>test-compile</goal>
                            </goals>
                        </execution>
                    </executions>
                    <dependencies>
                        <dependency>
                            <groupId>org.aspectj</groupId>
                            <artifactId>aspectjtools</artifactId>
                            <version>${aspectj.version}</version>
                        </dependency>
                    </dependencies>
                </plugin>
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>exec-maven-plugin</artifactId>
                    <version>1.4.0</version>
                    <configuration>
                        <mainClass>${main-class}</mainClass>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.dstovall</groupId>
                    <artifactId>onejar-maven-plugin</artifactId>
                    <version>1.4.4</version>
                    <executions>
                        <execution>
                            <goals>
                                <goal>one-jar</goal>
                            </goals>
                        </execution>
                    </executions>
                    <configuration>
                        <onejarVersion>0.96</onejarVersion>
                        <mainClass>${main-class}</mainClass>
                        <attachToBuild>true</attachToBuild>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
            </plugin>
            <!--
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
            </plugin>
            -->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <configuration>
                    <mainClass>${main-class}</mainClass>
                    <cleanupDaemonThreads>false</cleanupDaemonThreads>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.dstovall</groupId>
                <artifactId>onejar-maven-plugin</artifactId>
                <configuration>
                    <mainClass>${main-class}</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <pluginRepositories>
        <pluginRepository>
            <id>OneJAR googlecode.com</id>
            <url>http://onejar-maven-plugin.googlecode.com/svn/mavenrepo</url>
        </pluginRepository>
    </pluginRepositories>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjrt</artifactId>
                <version>${aspectj.version}</version>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>org.seleniumhq.selenium</groupId>
                <artifactId>selenium-java</artifactId>
                <version>2.48.2</version>
            </dependency>
            <dependency>
                <groupId>io.github.bonigarcia</groupId>
                <artifactId>webdrivermanager</artifactId>
                <version>1.3.0</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
        </dependency>
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
        </dependency>
        <dependency>
            <groupId>io.github.bonigarcia</groupId>
            <artifactId>webdrivermanager</artifactId>
        </dependency>
    </dependencies>

    <organization>
        <name>Scrum-Master.de - Agile Project Management</name>
        <url>http://scrum-master.de</url>
    </organization>

</project>

Java驱动程序应用程序:

如您所见,该应用程序仅包含第二个切入点所需的WebDriver引用(第一个切入点不需要它,可以通过target()绑定找到它)。 Application类还实现了Closeable ,这使我们能够在main方法中尝试使用资源 ,并确保在Application实例超出范围时以自动方式关闭驱动程序。

package de.scrum_master.app;

import io.github.bonigarcia.wdm.ChromeDriverManager;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;

import java.io.Closeable;
import java.io.IOException;

public class Application implements Closeable {
    private final WebDriver driver;

    public Application() {
        ChromeDriverManager.getInstance().setup();
        driver = new ChromeDriver();
    }

    @Override
    public void close() {
        driver.quit();
    }

    public WebDriver getDriver() {
        return driver;
    }

    public void doSomething() {
        driver.get("http://www.w3schools.com/tags/tryit.asp?filename=tryhtml5_input_type_hidden");

        // Button in main frame
        WebElement button = driver.findElement(By.className("seeResult"));
        // Text field in iframe
        driver.findElement(By.name("fname"));
        // Text area in main frame
        driver.findElement(By.id("textareaCode"));
        // Hidden input field in main frame
        driver.findElement(By.name("bt"));
        // Hidden input field in iframe
        WebElement hiddenCountryField = driver.findElement(By.name("country"));

        // Click button in main frame. This *refreshes* the iframe, making all existing
        // references to elements therein (e.g. 'hiddenCountryField') stale
        button.click();

        // Get value of hidden input field after iframe refresh
        System.out.println(driver.findElement(By.name("country")).getAttribute("value"));

        // This alternative would *not* work because the aspect cannot repair a reference
        // to an element which is gone forever because the iframe was refreshed
        // System.out.println(hiddenCountryField.getAttribute("value"));

        // Click submit button in iframe (triggers both advices)
        driver.findElement(By.cssSelector("input[type=submit]")).click();
    }

    public static void main(String[] args) {
        try (Application application = new Application()) {
            application.doSomething();
        }
    }
}

方面:

package de.scrum_master.aspect;

import de.scrum_master.app.Application;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;

public aspect IFrameSwitcher {
    WebElement around(WebDriver driver, By by) :
        !within(IFrameSwitcher) &&
            call(WebElement WebDriver+.findElement(*)) &&
            target(driver) &&
            args(by)
        {
            System.out.println(thisJoinPoint + " -> " + by);
            WebElement webElement;
            try {
                System.out.print("  Trying main frame -> ");
                driver.switchTo().defaultContent();
                webElement = proceed(driver, by);
                System.out.println("OK");
                return webElement;
            }
            catch (RuntimeException e) {
                System.out.println(e.getClass().getSimpleName());
                for (WebElement iframe : driver.findElements(By.tagName("iframe"))) {
                    try {
                        System.out.print("  Trying iframe " + iframe.getAttribute("id") + " -> ");
                        driver.switchTo().frame(driver.findElement(By.id("iframeResult")));
                        webElement = proceed(driver, by);
                        System.out.println("OK");
                        return webElement;
                    }
                    catch (RuntimeException e2) {
                        System.out.println(e2.getClass().getSimpleName());
                        e = e2;
                    }
                }
                throw e;
            }
        }

    void around(Application application, WebElement webElement) :
        within(Application) &&
        call(void WebElement+.click()) &&
        this(application) &&
        target(webElement)
    {
        System.out.println(thisJoinPoint + " -> " + webElement);
        WebDriver driver = application.getDriver();
        try {
            System.out.print("  Trying main frame -> ");
            driver.switchTo().defaultContent();
            proceed(application, webElement);
            System.out.println("OK");
        }
        catch (RuntimeException e) {
            System.out.println(e.getClass().getSimpleName());
            for (WebElement iframe : driver.findElements(By.tagName("iframe"))) {
                try {
                    System.out.print("  Trying iframe " + iframe.getAttribute("id") + " -> ");
                    driver.switchTo().frame(driver.findElement(By.id("iframeResult")));
                    proceed(application, webElement);
                    System.out.println("OK");
                    return;
                }
                catch (RuntimeException e2) {
                    System.out.println(e2.getClass().getSimpleName());
                    e = e2;
                }
            }
            throw e;
        }
    }
}

控制台日志:

在这里,您可以查看何时触发两个切入点中的哪个切入点,尝试失败后如何重启等。

call(WebElement org.openqa.selenium.WebDriver.findElement(By)) -> By.className: seeResult
  Trying main frame -> OK
call(WebElement org.openqa.selenium.WebDriver.findElement(By)) -> By.name: fname
  Trying main frame -> NoSuchElementException
  Trying iframe google_ads_iframe_/16833175/TryitLeaderboard_0 -> OK
call(WebElement org.openqa.selenium.WebDriver.findElement(By)) -> By.id: textareaCode
  Trying main frame -> OK
call(WebElement org.openqa.selenium.WebDriver.findElement(By)) -> By.name: bt
  Trying main frame -> OK
call(WebElement org.openqa.selenium.WebDriver.findElement(By)) -> By.name: country
  Trying main frame -> NoSuchElementException
  Trying iframe google_ads_iframe_/16833175/TryitLeaderboard_0 -> OK
call(void org.openqa.selenium.WebElement.click()) -> [[ChromeDriver: chrome on XP (5ab9e5f25d169bbc941ab1b08b346c50)] -> class name: seeResult]
  Trying main frame -> OK
call(WebElement org.openqa.selenium.WebDriver.findElement(By)) -> By.name: country
  Trying main frame -> NoSuchElementException
  Trying iframe google_ads_iframe_/16833175/TryitLeaderboard_0 -> OK
Norway
call(WebElement org.openqa.selenium.WebDriver.findElement(By)) -> By.cssSelector: input[type=submit]
  Trying main frame -> NoSuchElementException
  Trying iframe google_ads_iframe_/16833175/TryitLeaderboard_0 -> OK
call(void org.openqa.selenium.WebElement.click()) -> [[ChromeDriver: chrome on XP (5ab9e5f25d169bbc941ab1b08b346c50)] -> css selector: input[type=submit]]
  Trying main frame -> StaleElementReferenceException
  Trying iframe google_ads_iframe_/16833175/TryitLeaderboard_0 -> OK

我希望这有帮助。 请享用!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM