Override #override with AOP - java

I'm giving AOP a try for the first time. I want to do AOP on the #Override notation to write logs. It seems to be almost working but:
I have to import my own Override class in every class. Is this normal? I thought that it would magically go through my #Override decorator first and then through the Java one.
I have this very weird behavior where depending on the endpoint that I call first, it keeps working after or it only works for this one endpoint. Say, I have /a and /b, if I call /b first, it shows my logs, then I'll call /a and it won't show anything, if after that I call /b it will show logs. However, if I call /a first, it works, then I call /b and it works, and it just keeps working for all of them. It makes no sense to me.
This is my OverrideInterceptor:
#Slf4j
public class OverrideInterceptor implements MethodInterceptor<Object, Object> {
#Override
public Object intercept(MethodInvocationContext<Object, Object> context) {
String prettyMethod = context.getDeclaringType().getSimpleName() + "." + context.getName();
log.debug("{} with params {}", prettyMethod, context.getParameterValueMap());
long start = System.nanoTime();
Object result = context.proceed();
long end = System.nanoTime() - start;
log.debug("Execution of " + prettyMethod + " took: " + (end/1000) + "ms.");
return result;
}
}
And my annotation:
#Documented
#Retention(RUNTIME)
#Target({ElementType.TYPE, ElementType.METHOD})
#Around
#Type(OverrideInterceptor.class)
public #interface Override {
}
Both classes are in the package: package com.time.infrastructure.config;
And I'm using this #Override annotation in packages under com.time.infrastructure.db, com.time.infrastructure.rest, com.time.application.repository, etc.

For point 1: The built-in #Override annotation is a normal #interface - it resides in the java.lang package. What you've done here is that you've created a custom annotation with the name Override in the package com.time.infrastructure.config, which has nothing to do with java.lang.Override. So in that sense, it's "normal", but it's probably not doing what you think. You can't subtype annotations in Java, unfortunately.

Related

Trigger a method when calling another method in java (or how to do aop without aop)

I'm trying to achieve the functionality of "before" and "after" method invocation.
I tried spring-aop, but it was buggy (I suspect that it's the combination of groovy in my java application).
I don't want to use java reflection.
I think that creating a method with function as parameter is not suitable here because the functions already exists in another class.
Here's what I'm trying to do (this is only part of the activities, I have 10 more like this):
long startTimeMetric = System.nanoTime()
Timer timer = meterRegistry.timer("item 1")
myObject.theActivity(mediaContainer)
timer.record(System.nanoTime() - startTimeMetric, TimeUnit.NANOSECONDS)
long startTimeMetric = System.nanoTime()
Timer timer = meterRegistry.timer("item 2")
myObject2.theActivity2(mediaContainer)
timer.record(System.nanoTime() - startTimeMetric, TimeUnit.NANOSECONDS)
How can I make the repetitive first second and fourth line in each bucket automatic and elegant?
Thanks ahead.
edit:
The problem I mentioned with aop is that I'm getting all of the sudden null pointers, whereas without the aop, it was OK.
How did I implement the aop?
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface MyMonitor{}
#Aspect
#Component
public class TimeMonitoringAspect {
#Autowired
MeterRegistry meterRegistry;
#Around("#annotation(com.path.to.MyMonitor)")
public void before(ProceedingJoinPoint joinPoint) throws Throwable {
long startTimeMetric = System.nanoTime();
String methodName = joinPoint.getSignature().getName();
Timer timer = meterRegistry.timer(methodName);
joinPoint.proceed();
timer.record(System.nanoTime() - startTimeMetric, TimeUnit.NANOSECONDS);
}
}
For each method that I wanted to measure time, I annotated #MyMonitor before the method, and it worked most of the time, but in some cases, it gave the null pointer as I mentioned.
As far as AOP is concerned, you can still achieve using the following approaches.
Using Decorator Pattern. Refer this link and this link
You can use Java's InvocationHandler, refer this link. However it uses Proxy.
You can also use CGLib, refer this link. It uses reflection.
Spring provides a easy way to AOP which is used in most cases.
I would suggest to use lambda expressions:
public static void main(String[] args) {
executeActivity(meterRegistry, "item 1", Task::theActivity, mediaContainer);
executeActivity(meterRegistry, "item 2", Task::theActivity2, mediaContainer);
}
public static void executeActivity(MeterRegistry meterRegistry, String name, Consumer<Object> theActivity, Object mediaContainer) {
long startTimeMetric = System.nanoTime();
Timer timer = meterRegistry.timer(name);
theActivity.accept(mediaContainer);
timer.record(System.nanoTime() - startTimeMetric, TimeUnit.NANOSECONDS);
}
public interface MeterRegistry {
Timer timer(String name);
void record(Long time);
}

Aspect execution not firing on method

I am kinda new to Aspects and I am trying to surround a function with an aspect but I can't get it called.
The method signature is as follows:
public <T> T get(String uri, List<BasicNameValuePair> nameValuePairs, final Class<T> clazz)
and it's defined in class with fully-qualified name:
com.X.Y.infrastructure.rest.RestClient
And the Aspect
#Aspect
public class WebRequestTimeLoggerAspect {
#Around("execution(* com.X.Y.infrastructure.rest.RestClient.get(..))")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("hijacked method : " + joinPoint.getSignature().getName());
System.out.println("hijacked arguments : " + Arrays.toString(joinPoint.getArgs()));
System.out.println("Around before is running!");
Object ret = joinPoint.proceed();
System.out.println("Around after is running!");
return ret;
}
}
I just cant seem to find the problem.
Everything is declared and spring loads both beans into the container.
If possible. How do I add another function to the same execute deceleration?
Thanks.
Update:
So I have managed to do some progress, apparently I needed to Change project configuration to support Aspectj (in eclipse) but now I am getting the following error:
applying to join point that doesn't return void: method-execution(java.lang.Object com.X.Y.infrastructure.rest.RestClient.get(java.lang.String, java.util.List, java.lang.Class))
I will assume that i need to specify the return argument. I thought * will catch all, but that seems to be for void.
So how does the point cut should look for generics like that?
Thanks again
Another update
Following the comment I have added a return value to the method. But the aspect is still not firing.
By request the spring configuration:
<aop:aspectj-autoproxy />
<bean id="webRequestTimeLoggerAspect" class="com.X.Y.infrastructure.rest.WebRequestTimeLoggerAspect" />
And in fact I don't believe I need to declare a bean for that aspect because I have package scan. But just in case.

Declaring an aspect on an aspect

I happen to have an #Aspect that declares a method that is intercepted by a pointcut of another aspect. The aspects are created with compile-time weaving and the container is instantiated using Spring.
I annotated my aspect with #Configurable to tell Spring that the component is being created outside the container. I happen to have a static reference to a Log object in this aspect too. To summarize, the code looks something like this
#Aspect
#Configurable
public class MyAspect {
private static final Log log = LogFactory.getClass(MyAspect.class);
// Autowired dependencies
// Pointcuts and advice
// Happens to be a pointcut of some other Advice
#Asynchronous
private Object someMethod(...) {
}
}
During AspectJ compilation, I do not see the message I expect, which looks something like this:
weaveinfo Join point 'method-call(java.lang.Object mypackage.someMethod(...))' in Type 'mypackage.MyAspect' (MyAspect.java:30) advised by around advice from 'anotherpackage.AsynchronousAspect' (from AsynchronousAspect.java))
As expected, the third-party advice is never invoked at this point. However, if I add a simple log entry to my advice, something like
log.debug("Join point invoked!");
Then the compilation happens correctly and all the aspects are wired (including my third party dependencies) and invoked correctly.
What does adding a log entry do to change my assumptions?
What you want to do is pretty straightforward and not dangerous at all if you know what you are doing. Please apologise that I am not a Spring user and that I prefer native AspectJ syntax to #AspectJ. This little sample runs just fine:
public class Application {
public static void main(String[] args) {
System.out.println("Hello world!");
someMethod();
}
private static void someMethod() {
System.out.println("Doing something ...");
}
}
public aspect FirstAspect {
void around() : execution(void *..main(..)) {
System.out.println(thisJoinPointStaticPart + ": " + someMethod("before", "main"));
proceed();
System.out.println(thisJoinPointStaticPart + ": " + someMethod("after", "main"));
}
private Object someMethod(String position, String methodName) {
return position + " " + methodName;
}
}
public aspect SecondAspect {
Object around() : execution(* *..someMethod(..)) {
System.out.println(thisJoinPointStaticPart + ": before someMethod");
Object result = proceed();
System.out.println(thisJoinPointStaticPart + ": after someMethod");
return result;
}
}
The result is as expected:
execution(Object FirstAspect.someMethod(String, String)): before someMethod
execution(Object FirstAspect.someMethod(String, String)): after someMethod
execution(void Application.main(String[])): before main
Hello world!
execution(void Application.someMethod()): before someMethod
Doing something ...
execution(void Application.someMethod()): after someMethod
execution(Object FirstAspect.someMethod(String, String)): before someMethod
execution(Object FirstAspect.someMethod(String, String)): after someMethod
execution(void Application.main(String[])): after main
If furthermore you are concerned with thhe order in which aspects are applied/executed, please use declare precedence.
If you experience problems with accessing e.g. private members, you need to use a privileged aspect.
Update: Changed usage of thisEnclosingJoinPointStaticPart to thisJoinPointStaticPart. That was just a copy & paste error. The result is the same on execution join points, but anyway the correction shows better the code's intent.

Need help creating a specific pointcut that utilizes a value from a method annotation

I have the following method
#AutoHandling(slot = FunctionalArea.PRE_MAIN_MENU)
#RequestMapping(method = RequestMethod.GET)
public String navigation(ModelMap model) {
logger.debug("navigation");
...
//First time to the Main Menu and ID-Level is ID-1 or greater
if (!callSession.getCallFlowData().isMainMenuPlayed()
&& callSession.getCallFlowData().getIdLevel() >= 1) {
// Call Auto Handling
logger.info("Call AutoHandling");
autoHandlingComponent.processAutoHandling();
}
...
return forward(returnView);
}
Basically what I want to do, is have a pointcut on processAutoHandling()
But in the #After, I need to use the slot() for #AutoHandling
I tried this, but it does not get called
#Pointcut("execution(* *.processAutoHandling())")
public void processAutoHandleCall() {
logger.debug("processAutoHandleCall");
}
#Around("processAutoHandleCall() &&" +
"#annotation(autoHandling) &&" +
"target(bean) "
)
public Object processAutoHandlingCall(ProceedingJoinPoint jp,
AutoHandling autoHandling,
Object bean)
throws Throwable {
...
You can use the wormhole design pattern for this. I am illustrating using AspectJ byte-code based approach and syntax, but you should be able to get the same effect using an explicit ThreadLocal if you are using Spring's proxy-based AOP.
pointcut navigation(AutoHandling handling) : execution(* navigation(..))
&& #annotation(handling);
// Collect whatever other context you need
pointcut processAutoHandleCall() : execution(* *.processAutoHandling());
pointcut wormhole(AutoHandling handling) : processAutoHandleCall()
&& cflow(navigation(handling));
after(AutoHandling handling) : wormhole(hanlding) {
... you advice code
... access the slot using handling.slot()
}
a) It can't work, you are trying to match two different things:
#Around("processAutoHandleCall() &&" +
"#annotation(autoHandling) &&" +
"target(bean) "
)
processHandleCall() matches the inner method execution autoHandlingComponent.processAutoHandling() while #annotation(autoHandling) matches the outer method execution navigation(ModelMap model)
b) since you are obviously trying to advise a Controller, there are a few caveats:
if you use proxy-target-class=true everything should work as is, just make sure you don't have any final methods
if you don't, all your controller methods must be backed by an interface and the #RequestMapping etc annotations must be on the interface, not the implementing class as described in this section of the Spring MVC reference docs

Spring logging by package name

I need to log many classes in some packages in a project which I can not change its source code.
So I need a solution which I can specify package name, and with spring aop add logging to that package's classes without change them but I dont know how can I do that.
How can I do that?
With Spring AOP you can only log these classes if they are used as Spring Beans, and even then you can only log public method executions.
Here is an aspect in #AspectJ notification (this is the style that is compatible with both "real AspectJ" and Spring AOP, read about the difference in the spring reference) that you can use in both Spring AOP and AspectJ byte code weaving:
#Aspect
public class LoggingAspect{
#Pointcut("execution(* com.mycompany.myproject.*.*(..))")
public void methodToLog(){
};
#Around("methodToLog()")
public Object logMethod(final ProceedingJoinPoint joinPoint) throws Throwable{
final StaticPart staticPart = joinPoint.getStaticPart();
final String sig =
"" + staticPart.getSignature() + " with args: "
+ Arrays.deepToString(joinPoint.getArgs());
System.out.println("Entering method " + sig);
final Object result = joinPoint.proceed();
System.out.println("Leaving method " + sig);
return result;
}
}
Here is a stupid class with some methods:
package com.mycompany.myproject;
public class Dummy1{
public static void main(final String[] args){
final Dummy1 dummy = new Dummy1();
dummy.doSomeStuff();
dummy.doSomeStuffWithSomeArgs("Hello", 123);
}
private void doSomeStuff(){}
public void doSomeStuffWithSomeArgs(final String firstArg,
final int secondArg){}
}
When you start this class in Eclipse / AJDT as Java/AspectJ application, you get the following output:
Entering method void com.mycompany.myproject.Dummy1.main(String[]) with args: [[]]
Entering method void com.mycompany.myproject.Dummy1.doSomeStuff() with args: []
Leaving method void com.mycompany.myproject.Dummy1.doSomeStuff() with args: []
Entering method void com.mycompany.myproject.Dummy1.doSomeStuffWithSomeArgs(String, int) with args: [Hello, 123]
Leaving method void com.mycompany.myproject.Dummy1.doSomeStuffWithSomeArgs(String, int) with args: [Hello, 123]
Leaving method void com.mycompany.myproject.Dummy1.main(String[]) with args: [[]]
To test this in Spring AOP would involve more work (the main method approach won't work, you will have to create an ApplicationContext and register a bean of type Dummy1, on which you will call the methods), so I'll leave that to you, but I am pretty sure the private method call will not be logged.
If you download the SpringSource Tool Suite, you get nice tools for aspect visualisation and testing. You should also read the AspectJ book, even if you only want to use Spring AOP. It's a great book.
BTW: you will obviously want to use a real logger, not system.out. You can either define one per aspect, or (only with real aspectj) you can introduce it as a static member in the target class to get per-class logging. A killer feature of AspectJ in my opinion.

Categories