Idan Fridman

About Idan Fridman

Idan is Software engineer with experience in Server side technologies. Idan is responsible for various infrastructure models in the software industry(Telecommunications, Finance).

Auditing infrastructure for your app using Spring AOP, Custom annotations and Reflection

The next post will demonstrate how to write simple auditing using Spring AOP and annotations. The auditing mechanism will be clean, efficient and easy to maintain (and Kewwl!).

I will demonstrate my example on a User management system (I assume you have general knowledge on reflection and AOP).

We start with simple DB table to hold our auditing data:
 
 
 

`id`,
 `username`
 `user_type`
 `action`
 `target_user`
 `date`
 `user_ip`

We need to populate 4 main fields(Username, UserType, Action, TargetUser)

*Username – the user who performs the action

*TargetUser – Target user the action is performed.

Now let’s create new annotation to mark our wanna-be-audit method. We’re going to be very “creative” and use: @AuditAble

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface Auditable {
AuditingActionType actionType();
}

Annotated @AuditAble method example:

@Override
 @Transactional
 @Auditable(actionType = AuditingActionType.INTERNAL_USER_REGISTRATION)
 public void createInternalUser(UserDTO userDTO) {
 userCreationService.createInternalUserOnDB(userDTO);

}

Our future Aspect (aop) will collect some auditing data from the method param’s using DTO’s. In our case the target username and the actionType will be collected as our auditing info.

For that I created another annotation AuditingTargetUsername:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface AuditingTargetUsername {
String value() default "";
}

So inside UserDTO we got:

public abstract class UserDTO implements Serializable {

 @NotNull
 @AuditingTargetUsername
 private String userName;

...

}

We annotated the userName with @AuditingTargetUsername. That information will be collected later on.

Now let’s create our AOP’s aspect. Here all the auditing logic is collected and performed (Intercepting @Auditable methods, Extracting information from annotations, Using repository to save the final auditing record):

@Aspect
public class AuditingAspect {

....

@After("@annotation(auditable)")
 @Transactional
 public void logAuditActivity(JoinPoint jp, Auditable auditable) {
 String targetAuditingUser;
 String actionType = auditable.actionType().getDescription();

 String auditingUsername = Authentication auth = SecurityContextHolder.getContext().getAuthentication().getName()
 role = userService.getCurrentUser(false).getPermissionsList().toString();
 auditingUsernameIp = request.getRemoteAddr();
 }
 logger.info(
 "Auditing information. auditingUsername=" + auditingUsername + ", actionType=" + actionType + ", role=" + role + ", targetAuditingUser="
 + targetAuditingUser + " auditingUsernameIp=" + auditingUsernameIp
 );
 auditingRepository
 .save(new AuditingEntity(auditingUsername, role, actionType, targetAuditingUser, auditingUsernameIp,
 new Timestamp(new java.util.Date().getTime())));
 }

ill explains the main code areas:

Pointcut – all @Auditable.annotations
Advice – Type @After (We want to audit after the method is invoked)

ActionType value is retrieved via the annotated method’s declaration:

@Auditable(actionType = AuditingActionType.INTERNAL_USER_REGISTRATION)

auditingUsername is the current user who performs the action (in our case the logged in user). I retrieved that via SecurityContext(Spring Security).

Now we will extract the @targetAuditingUser field via reflection on runtime:

targetAuditingUser = extractTargetAuditingUser(jp.getArgs());
...

public String extractTargetAuditingUserFromAnnotation(Object obj) {
...
 result = getTargetAuditingUserViaAnnotation(obj);

...

}

Thats the logic to extract the annotated fields via reflection:

private String getTargetAuditingUserViaAnnotation(Object obj) {
class cl=obj.getClass()
 String result = null;
 try {
 for (Field f : cl.getDeclaredFields())
 for (Annotation a : f.getAnnotations()) {
 if (a.annotationType() == AuditingTargetUsername.class) {
 f.setAccessible(true);
 Field annotatedFieldName = cl.getDeclaredField(f.getName());
 annotatedFieldName.setAccessible(true);
 String annotatedFieldVal = (String) annotatedFieldName.get(obj);
 logger.debug("Found auditing annotation. type=" + a.annotationType() + " value=" + annotatedFieldVal.toString());
 result = annotatedFieldVal;
 }
 }
 } catch (Exception e) {
 logger.error("Error extracting auditing annotations from obj" + obj.getClass());
 }
 return result;
 }

Result on DB:

audit1

That’s it. We’ve got clean auditing infrastructure all you need is to annotate your method with @Auditable and annotate inside your DTO’s/Entities the desired information to be audited.

Idan.

Related Whitepaper:

Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions

Get ready to program in a whole new way!

Functional Programming in Java will help you quickly get on top of the new, essential Java 8 language features and the functional style that will change and improve your code. This short, targeted book will help you make the paradigm shift from the old imperative way to a less error-prone, more elegant, and concise coding style that’s also a breeze to parallelize. You’ll explore the syntax and semantics of lambda expressions, method and constructor references, and functional interfaces. You’ll design and write applications better using the new standards in Java 8 and the JDK.

Get it Now!  

One Response to "Auditing infrastructure for your app using Spring AOP, Custom annotations and Reflection"

  1. sumeet says:

    Thanks Idan. This is what I am looking for. Would appreciate if you can upload archive of the project.
    Thanks
    Sumeet

Leave a Reply


× six = 36



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

20,709 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books