Search code examples
javainstrumentation

How to call methods during run time using java instrumentation


I'm completely new to this instrumentation concept. I have a custom jar file which has lot of methods. Lets assume for now i have start and stop method. Inorder to collect the start and stop metrics i need to call those methods after every click . Instead of doing that is there a way to instrument this. I want this methods to be called for all clickable elements dynamically before and after during runtime. Any advise on this would be great. Thanks in advance. Please find the sample code.

Custom Methods:

  Public void start (){
    long start = System.currentTimeMillis();
    }

    public void stop{
    long finish= System.currentTimeMillis();
     long totalTime = finish - start;
    }

Sample Code:

start();
driver.findElement(By.name("username")).sendkeys("@@@");
stop();

start();
driver.findElement(By.name("password")).sendkeys("@@@");
stop();

start();
driver.findElement(By.name("login")).click();
stop();

Solution

  • Here's an example instrumentation solution using ByteBuddy, although as I mentioned in comments under this question it's probably not a best approach to solve this.

    For this simple example the code only covers a case where invocations on WebDriver and WebElement are chained e.g.:

    driver.findElement(By.name("login")).click();
    driver.findElement(By.name("logout")).click();
    

    Something like below fragment will not work without additional coding:

    WebElement element1 = findElement(By.name("login"));
    WebElement element2 = findElement(By.name("logout"));
    element2.click();
    element1.click();
    

    Instrumentation code:

    public class ByteBuddyTest {
    
        public static void main(String[] args) throws Exception {    
    
            ByteBuddyAgent.install();
    
            new ByteBuddy()
                .redefine(RemoteWebDriver.class)
                .visit(Advice.to(WebDriverAdvice.class).on(named("findElement").and(takesArguments(1))))
                .make()
                .load(ByteBuddyTest2.class.getClassLoader(),
                      ClassReloadingStrategy.fromInstalledAgent());
    
            new ByteBuddy()
                .redefine(RemoteWebElement.class)
                .visit(Advice.to(WebElementAdvice.class).on(named("click")))
                .make()
                .load(ByteBuddyTest2.class.getClassLoader(),
                      ClassReloadingStrategy.fromInstalledAgent());
    
            InternetExplorerDriver driver = new InternetExplorerDriver();
    
            driver.get("<some webpage>");
    
            driver.findElement(By.id("<some_id>")).click();
        }
    
        public static class WebDriverAdvice {    
            @Advice.OnMethodEnter
            public static void enter(@Advice.Origin String method) {
                System.out.printf("Driver Method Enter: %s\n", method);
                Times.start = System.currentTimeMillis();
            }
        }
    
        public static class WebElementAdvice {
            @Advice.OnMethodExit
            public static void exit(@Advice.Origin String method, @Advice.This Object target) {
                System.out.printf("Element Method Exit: %s\n", method);
                System.out.println("Time: " + (System.currentTimeMillis() - Times.start));
            }
        }
    
        public static class Times {
            public static long start = 0L;
        }
    }
    

    Example using WebDriverEventListener

    public class WebDriverEventListenerTest {
    
        public static void main(String[] args) throws Exception {
    
            InternetExplorerDriver driver = new InternetExplorerDriver();
            EventFiringWebDriver eventDriver = new EventFiringWebDriver(driver);
            eventDriver.register(new EventHandler());
    
            eventDriver.get("<some webpage>");
            eventDriver.findElement(By.id("<some id>")).click();
            eventDriver.findElement(By.id("<some id>")).click();
        }
    
        public static class EventHandler extends AbstractWebDriverEventListener {
    
            @Override public void beforeFindBy(By by, WebElement element, WebDriver driver) {
                System.out.printf("Driver Find By: %s\n", by);
                Times.start = System.currentTimeMillis();
            }
    
            @Override public void afterClickOn(WebElement element, WebDriver driver) {
                System.out.printf("Element Method Exit: %s\n", element);
                System.out.println("Time: " + (System.currentTimeMillis() - Times.start));
            }
        }
    
        public static class Times {
            public static long start = 0L;
        }
    }