I have some troubles with aspectj and its syntax. I have something written on a java file and I want to translate it to an .aj file because I think that is easier, but I can't find a tutorial to follow.
This is my code:
#Aspect
public class Aspect{
#Pointcut("#annotation(annotationVariableName)")
public void annotationPointCutDefinition(Annotation annotationVariableName){
}
#Pointcut("execution(* *(..))")
public void atExecution(){}
#Around("annotationPointCutDefinition(withTransactionVariableName) && atExecution()")
public Object aroundAdvice(ProceedingJoinPoint joinPoint, Annotation annotationVariableName) throws Throwable {
boolean parameter= annotationVariableName.parameter();
Object returnObject = null;
try {
returnObject = joinPoint.proceed();
} catch (Throwable throwable) {
throw throwable;
}
return returnObject;
}
}
Can anyone help me with this? Thank you!
I have made up a little example MCVE relating to your comment about transaction management, so as to make the code and its log output a little clearer:
Annotation:
package de.scrum_master.app;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Transaction {
boolean myFlag();
}
Driver application:
Please note that two methods bear the annotation, one does not.
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
Application application = new Application();
application.doSomething();
application.doSomethingElse();
application.doSomethingWeird();
}
#Transaction(myFlag = true)
public void doSomething() {
System.out.println("Doing something");
}
public void doSomethingElse() {
System.out.println("Doing something else\n");
}
#Transaction(myFlag = false)
public void doSomethingWeird() {
System.out.println("Doing something weird");
throw new RuntimeException("oops");
}
}
Aspect:
package de.scrum_master.aspect;
import org.aspectj.lang.SoftException;
import de.scrum_master.app.Transaction;
public aspect TransactionAspect {
pointcut hasAnnotation(Transaction myAnnotation) : #annotation(myAnnotation);
pointcut methodExecution() : execution(* *(..));
Object around(Transaction myAnnotation) : methodExecution() && hasAnnotation(myAnnotation) {
System.out.println(thisJoinPoint + " -> " + myAnnotation);
boolean parameter = myAnnotation.myFlag();
System.out.println("Transaction start");
try {
Object result = proceed(myAnnotation);
System.out.println("Transaction commit\n");
return result;
} catch (Exception e) {
System.out.println("Transaction roll-back\n");
// Native AspectJ advices must not return checked exceptions, only runtime exceptions.
// So we soften the caught exception, just in case.
throw new SoftException(e);
}
}
}
Console log:
execution(void de.scrum_master.app.Application.doSomething()) -> #de.scrum_master.app.Transaction(myFlag=true)
Transaction start
Doing something
Transaction commit
Doing something else
execution(void de.scrum_master.app.Application.doSomethingWeird()) -> #de.scrum_master.app.Transaction(myFlag=false)
Transaction start
Doing something weird
Transaction roll-back
Exception in thread "main" org.aspectj.lang.SoftException
at de.scrum_master.app.Application.doSomethingWeird_aroundBody3$advice(Application.java:22)
at de.scrum_master.app.Application.doSomethingWeird(Application.java:1)
at de.scrum_master.app.Application.main(Application.java:8)
Caused by: java.lang.RuntimeException: oops
at de.scrum_master.app.Application.doSomethingWeird_aroundBody2(Application.java:23)
at de.scrum_master.app.Application.doSomethingWeird_aroundBody3$advice(Application.java:17)
... 2 more
By the way, if you are fine with anonymous pointcuts, there is no need to declare them separately. You can just do it this way:
Aspect variant with anonymous pointcut:
package de.scrum_master.aspect;
import org.aspectj.lang.SoftException;
import de.scrum_master.app.Transaction;
public aspect TransactionAspect {
Object around(Transaction myAnnotation) : execution(* *(..)) && #annotation(myAnnotation) {
System.out.println(thisJoinPoint + " -> " + myAnnotation);
boolean parameter = myAnnotation.myFlag();
System.out.println("Transaction start");
try {
Object result = proceed(myAnnotation);
System.out.println("Transaction commit\n");
return result;
} catch (Exception e) {
System.out.println("Transaction roll-back\n");
// Native AspectJ advices must not return checked exceptions, only runtime exceptions.
// So we soften the caught exception, just in case.
throw new SoftException(e);
}
}
}
Personally I found the following programmer guide useful myself although it's not really a tutorial : https://eclipse.org/aspectj/doc/next/progguide/index.html. Click on Pointcuts to get the basics of converting your pointcuts, advice is also covered on that page although it doesn't detail "around" advice but there is an example of that under production aspects
A quick search for a tutorial throws up the following (I haven't used this) :
http://o7planning.org/en/10257/java-aspect-oriented-programming-tutorial-with-aspectj
Related
I write a class in Java and Spring Boot. It is a service, and in the service, it calls other libraries. The problem is when I call the libraries, it will log some errors, these erros actually didn't affect the execution of the service.
So I am wondering, can I suppress the errors in the class?
The service class looks like below.
#Service
public class serviceImpl implements service {
#Override
public String executeComputation(String rawData, String computationName)
throws BrokerException, IOException {
//call some libs
}
}
The error looks like this:
Unexpected exception during (something) evaluation. Details: Cannot invoke method collectEntries() on null object. Source Code: import java.text.DateFormat;
Write an aspect for it. An example of aspect:
#Aspect
public class MyAspect {
#Around("thePointcutExpression")
public Object executeComputationAspect(ProceedingJoinPoint pjp) throws Throwable {
Object ob;
try {
ob = pjp.proceed();
} catch(Exception e) {} // swallow the exception
return ob;
}
}
I am using AOP to wrap an auditing framework around some services. I've come across an issue where we're auditing multiple events for the same action due to recursion. The quick solution it to mark the method as #NonAuditable and add it to my pointcut strategy. I am finding that the method still gets executed however.
Here's my existing strategy:
#Around(value="(" +
"execution( * my.class.services..*.*(..)) " +
") && "+
"#annotation(auditable)), argName="audit")
public Object audit(ProceedingJoinPoint call, Audit audit) {
...
...
}
How can I update my execution to say "only execute within the services package if it doesn't contain the #NonAuditable annotation?
I tried the following, which did not work:
#Around(value="(" +
"execution( * my.class.services..*.*(..)) " +
") && "+
"!#annotation(NonAuditable) && " +
"#annotation(auditable), argName="audit")
public Object audit(ProceedingJoinPoint call, Audit audit) {
...
...
}
UPDATE:
Here are some examples of some methods that gets audited
package my.class.services.UserService
import ...
...
#Auditable(message="Request for user", Context="Search")
public User getUser(long id){
User u = userRepository.getUser(id);
... // do work
}
\
package my.class.services.CarService
import ...
...
#Auditable(message="Request for traffic violations", Context="Search")
public List<Ticket> getTickets(long id){
List<Ticket> tix = dmvRepository.getUserTicketsById(id);
... // do work
}
#NonAuditable(message="Request for traffic violations", Context="Search")
public List<Ticket> getSpeedingTickets(long id){
List<Ticket> tickets = this.getTickets(id);
Collection filter = Collection.filter(...);
// do some other logic to just get speeding tickets.
return filter;
}
One problem I inherited is that getTickets is being called recursively by another method (getSpeedingTickets) and I am looking to be able to apply an Annotation (#NonAuditable) on that method to stop getTickets from being audited.
Okay, I thought about it again and think I have guessed what you mean. I think your situation is like this:
Annotations:
package de.scrum_master.app;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface Auditable {}
package de.scrum_master.app;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface NonAuditable {}
Driver application:
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
Application application = new Application();
application.auditableAction("main");
application.inBetweenAction("main");
application.otherAction();
application.nonAuditableAction();
}
#Auditable
public void auditableAction(String caller) {
System.out.println(" auditableAction called by " + caller);
}
#NonAuditable
public void nonAuditableAction() {
auditableAction("nonAuditableAction");
inBetweenAction("nonAuditableAction");
}
public void otherAction() {
auditableAction("otherAction");
inBetweenAction("otherAction");
}
public void inBetweenAction(String caller) {
auditableAction(caller + " via inBetweenAction");
}
}
Now I assume you want to avoid auditing executions of the #Auditable method if called directly or indirectly by the #NonAuditable method. Correct? If so, the solution is to use a cflow() or cflowbelow() pointcut. Because such pointcuts cannot be evaluated statically but only dynamically during runtime, you might need to monitor the performance of your application after applying the aspect, but in many cases this is not a real problem. See for yourself. The solution looks as follows:
Aspect:
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
#Aspect
public class AuditAspect {
#Around(
"within(de.scrum_master.app..*) && " +
"execution(* *(..)) && " +
"#annotation(de.scrum_master.app.Auditable) && " +
"!cflow(#annotation(de.scrum_master.app.NonAuditable))"
)
public Object audit(ProceedingJoinPoint thisJoinPoint) throws Throwable {
System.out.println(thisJoinPoint);
return thisJoinPoint.proceed();
}
}
Console log:
execution(void de.scrum_master.app.Application.auditableAction(String))
auditableAction called by main
execution(void de.scrum_master.app.Application.auditableAction(String))
auditableAction called by main via inBetweenAction
execution(void de.scrum_master.app.Application.auditableAction(String))
auditableAction called by otherAction
execution(void de.scrum_master.app.Application.auditableAction(String))
auditableAction called by otherAction via inBetweenAction
auditableAction called by nonAuditableAction
auditableAction called by nonAuditableAction via inBetweenAction
Please note that nothing is logged before the last two lines.
I am implementing an AOP interceptor which process the logic in RetryTemplate. And problem is that ProceedingJoinPoint#execute is declared to throw Throwable, but RetryTemplate#doWithRetry allows to re-throw an Exception only. So I have to wrap Throwable and then unwrap it back.
My best try looks like this:
#Aspect
#Component
public class RetryableOperationFailureInterceptor {
#Around("#annotation(retryable)")
public Object performOperation(final ProceedingJoinPoint pjp, Retryable retryable)
throws Throwable {
RetryTemplate retryTemplate = getRetryTemplate(retryable);
try {
return retryTemplate.execute(new RetryCallback<Object>() {
#Override
public Object doWithRetry(RetryContext context)
throws Exception {
try {
return pjp.proceed(); // throws Throwable
} catch (Exception e) {
throw e;
} catch (Error e) {
throw e;
} catch (Throwable e) {
throw new RetryWrappedException(e);
}
}
});
} catch (RetryWrappedException e) {
throw e.getCause();
}
}
}
Is there any way to avoid wrapping or/and catching Throwable? The latest is more important for me, as catching Throwable trigger a code style violation.
M. Deinum is right, but you do not need to use your own RetryWrappedException because AspectJ already offers a SoftException (extends RuntimeException) for softening/wrapping checked exceptions, but it also works for other Throwables.
Because I found your question interesting, but am not a Spring user, I replicated some of your sample classes as dummies or minimal implementations in order to create a fully working POJO + AspectJ example without any Spring dependency. I also extended the around advice so as to do the retry loop by itself, reading a maxRetries value from the Retryable annotation. I know it is not 100% your use case, but it illustrates the problem and a few other things:
Dummy classes/interfaces:
package de.scrum_master.app;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface Retryable {
int maxRetries();
}
--
package de.scrum_master.app;
public class RetryContext {}
--
package de.scrum_master.app;
public interface RetryCallback<T> {
Object doWithRetry(RetryContext context) throws Exception;
}
--
package de.scrum_master.app;
public class RetryTemplate {
public Object execute(RetryCallback<Object> retryCallback) throws Exception {
return retryCallback.doWithRetry(new RetryContext());
}
}
Driver application:
package de.scrum_master.app;
import java.util.Random;
public class Application {
private static final Random RANDOM = new Random();
public static void main(String[] args) throws Exception {
new Application().performOperation();
}
#Retryable(maxRetries = 2)
public void performOperation() throws Exception {
System.out.println("Performing some operation");
if (RANDOM.nextBoolean())
throw new Exception("Operation failed");
System.out.println("Operation succeeded");
}
}
Without an aspect, there will be no retries and performOperation() will randomly either succeed
Performing some operation
Operation succeeded
or fail
Performing some operationException in thread "main"
java.lang.Exception: Operation failed
at de.scrum_master.app.Application.performOperation(Application.java:16)
at de.scrum_master.app.Application.main(Application.java:9)
Intercept/retry aspect:
Now with the following aspect we can
intercept methods annotated by Retryable,
get the number of maximum retries and
loop and try joinpoint execution via proceed() as many times as specified (maxRetries + 1 because even if zero retries are requested we call the method at least once).
As you can also see, we use different error handling depending on error type:
An Exception will either be just printed if the maximum number of retries has not been reached yet or re-thrown otherwise, i.e. if the final retry also fails, its the exception will be thrown.
An Error will always be re-thrown because an error is usually thrown by the JVM and non-recoverable, i.e. in this case there will be no more retries.
A Throwable (should never occur) will be wrapped into a SoftException and the latter re-thrown.
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.SoftException;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import de.scrum_master.app.RetryCallback;
import de.scrum_master.app.RetryContext;
import de.scrum_master.app.RetryTemplate;
import de.scrum_master.app.Retryable;
#Aspect
public class RetryableOperationFailureInterceptor {
#Around("#annotation(retryable) && execution(* *(..))")
public Object performOperation(final ProceedingJoinPoint pjp, final Retryable retryable) throws Throwable {
final int maxRetries = retryable.maxRetries();
System.out.println(pjp + " - maxRetries = " + maxRetries);
RetryTemplate retryTemplate = getRetryTemplate(retryable);
return retryTemplate.execute(
new RetryCallback<Object>() {
#Override
public Object doWithRetry(RetryContext context) throws Exception {
for (int i = 0; i <= maxRetries; i++) {
System.out.println("Retry #" + i);
try {
return pjp.proceed();
} catch (Exception e) {
if (i == maxRetries)
throw e;
System.out.println(e);
} catch (Error e) {
throw e;
} catch (Throwable t) {
throw new SoftException(t);
}
}
return null;
}
}
);
}
private RetryTemplate getRetryTemplate(Retryable retryable) {
return new RetryTemplate();
}
}
Resulting output with active Aspect:
Immediate success (no retries necessary):
execution(void de.scrum_master.app.Application.performOperation()) - maxRetries = 2
Retry #0
Performing some operation
Operation succeeded
Success after retries:
execution(void de.scrum_master.app.Application.performOperation()) - maxRetries = 2
Retry #0
Performing some operation
java.lang.Exception: Operation failed
Retry #1
Performing some operation
java.lang.Exception: Operation failed
Retry #2
Performing some operation
Operation succeeded
Failure after final retry:
execution(void de.scrum_master.app.Application.performOperation()) - maxRetries = 2
Retry #0
Performing some operation
java.lang.Exception: Operation failed
Retry #1
Performing some operation
java.lang.Exception: Operation failed
Retry #2
Performing some operation
Exception in thread "main" java.lang.Exception: Operation failed
at de.scrum_master.app.Application.performOperation_aroundBody0(Application.java:16)
at de.scrum_master.app.Application$AjcClosure1.run(Application.java:1)
at org.aspectj.runtime.reflect.JoinPointImpl.proceed(JoinPointImpl.java:149)
at de.scrum_master.aspect.RetryableOperationFailureInterceptor$1.doWithRetry(RetryableOperationFailureInterceptor.java:27)
at de.scrum_master.app.RetryTemplate.execute(RetryTemplate.java:5)
at de.scrum_master.aspect.RetryableOperationFailureInterceptor.performOperation(RetryableOperationFailureInterceptor.java:20)
at de.scrum_master.app.Application.performOperation(Application.java:13)
at de.scrum_master.app.Application.main(Application.java:9)
This is the first time I am trying to write a custom annotations in java.
I am not sure whether it is possible or not but wanted to give it a try before approaching another solution.
So here is the scenario, I have a lots of method that sends the data out from the application to a device. I have a requirement to log all these data in database.
I would like to create an annotation for this so that I can write the code in the annotation to log the data in database and then annotation all the methods with this annotation.
I can modify the code to log into the database but in that case I have to go in each method and place my code at correct place inorder to log them into database.
This is the reason I am looking for annotation based approach.
Is it possible what I am looking for or am I asking more.
Any pointers will be appreciated or If someone has different approach for my solution that will be really help full.
Instead of writing your own Annotations and processing them, have a look at what Spring provides, e.g. Interceptors:
Interceptors vs Aspects in Spring?
You can try below approach
package annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#interface Todo {
public enum Priority {LOW, MEDIUM, HIGH}
String logInfo() default "Logging...";
Priority priority() default Priority.LOW;
}
package annotation;
public class BusinessLogic {
public BusinessLogic() {
super();
}
public void compltedMethod() {
System.out.println("This method is complete");
}
#Todo(priority = Todo.Priority.HIGH)
public void notYetStartedMethod() {
// No Code Written yet
}
#Todo(priority = Todo.Priority.MEDIUM, logInfo = "Inside DAO")
public void incompleteMethod1() {
//Some business logic is written
//But its not complete yet
}
#Todo(priority = Todo.Priority.LOW)
public void incompleteMethod2() {
//Some business logic is written
//But its not complete yet
}
}
package annotation;
import java.lang.reflect.Method;
public class TodoReport {
public TodoReport() {
super();
}
public static void main(String[] args) {
Class businessLogicClass = BusinessLogic.class;
for(Method method : businessLogicClass.getMethods()) {
Todo todoAnnotation = (Todo)method.getAnnotation(Todo.class);
if(todoAnnotation != null) {
System.out.println(" Method Name : " + method.getName());
System.out.println(" Author : " + todoAnnotation.logInfo());
System.out.println(" Priority : " + todoAnnotation.priority());
System.out.println(" --------------------------- ");
}
}
}
}
Java Gurus,
I am pretty new for annotations and haven't searched for this a lot, so please bear with me...
I would like to implement a Custom Annotation which will intercept a method call; to start with something very basic it can just print the methods name and parameters so that I could avoid the logger statement.
A sample call like this:
public MyAppObject findMyAppObjectById(Long id) throws MyCustomException {
log.debug("in findMyAppObjectById(" + id + ")");
//....
}
can be converted into:
#LogMethodCall(Logger.DEBUG)
public MyAppObject findMyAppObjectById(Long id) throws MyCustomException {
//....
}
Could I get some hints about this?
Based in your answers of my comments, you will not be able to do this with just annotations. You can, of course, create your annotations and create some reflective code that will detected then and execute some code, but this will not change your code too much, because you will need to call the parser method before you call your methods and I think that will not help you too much, since you will need to call the parser method before each call.
If you need the behavior that you mentioned (automatic call), you will need to combine your annotations with some AOP framework like Spring (plain Java) or AspectJ (AspectJ code). With then, you can set pointcuts and everytime this point is reached, some code may be executed. You can configure then to execute some code before and/or after method execution.
If the first scenario is sufficient, you can do something like:
Logger: enum
public enum Logger {
INFO,
DEBUG;
}
LogMethodCall: annotation
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention( RetentionPolicy.RUNTIME ) // the annotation will be available during runtime
#Target( ElementType.METHOD ) // this can just used in methods
public #interface LogMethodCall {
Logger logLevel() default Logger.INFO;
}
Person: annotated class
public class Person {
// will use the default log level (INFO)
#LogMethodCall
public void foo( int a ) {
System.out.println( "foo! " + a );
}
#LogMethodCall( logLevel = Logger.DEBUG )
public void bar( int b ) {
System.out.println( "bar! " + b );
}
}
Utils: class with the log static method (this will perform the "parsing")
public class Utils {
public static void log( Object o, String methodName ) {
// gets the object class
Class klass = o.getClass();
// iterate over its methods
for ( Method m : klass.getMethods() ) {
// verify if the method is the wanted one
if ( m.getName().equals( methodName ) ) {
// yes, it is
// so, iterate over its annotations
for ( Annotation a : m.getAnnotations() ) {
// verify if it is a LogMethodCall annotation
if ( a instanceof LogMethodCall ) {
// yes, it is
// so, cast it
LogMethodCall lmc = ( LogMethodCall ) a;
// verify the log level
switch ( lmc.logLevel() ) {
case INFO:
System.out.println( "performing info log for \"" + m.getName() + "\" method" );
break;
case DEBUG:
System.out.println( "performing debug log for \"" + m.getName() + "\" method" );
break;
}
}
}
// method encountered, so the loop can be break
break;
}
}
}
}
AnnotationProcessing: class with code to test the annotation processing
public class AnnotationProcessing {
public static void main(String[] args) {
Person p = new Person();
Utils.log( p, "foo" );
p.foo( 2 );
Utils.log( p, "bar" );
p.bar( 3 );
}
}
Of course, you will need to improve my code to fit your needs. It is just a start point.
More about annotations:
http://docs.oracle.com/javase/1.5.0/docs/guide/language/annotations.html
http://docs.oracle.com/javase/tutorial/java/javaOO/annotations.html
http://tutorials.jenkov.com/java-reflection/annotations.html
More about AOP:
http://en.wikipedia.org/wiki/Aspect-oriented_programming
http://static.springsource.org/spring/docs/3.0.x/reference/aop.html
http://www.eclipse.org/aspectj/
Use Spring AOP along with Java Annotation. Spring AOP negates the requirement for writing a util class for parsing of Java classes using Java Reflection.
Example -
Custom Annotation -
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface A {
boolean startA() default false;
boolean endA() default false;
}
Aspect-
#Aspect
public class AAspect {
#Pointcut(value = "execution(* *.*(..))")
public void allMethods() {
LOGGER.debug("Inside all methods");
}
#Before("allMethods() && #annotation(A)")
public void startAProcess(JoinPoint pjp, A a) throws Throwable {
if (a.startA()) {
//Do something
}
}
Enable AspectJ -
#Configuration
#EnableAspectJAutoProxy
public class AConfig {
}
Use in code -
#A(startA = true, endA = true)
public void setUp(){
//Do something- logic
}
As already suggested, AOP and annotations is the best option. I would recommend to use a ready-made mechanism from jcabi-aspects (I'm a developer):
#Loggable(Loggable.DEBUG)
public String load(URL url) {
return url.openConnection().getContent();
}
All method calls will be logged to SLF4J.