Spring AOP get annotation value - aop

I am using spring AOP, how can I get values from annotation,
Here is my annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
#Inherited
#Documented
public #interface ExecutionMethodProfiler
{
String value() default "defaultValue";;
}
here is my XML:
<aop:aspectj-autoproxy/>
<aop:config>
<aop:aspect ref="methodProfiler">
<aop:pointcut id="serviceMethod"
expression="(execution(* com.old..*(..)) or execution(* com.test..*(..))) and #annotation(com.test.profiler.ExecutionMethodProfiler)" />
<aop:around pointcut-ref="serviceMethod" method="profile"/>
</aop:aspect>
</aop:config>
And this is my serviceMethod:
public void profile(ProceedingJoinPoint jointPoint) throws Throwable {}
as of now I can get the values by using this code:
MethodSignature signature = (MethodSignature) jointPoint.getSignature();
System.out.println(signature.getMethod().getAnnotation(ExecutionMethodProfiler.class).value());
I don't like it, is there a better way?

First change your advice to take your annotation as an additional argument:
public void profile(
ProceedingJoinPoint jointPoint,
ExecutionMethodProfiler methodProfiler
) throws Throwable {}
Then bind the annotation to that argument in your pointcut:
<aop:around
pointcut="(execution(* com.old..*(..)) or execution(* com.test..*(..))) and #annotation(methodProfiler)"
method="profile"
arg-names="methodProfiler"
/>
I did not actually test it because I am a bit busy now, but this is basically how it works.

Related

Pointcut on annotated method

I'm trying to add a custom annotation for JPA repository methods to have a advice on #Query value.
Below is the piece of code I tried
MyFilter class
#Aspect
#Component
public class MyFilter {
#Pointcut("execution(* *(..)) && #within(org.springframework.data.jpa.repository.Query)")
private void createQuery(){}
#Around("createQuery()")
public void invoke(JointPoint jp) {
}
}
The Respository code
#MyFilter
#Query(Select * ...)
MyObject findByNameAndClass(...)
So I keep getting error
createQuery() is never called At MyFilter
I'm trying to update the Query value using the advice.
What am I doing wrong?
To capture the annotation I often use this pattern:
"execution(#AnnotationToCapture * *(..)) && #annotation(annotationParam)"
Then in the proceeding method, you can have the annotation as parameter:
(..., AnnotationToCapture annotationParam, ...)
You should not use the #within annotation for this purpose. Instead, you should use #annotation, as follows:
#Pointcut("execution(* *(..)) && #annotation(org.springframework.data.jpa.repository.Query)")
private void createQuery(){}
Also, you should use JoinPoint to access the method signature and then you can extract the annotation from the signature.

byte-buddy Advice or Intercept Constructor invocation

I am trying to intercept or advice constructor invocation as following:
new AgentBuilder.Default()
//.disableClassFormatChanges()
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(AgentBuilder.TypeStrategy.Default.REDEFINE)
.type(ElementMatchers.nameMatches("org.openqa.selenium.firefox.FirefoxDriver"))
.transform((builder, typeDescription, classLoader, module) -> builder
.constructor(ElementMatchers.any())
.intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.to(FirefoxDriverInterceptor.class))))
//.intercept(SuperMethodCall.INSTANCE.andThen(Advice.to(FirefoxDriverConstructorAdvice.class))))
Interceptor:
#RuntimeType
public static void intercept(#Origin Constructor<?> constructor) {
System.out.println("Intercepted: " + constructor.getName());
}
Advice:
#Advice.OnMethodExit
public static void after(//#Advice.This Object thisObject,
#Advice.Origin String method,
#Advice.AllArguments Object[] args
) {
logger.info("<----- instantiated firefoxwebdriver: {}", method);
}
on trying with interceptor or advice, exception throws is:
java.lang.IllegalStateException: Cannot call super (or default) method for public org.openqa.selenium.firefox.FirefoxDriver(org.openqa.selenium.firefox.GeckoDriverService,org.openqa.selenium.Capabilities)
at net.bytebuddy.implementation.SuperMethodCall$Appender.apply(SuperMethodCall.java:102)
at net.bytebuddy.implementation.bytecode.ByteCodeAppender$Compound.apply(ByteCodeAppender.java:134)
Let me know for nay pointers. Thank you for your help.
You are blending Advice and MethodDelegation here which are not related. They have some common concepts, but you cannot mix the annotations.
You can however wrap advice around a delegation:
Advice.to(...).wrap(MethodDelegation.to(...))

NSubstitute throws CouldNotSetReturnDueToTypeMismatchException when mocking Query on NHibernate Session

I have a repository offering a GetAll method which again calls the Query extension method on the ISession instance of NHibernate.
public ICollection<Product> GetAll()
{
return _session.Query<Product>().ToList();
}
My unit test looks like this:
[Test]
public void GetAllReturnsCollectionFromSession()
{
IQueryable<Product> productList = new ProductListBuilder().Build().AsQueryable();
_fixture.Session.Query<Product>().Returns(productList);
var sut = _fixture.CreateSut();
var result = sut.GetAll();
Assert.AreSame(productList, result);
_fixture.Session.Received().Query<Product>();
}
In the _fixture.Session.Query().Returns(productList) statement, NSubstitute throws the following exception:
NSubstitute.Exceptions.CouldNotSetReturnDueToTypeMismatchException : Can not return value of type IQueryable`1Proxy for ISession.GetSessionImplementation (expected type ISessionImplementor).
Make sure you called Returns() after calling your substitute (for example: mySub.SomeMethod().Returns(value)),
and that you are not configuring other substitutes within Returns() (for example, avoid this: mySub.SomeMethod().Returns(ConfigOtherSub())).
If you substituted for a class rather than an interface, check that the call to your substitute was on a virtual/abstract member.
Return values cannot be configured for non-virtual/non-abstract members.
Correct use:
mySub.SomeMethod().Returns(returnValue);
Potentially problematic use:
mySub.SomeMethod().Returns(ConfigOtherSub());
Instead try:
var returnValue = ConfigOtherSub();
mySub.SomeMethod().Returns(returnValue);
at NSubstitute.Core.ConfigureCall.CheckResultIsCompatibleWithCall(IReturn valueToReturn, ICallSpecification spec)
at NSubstitute.Core.ConfigureCall.SetResultForLastCall(IReturn valueToReturn, MatchArgs matchArgs)
at NSubstitute.Core.CallRouter.LastCallShouldReturn(IReturn returnValue, MatchArgs matchArgs)
at NSubstitute.Core.SubstitutionContext.LastCallShouldReturn(IReturn value, MatchArgs matchArgs)
at NSubstitute.SubstituteExtensions.Returns[T](MatchArgs matchArgs, T returnThis, T[] returnThese)
at NSubstitute.SubstituteExtensions.ReturnsForAnyArgs[T](T value, T returnThis, T[] returnThese)
at Statoil.Wellcom.DataLayer.Implementation.Oracle.UnitTests.Repositories.DwapplicationRepositoryTests.GetAllReturnsCollectionFromSession() in C:\git\WELLCOM\source\Statoil.Wellcom.DataLayer.Implementation.Oracle.UnitTests\Repositories\DwapplicationRepositoryTests.cs:line 123
It looks like NSubstitute is unable to set the return value due to Query being an extension method. How would I go about mocking the extension method call on the ISession?
The easiest solution is to wrap your ISession in another interface/concrete class so you can stub that out:
public interface ISessionWrapper
{
IQueryable<T> Query<T>();
}
public class SessionWrapper : ISessionWrapper
{
private readonly ISession _session;
public SessionWrapper(ISession session)
{
_session = session;
}
public IQueryable<T> Query<T>()
{
return _session.Query<T>();
}
}
There is no way to mock extension method with NSubstitute, however if you know what extension method is using inside, than you can mock that. Your test will use extension method on mocked object and eventually it will use mocked method. Difficult part is to know what is going on inside.
It worked for me in projects, where I knew all the source code and I could check what's inside.

get Annotation of test method in testNG ITestListener

I am trying to integrate TestLink with TestNG
Approach is below
1>Write ITestListner with onTestFailure and onTestSuccess
2> get Annotation of the method(like testName which will be equivalent to test name in testlink) which is being failed/success in a variable
3>Make connection with TestLink using API available and update the test case.
However I am struggling to find method Annotation value in ITestListner and requirement is to get annotation values in ITestListner only so that correct test cases can be updated in Test_link
Can someone please help me how to get Test Method annotation value in ITestListner or any other approach in which i can integrate testlink update with TestNG
Hi Thanks niharika for help
,First of all you are correct in explaining use of TestNG but we are using TestNG for Selenium and already there are around 1000 test cases writen in test Methods and we have to live with that
Some how i have figured the solution ,we can still get the testName of the test method using two listners
This is just work around I am not sure if this is the best approach but as of now solving my purpose
package com.automation.testng.listner;
import org.testng.*;
public class MyIInvokeMethodListner_TestName_TestLink implements IInvokedMethodListener {
public static String testName;
public void afterInvocation(IInvokedMethod arg0, ITestResult arg1) {
// TODO Auto-generated method stub
}
public void beforeInvocation(IInvokedMethod m, ITestResult tr) {
// TODO Auto-generated method stub
//This give the Annotation Test object
org.testng.annotations.Test t=m.getTestMethod().getMethod().getAnnotation(org.testng.annotations.Test.class);
MyIInvokeMethodListner_TestName_TestLink.testName = t.testName().toString();
}
}
MyITestListner goes like below
package com.automation.testng.listner;
import org.testng.*;
public class MyITestListner_TestLink extends TestListenerAdapter {
/*IAnnotationTransformer at;
public Listner_1()
{
this.at = new Annotation_listner();
}*/
#Override
public void onTestFailure(ITestResult tr)
{
System.out.println("Hurray !I am being inboked from Test listner");
MyIInvokeMethodListner_TestName_TestLink a = new MyIInvokeMethodListner_TestName_TestLink();
System.out.println(MyIInvokeMethodListner_TestName_TestLink.testName);
}
public void onTestSuccess(ITestResult tr)
{
MyIInvokeMethodListner_TestName_TestLink a = new MyIInvokeMethodListner_TestName_TestLink();
System.out.println(MyIInvokeMethodListner_TestName_TestLink.testName);
}
}
Basically we are getting the method and then using Test Annotation class setting the static variable which can be used in MyITestListner
The ITestListener is the one which is used after <test> tag. For getting the method name and annotation specifics, you need to implement IInvokedMethodListener and in the after/before methods of this interface, and use something like method.getTestMethod().getMethodName() to get the executing method name.
If you are adding testName at the method level, I think you are doing it wrong since the help of testng mentions this "The name of the test this test class should be placed in. This attribute is ignore if #Test is not at the class level."
If you are indeed specifying the #Test at your class level then you can get it as below :
method.getTestMethod().getTestClass().getTestName()
A bit ugly and you probably want to wrap those parts in null checks in your code but this is how you get the testName specified in the annotation from the ITestResult:
iTestResult.getMethod().getConstructorOrMethod().getMethod().getAnnotation(Test.class).testName()

how to pass context(ApplicationContext) as an argument to logBefore Method(Method of Aspect)?

I want to implement an aspect to avoid lazy Loading problems .
so how to pass context(Application Context) as an argument for logBefore Method?
What is the signature pointcut defines the method above(the place to which I make "???"), knowing that this method (logBefore) will be execute before all the methods that have "Set"
as a result type and "slm.aoa.buisiness.facade" as package
#Aspect
public class EagerLoading {
#Before("execution(???)")
public void logBefore(JoinPoint joinPoint) {
SessionFactory sessionFactory = (SessionFactory) context.getBean("sessionFactory");
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
}
and Thanks for your Help .