How can I have #AspectJ target specific subclasses when method signature has parent class? - aop

Say I have a method signature that is
public void accept(ParentInterface parent)
where ParentInterface is an interface. I want my pointcut to only specifically target a class TestA, but not a class TestB, both which implement the ParentInterface.
Currently, I have the following pointcut:
#Pointcut("call(public void accept(package.ParentInterface))")
But that would catch instances where accept is taking in a TestB instance too. Is there a method of fixing this?

Interface + implementations + driver application:
package de.scrum_master.app;
public interface ParentInterface {}
package de.scrum_master.app;
public class TestA implements ParentInterface {}
package de.scrum_master.app;
public class TestB implements ParentInterface {}
package de.scrum_master.app;
public class Application {
public void accept(ParentInterface parent) {}
public static void main(String[] args) {
Application application = new Application();
application.accept(new TestA());
application.accept(new TestB());
}
}
Aspect pinning down argument type via args() + pointcut method signature:
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import de.scrum_master.app.TestA;
#Aspect
public class MyAspect {
#Pointcut("call(public void accept(de.scrum_master.app.ParentInterface)) && args(argument)")
static void acceptCalls(TestA argument) {}
#Before("acceptCalls(argument)")
public void intercept(TestA argument, JoinPoint thisJoinPoint) {
System.out.println(thisJoinPoint + " -> " + argument);
}
}
Console log:
call(void de.scrum_master.app.Application.accept(ParentInterface)) -> de.scrum_master.app.TestA#4a574795

Related

what will happen if intercepted method called another intercepted method

using bytebuddy to intercept class Foo, whihc has two method A,B
in A method, B method get called. if we deleget both A and B to C method in intercetpor class Bar, which call callable#call at first line
what will happen ? what's the execution sequence of those methods ?
package org.wxt.xtools.agents;
import static net.bytebuddy.matcher.ElementMatchers.isAbstract;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
import static net.bytebuddy.matcher.ElementMatchers.not;
import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
import net.bytebuddy.utility.JavaModule;
class Foo {
public void methodA() {
System.out.println("method A");
methodB();
}
public void methodB() {
System.out.println("method B");
}
}
class Bar {
#RuntimeType
public static void interceptor(#Origin Class<?> clazz, #Origin Method method, #SuperCall Callable<?> callable,
#net.bytebuddy.implementation.bind.annotation.This Object inst) throws Exception {
callable.call();
System.out.println("intercepting " + method.getName());
}
}
public class Test {
public static void premain(String agentArgs, Instrumentation inst) throws IOException {
AgentBuilder.Transformer methodsTransformer = new AgentBuilder.Transformer() {
#Override
public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription,
ClassLoader classLoader, JavaModule javaModule) {
return builder.method(namedOneOf("methodA", "methodB").and(not(isAbstract())))
.intercept(MethodDelegation.to(Bar.class));
}
};
AgentBuilder.Listener listener = new AgentBuilder.Listener.WithErrorsOnly(
new AgentBuilder.Listener.StreamWriting(System.out));
new AgentBuilder.Default().type(named("org.wxt.xtools.agents.Foo")).transform(methodsTransformer)
.with(listener).installOn(inst);
}
public static void main(String[] args) throws IOException {
premain(null, ByteBuddyAgent.install());
new Foo().methodA();
}
}
results
method A
method B
intercepting methodB
intercepting methodA
You can of course just try it, but what will happen is that the internal call will again be intercepted. Methods do not know who is calling them. To avoid this, you can discover the caller via a StackWalker or by generating a stack trace using an exception.

Where do Before and After hooks go in Cucumber

I have a fairly simple Cucumber test framework with a feature file, a step definitions file, and a test runner class that looks like this:
#RunWith(Cucumber.class)
#CucumberOptions(features = "src/test/java/com/tests/cucumber/features/ui/ExampleTest.feature",
glue = { "com.tests.cucumber.stepdefinitions" },
)
public class ExampleTestRunner {
}
This runs a scenario in the feature file just fine. Now I want to add a Before and After hook to do some setup and teardown, but I can't for the like of me get the hooks to run. I've tried adding the hooks to the ExampleTestRunner and to the StepDefinition class, but they never run. Where should I put these hooks? At the moment, the hooks just look like this, but I'll add content to them once I've worked this out!
package com.tests.cucumber.stepdefinitions;
import cucumber.api.java.After;
import cucumber.api.java.Before;
public class StepDefinitions {
#Before
public void before() {
System.out.println("starting before()");
}
}
Thanks for any help.
I am a little hesitant to answer this question even though I managed to get this to work. As far as I can tell, the problem was that I had added the Before and After methods in classes that were extended by other classes. In this situation, the tests would not run. I had to add the Before and After methods to a class that was not extended.
It feels like this is similar to the situation in which if you specify a step definition in a class that is extended by another class, then the step definition is considered to have a duplicate definition. Do I have the correct diagnosis here?
I use like this;
Runner Class:
#RunWith(Cucumber.class)
#CucumberOptions(
features = {"src\\test\\features\\ui_features"},
glue = {"com\\base\\tm\\auto_reg\\tests\\ui_tests\\price_features"},
plugin = {"com.cucumber.listener.ExtentCucumberFormatter:"}
)
public class PriceFeatureRunner {
#BeforeClass
public static void setup() {
RunnerUtil.setup(PriceFeatureRunner.class);
}
#AfterClass
public static void teardown() {
RunnerUtil.teardown();
}
}
RunnerUtil.java:
public class RunnerUtil {
public static void setup(Class<?> clazz) {
String reportPath = "target/cucumber-reports/" + clazz.getSimpleName().split("_")[0] + "_report.html";
ExtentProperties extentProperties = ExtentProperties.INSTANCE;
extentProperties.setReportPath(reportPath);
}
public static void teardown() {
UiHooks uiHooks = new UiHooks();
uiHooks.afterScenario();
ExtentReportConfiguration.configureExtentReportTeardown();
}
}
UiHooks.java
public class UiHooks implements HookHelper {
public static final String BASE_URL = "https://www.stackoverfow.com/";
private Scenario scenario;
#Override
#Before
public void beforeScenario(Scenario scenario) {
this.scenario = scenario;
Reporter.assignAuthor(System.getProperty("user.name"));
}
#Override
#After
public void afterScenario() {
if (HookUtil.driver != null) {
HookUtil.driver.quit();
}
if (HookUtil.seleniumBase != null) {
HookUtil.seleniumBase.stopService();
}
}
#Override
#After
public void afterTest() {
if (HookUtil.driver != null) {
HookUtil.driver.quit();
}
if (HookUtil.seleniumBase != null) {
HookUtil.seleniumBase.stopService();
}
}
}
HookHelper.Java
public interface HookHelper {
#Before
void beforeScenario(Scenario scenario);
#After
void afterScenario();
void afterTest();
}

spring-boot-starter-aop around annotated interface's method won't advice my aspect

I have the following implementation:
public interface BusinessResource {
#RequiresAuthorization
public ResponseEnvelope getResource(ParamObj param);
}
and
#Component
public class BusinessResourceImpl implements BusinessResource {
#Autowired
public Response getResource(ParamObj param) {
return Response.ok().build();
}
}
and
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class AuthorizerAspect {
protected static final Logger LOGGER =
LoggerFactory.getLogger(AuthorizerAspect.class);
#Autowired
public AuthorizerAspect() {
LOGGER.info("Break point works here..." +
"so spring is creating the aspect as a component...");
}
#Around(value="#annotation(annotation)")
public Object intercept(ProceedingJoinPoint jp,
RequiresAuthorization annotation) throws Throwable {
LOGGER.info("BEGIN");
jp.proceed();
LOGGER.info("END");
}
}
The maven dependencies are properly configured with the spring-boot-starter-aop dependency. So what happens is that AuthorizerAspect won't intercept around the getResource method if the #RequiresAuthorization is used on the declared method of the BusinessResource interface, but if I change the implementation to annotate the same method now in the BusinessResourceImpl class, the aspect will take place.
NOTE: With the annotation in the interface level, the proxy isn't even created, whereas the annotation being placed in the implementation level will create a proxy for the resource.
Question is: Is there a way to advice objects which the annotation is present just on the interface?
May this alternative be useful for those who like me found no direct approach to sort that limitation on Spring AOP through proxies:
public interface BusinessResource {
#RequiresAuthorization
public ResponseEnvelope getResource(ParamObj param);
}
And
#Component
public class BusinessResourceImpl implements BusinessResource {
#Autowired
public Response getResource(ParamObj param) {
return Response.ok().build();
}
}
And
import import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
#Configuration
public class AuthorizerAspect {
protected static final Logger LOGGER =
LoggerFactory.getLogger(AuthorizerAspect.class);
#Autowired
public AuthorizerAspect() {
LOGGER.info("Break point works here..." +
"so spring is creating the aspect as a component...");
}
public Object invoke(MethodInvocation invocation) throws Throwable {
LOGGER.info("BEGIN");
invocation.proceed();
LOGGER.info("END");
}
#Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
return new DefaultAdvisorAutoProxyCreator();
}
#Bean("requiresAuthorizationPointcut")
public AbstractPointcutAdvisor createPointcut() {
return new AbstractPointcutAdvisor() {
private static final long serialVersionUID = 4733447191475535406L;
#Override
public Advice getAdvice() {
return AuthorizerAspect.this;
}
#Override
public Pointcut getPointcut() {
return new StaticMethodMatcherPointcut() {
#Override
public boolean matches(Method method, Class<?> targetClass) {
if (method.isAnnotationPresent(RequiresAuthorization.class)) {
return true;
}
if (method.getDeclaringClass().isInterface()) {
String methodName = method.getName();
try {
Method targetMethod = targetClass.getMethod(methodName, method.getParameterTypes());
return targetMethod != null && targetMethod.isAnnotationPresent(RequiresAuthorization.class);
} catch (NoSuchMethodException |
SecurityException e) {
LOGGER.debug("FAILURE LOG HERE",
e.getMessage());
return false;
}
}
return method.isAnnotationPresent(RequiresAuthorization.class);
}
};
}
};
}
}
So as you'll notice, we're sorting it by using method interceptors.

How to use JMockit MockUp for default interface method

Trying to apply a MockUp on a Java 8 default interface method, and JMockit tells me that method cannot be found. This has been tried with JMockit 1.15, 1.19, and 1.25. Here's a very simple use case:
#RunWith(JMockit.class)
public class TestTest {
public interface MyInterface {
default void foo(int f) {
bar(String.valueOf(f));
}
void bar(String s);
}
public class MyClass implements MyInterface {
public void bar(String s) {
System.out.println(s);
}
}
#Test
public void testtest() throws Exception {
new MockUp<MyClass>() {
#Mock
void foo(int i) {
System.out.println("MOCKMOCK " + (i*2));
}
#Mock
void bar(String s) {
System.out.println("MOCK " + s);
}
};
MyClass baz = new MyClass();
baz.foo(5);
baz.bar("Hello world");
}
}
This gets me the error
java.lang.IllegalArgumentException: Matching real methods not found for the following mocks:
com.example.dcsohl.TestTest$1#foo(int)
at com.example.dcsohl.TestTest$1.<init>(TestTest.java:29)
at com.example.dcsohl.TestTest.testtest(TestTest.java:29)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
...
How can we #Mock this method?
Slightly modifying your use case to return strings instead of printing to standard out the following solution will work.
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import mockit.Expectations;
public class TestTest {
public interface MyInterface {
default String foo(int f) {
return bar(String.valueOf(f));
}
String bar(String s);
}
public class MyClass implements MyInterface {
public String bar(String s) {
return s;
}
}
#Test
public void testtest() throws Exception {
MyClass baz = new MyClass();
new Expectations(MyClass.class) {{
baz.foo(anyInt); result = "FOOMOCK";
baz.bar(anyString); result = "BARMOCK";
}};
assertEquals(baz.foo(5), "FOOMOCK");
assertEquals(baz.bar("Hello world"), "BARMOCK");
}
}
There are many useful examples of how to mock out interfaces with method bodies (ie default or static methods) outlined in the examples section on the jmockit github repository.
Use #Mocked instead of a MockUp, it supports default methods.

#EventHandler events not working

I am trying to make a simple addition to my plugin so that when someone joins they receive a message that says "Heyyyyyyy". My plugin has a few commands also.
Here's my Main class:
package me.ben.test;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.plugin.java.JavaPlugin;
public class Main extends JavaPlugin implements Listener {
#Override
public void onEnable() {
this.getServer().getPluginManager().registerEvents(new Click(), this);
getLogger().info("The Plugin Has Been Enabled!");
}
#Override
public void onDisable() {
getLogger().info("The Plugin Has Been Disabled!");
}
public boolean onCommand(CommandSender sender, Command cmd, String label,
String[] args) {
if (cmd.getName().equalsIgnoreCase("hello") && sender instanceof Player) {
Player player = (Player) sender;
player.sendMessage("Hello, " + player.getName() + "!");
return true;
} else if (cmd.getName().equalsIgnoreCase("isonline")
&& args.length == 1) {
Player target = Bukkit.getServer().getPlayer(args[0]);
if (target == null) {
sender.sendMessage(ChatColor.AQUA + "Player " + args[0]
+ " is not online.");
return true;
} else if (target != null) {
sender.sendMessage(ChatColor.AQUA + "Player " + args[0]
+ " is online.");
return true;
} else {
return false;
}
}
return false;
}
}
and here is my Click class:
package me.ben.test;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.java.JavaPlugin;
public class Click extends JavaPlugin implements Listener {
#EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
event.getPlayer().sendMessage("Heyyyyyyy");
}
}
All of the #EventHandler things are not working so I quick made this simple one.
You can have only one class that extends JavaPlugin. Remove extends JavaPlugin from your Click Class - only your main class should extend JavaPlugin.
Check out Bukkit's official plugin tutorial for help on coding Bukkit Plugins.
You are using Listener in your Main class but you are not handling any event there, use it only when you want the class to be able to handler bukkit events.
You can use Listener with your Main class if you want, but you'll need to put the methods that handles events in your main class, but it'll become messy in big projects...
You also don't need to extend JavaPlugin everywhere, just in your main class.
If you want to use your main class:
public class Main extends JavaPlugin implements Listener {
#Override
public void onEnable() {
this.getServer().getPluginManager().registerEvents(this, this);
getLogger().info("The Plugin Has Been Enabled!");
}
#EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
event.getPlayer().sendMessage("Heyyyyyyy");
}
}
If you want to use a separated class to handle events:
public class Main extends JavaPlugin {
#Override
public void onEnable() {
this.getServer().getPluginManager().registerEvents(new Click(), this);
getLogger().info("The Plugin Has Been Enabled!");
}
}
public class Click implements Listener {
#EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
event.getPlayer().sendMessage("Heyyyyyyy");
}
}
Don't forget that you need to create a plugin.yml file correctly otherwise nothing will work.