Spring AOP Logging and Cache - java

I'm logging method input and output parameters by a simple Aspect.
package com.mk.cache;
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.stereotype.Component;
import java.util.Arrays;
#Aspect
#Component
public class LoggingAspect {
#Around("within(#com.mk.cache.LoggedIO *) && execution(* *(..))")
public Object logAroundPublicMethod(ProceedingJoinPoint joinPoint) throws Throwable {
String wrappedClassName = joinPoint.getSignature().getDeclaringTypeName();
Logger LOGGER = LoggerFactory.getLogger(wrappedClassName);
String methodName = joinPoint.getSignature().getName();
LOGGER.info("LOG by AOP - invoking {}({})", methodName, Arrays.toString(joinPoint.getArgs()));
Object result = joinPoint.proceed();
LOGGER.info("LOG by AOP - result of {}={}", methodName, result);
return result;
}
}
which is attached by this Annotation.
package com.mk.cache;
public #interface LoggedIO {
}
I use this mechanism to log inputs and outputs of methods like this (notice #LoggedIO):
package com.mk.cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
#Service
#LoggedIO
public class CachedService {
private static final Logger LOGGER = LoggerFactory.getLogger(CachedService.class);
#Cacheable("myCacheGet")
public int getInt(int input) {
LOGGER.info("Doing real work");
return input;
}
}
I also use Spring Cache. Here is the example application.
package com.mk.cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
#SpringBootApplication
#EnableCaching
public class CacheApplication implements CommandLineRunner {
private static final Logger LOGGER = LoggerFactory.getLogger(CacheApplication.class);
public static void main(String[] args) {
SpringApplication.run(CacheApplication.class, args);
}
#Autowired
private CachedService cachedService;
#Override
public void run(String... args) throws Exception {
LOGGER.info("cachedService.getInt(1)={}", cachedService.getInt(1));
LOGGER.info("cachedService.getInt(1)={}", cachedService.getInt(1));
}
}
The output looks like this:
LOG by AOP - invoking getInt([1])
Doing real work
LOG by AOP - result of getInt=1
cachedService.getInt(1)=1
cachedService.getInt(1)=1
My problem is, that when I call LOGGER.info("cachedService.getInt(1)={}", cachedService.getInt(1)); for the second time, the cached value is used, but the input and output parameters are not logged, as the cache is the first wrapper. Is it possible to somehow configure the LoggingAspect to be the first wrapper, so I will be able to use both AOP logging and both Spring Cache?

Just implement spring Ordered interface and in getOrder() method return 1.
#Aspect
#Component
public class LoggingAspect implements Ordered {
#Around("within(#com.mk.cache.LoggedIO *) && execution(* *(..))")
public Object logAroundPublicMethod(ProceedingJoinPoint joinPoint) throws Throwable {
String wrappedClassName = joinPoint.getSignature().getDeclaringTypeName();
Logger LOGGER = LoggerFactory.getLogger(wrappedClassName);
String methodName = joinPoint.getSignature().getName();
LOGGER.info("LOG by AOP - invoking {}({})", methodName, Arrays.toString(joinPoint.getArgs()));
Object result = joinPoint.proceed();
LOGGER.info("LOG by AOP - result of {}={}", methodName, result);
return result;
}
#Override
public int getOrder() {
return 1;
}
}
Read more here. Chapter 11.2.7

Related

I can't get value of a property in spring-boot application

I am coding in spring-boot. I tried to get the value of properties.properties in other packages without success. For example in the classe ClassUtils.java, the value of testValue is always null
This is my project
This is my code:
package com.plugins.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.plugins.domain.ClassUtils;
#RestController
public class SearchContactController {
#Value("${environnement.test}")
String testValue;
#Value("${environnement.url}")
String urlValue;
#RequestMapping(value = "/test")
public String pingRequest() {
System.out.println("value ===> " + testValue + " /// " + urlValue);
return "test !" + ClassUtils.getTestValue();
}
}
This is my second class, where I can't get the value of testValue variable:
package com.plugins.domain;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
#Component
public class ClassUtils {
#Value("${environnement.test}")
static String testValue;
public static String getTestValue(){
return "The return "+testValue;
}
}
This is my springApp.java
package com.plugins;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class SpringBootVideApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootVideApplication.class, args);
}
}
Enable #ComponentScan({"com.plugins"}) , in Application
To access the properties defined in application.properties
myapp.url="xxxxxx"
in your class
#Value("${myapp.url}")
private String testValue;
but this cannot be a static variable, if it is a static variable you do some hack like this, by defining setter method
private static String testValue;
#Value("${myapp.url}")
public void testValue(String value) {
testValue = value;
}
I resolve this issue by addin #Autowired in the class which use the method of the other class this is a snippet
// Class: SearchContactController.java
#Autowired
ClassUtils cd;
#RequestMapping(value = "/ping")
public String pingRequest() {
return "Ping OK !" + cd.getTestValue();
}

Interrupt spring scheduler task before next invocation

I have a Spring-Boot application which is going to be an orchestration service for several other processes we want to trigger. I have it currently set up using Spring Scheduling pulling crons dynamically from a database. I threw in a rest method to trigger the process to pull new cron information from the database. This logic all works correctly. The only "issue" is that it doesn't use the new cron information until the next scheduled run which gets to the real question. Is there a way to interrupt the current Trigger and schedule one again using the updated cron information. Here is the application for reference:
package com.bts.poc;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
#SpringBootApplication
#EnableScheduling
#RestController
#RequestMapping("/APSCommon/Scheduling")
public class Application implements SchedulingConfigurer {
#Autowired
private DynamicCron dynamicCron;
#Autowired
PropertyManager propertyManager;
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class);
}
private String cronConfig() {
String cronTabExpression = propertyManager.getProperty("COMPANY", "JOB_NAME","CRON_EXPRESSION");
return cronTabExpression;
}
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addTriggerTask(new Runnable() {
#Override
public void run() {
dynamicCron.runJob();
}
}, new Trigger() {
#Override
public Date nextExecutionTime(TriggerContext triggerContext) {
String cron = cronConfig();
CronTrigger trigger = new CronTrigger(cron);
Date nextExec = trigger.nextExecutionTime(triggerContext);
DynamicCron.cronExpression = cron;
return nextExec;
}
});
}
#RequestMapping(value = "/reloadScheduling", method = RequestMethod.GET)
public String reloadScheduling() {
PropertyManager.setResetProperties(true);
return "schedules will be altered next run";
}
}
So using SchedulingConfigurer->configureTasks you can't get access to the ScheduledFuture(s) in the Spring version I am using (4.2.7.RELEASE). From several posts I have read it has been mentioned as possible functionality for the future. I got around this by doing the following:
package com.bts.poc;
import com.bts.poc.service.DynamicCron;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.TimeZone;
import java.util.concurrent.ScheduledFuture;
#SpringBootApplication(exclude = MessageSourceAutoConfiguration.class)
#EnableScheduling
#RestController
public class Application extends SpringBootServletInitializer {
#Autowired
private DynamicCron dynamicCron;
#Autowired
private PropertyManager propertyManager;
private static List<ScheduledFuture> scheduledFutures = new ArrayList<>();
private static final Logger LOGGER = LoggerFactory.getLogger(Application.class);
private static TaskScheduler scheduler;
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
private String cronConfig() {
return propertyManager.getProperty("COMPANY", "JOB_NAME", "CRON_EXPRESSION");
}
#RequestMapping(value = {"scheduling/start"}, method = RequestMethod.GET)
public #ResponseBody String startScheduling() {
scheduleAll();
LOGGER.info("Scheduling of jobs has been started.");
return "Scheduling of jobs has been started.";
}
#RequestMapping(value = {"scheduling/cancel"}, method = RequestMethod.GET)
public #ResponseBody String cancelScheduling() {
cancelAll();
LOGGER.info("Cancelling all scheduled jobs.");
return "Cancelling all scheduled jobs.";
}
private void scheduleAll() {
LOGGER.info("Scheduling all applications to run.");
cancelAll();
//eventually go through the database and load all jobs to be scheduled here.
schedule(cronConfig());
}
/**
* Cancel all the scheduled reports
*/
private void cancelAll() {
for (ScheduledFuture scheduledFuture : scheduledFutures) {
scheduledFuture.cancel(true);
}
scheduledFutures.clear();
}
/**
* Schedule the scheduled report with the given cron schedule information
*/
private void schedule(String cronSchedule) {
TimeZone tz = TimeZone.getDefault();
LOGGER.info("Setting up application {} to execute with cron string: '{}'.", cronSchedule);
CronTrigger trigger = new CronTrigger(cronSchedule, tz);
scheduler = scheduler();
if (scheduler == null) {
LOGGER.error("Unable to schedule job as scheduler was not found");
return;
}
ScheduledFuture<?> future = scheduler.schedule(new DynamicCron(), trigger);
scheduledFutures.add(future);
}
#Bean
public TaskScheduler scheduler() {
if (scheduler == null) {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(10);
scheduler.afterPropertiesSet();
}
return scheduler;
}
}
This basically replicates the functionality the ScheduledTaskRegistrar provides allowing you manage the ScheduledFuture(s). Hopefully this can help someone else in the future.

Spring AOP advice not getting executed

I'm trying to get advice executed, and it isn't working. I am trying to do it without an application context.
This is my Rest Controller:
package hello;
import java.util.concurrent.atomic.AtomicLong;
import aspect.exception.GreetingsNotFoundException;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
#RequestMapping("/greeting")
public Greeting greeting(#RequestParam(value="name", defaultValue="World") String name) {
return new Greeting(counter.incrementAndGet(),
String.format(template, name));
}
}
Aspect class:
#Aspect
//#ComponentScan
public class AfterThrowingException implements ThrowsAdvice {
// Obtain a suitable logger.
private static Logger logger = LoggerFactory.getLogger(AfterThrowingException.class);
#Before("execution(hello.GreetingController.greeting()")
public void logBefore(JoinPoint joinPoint){
System.out.println("Inside AfterThrowingException.logBefore()");
}
Configuration class:
#Configuration
#EnableAspectJAutoProxy
public class AppConfig {
#Bean
public AfterThrowingException afterThrowingException() {
return new AfterThrowingException();
}
}
The format of execution pointcut is:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)
Your controller method takes parameters, but it is not in the definition
Try:
#Before("* execution(hello.GreetingController.greeting(..)")
You need to proceed after completing of #Before advice
jointPoint.proceed();
Your names are out of sync

Spring AOP: aspect is not executing in case of MethodConstraintViolationException

I have a bean annoteded with JSR 303 annotations. I also added Spring aspect (#Around) for handling MethodConstraintViolationException. My problem is: if I execute methods with correct parameters - my aspect works (is executed - breakpoints added), but when I run methods with incorrect parameters then MethodConstraintViolationException is thrown and my aspect is not executed.
package noname.exception;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.hibernate.validator.method.MethodConstraintViolationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import noname.service.exceptions.ValidationException;
import noname.utils.ValidationExceptionProcessor;
#Aspect
public class ExceptionAspect implements Ordered {
#Autowired
private ValidationExceptionProcessor processor;
#Pointcut(value = "execution(* noname.conversionstrategy.api.IDocumentConverter.*(..))")
public void aopDocumentConverterPointcut() {
}
#Pointcut(value = "execution(* noname.service.api.IMailMerger.*(..))")
public void aopMailMargeServicePointcut() {
}
#SuppressWarnings("deprecation")
#Around("aopDocumentConverterPointcut() || aopMailMargeServicePointcut()")
public Object exceptionsAspect(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
try {
return proceedingJoinPoint.proceed();
} catch ( Throwable e ) {
if (e instanceof MethodConstraintViolationException) {
ValidationException exp = processor.process((MethodConstraintViolationException) e);
throw exp;
} else {
throw e;
}
}
}
#Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
Intefaces (IMailMerger and IDocumentConverter) are similar:
package noname.conversionstrategy.api;
import java.util.List;
import javax.validation.constraints.NotNull;
import org.springframework.validation.annotation.Validated;
import noname.service.domain.DocumentActionInput;
import noname.service.domain.DocumentActionResult;
import noname.validator.ValidActionInput;
#Validated
public interface IDocumentConverter {
DocumentActionResult convertDocument(#NotNull(message = "DocumentActionInput must be provided") #ValidActionInput DocumentActionInput document);
List<DocumentActionResult> convertDocuments(#NotNull(message = "DocumentActionInput must be provided") #ValidActionInput List<DocumentActionInput> documents);
}
I suppose spring execute first bean validation (it is probably executed with aspect too (?) ). If this validation throws MethodConstraintViolationException then my aspect is not executed, because spring aop doesn't support catching exceptions from anoother aspect (need confirmation).
I also created test with proxy (everything with my aspect looks fine):
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:applicationContextTest.xml"})
public class ExceptionAspectSpringTest {
#Autowired
private IDocumentConverter documentConverter;
#Autowired
private ExceptionAspect exceptionAspect;
private IDocumentConverter proxy;
#Before
public void setUp() {
AspectJProxyFactory aspectJProxyFactory = new AspectJProxyFactory(documentConverter);
aspectJProxyFactory.addInterface(IDocumentConverter.class);
aspectJProxyFactory.addAspect(exceptionAspect);
proxy = aspectJProxyFactory.getProxy();
}
#Test( expected = ValidationException.class )
public void shouldThownValidationException() {
DocumentActionInput document = new DocumentActionInput();
proxy.convertDocument(document);
}
}
Any help appreciated

Spring AOP Parameterized Annotation

I am new to spring AOP and struggling to achieve the following.
I want to define an annotation #RequirePermission which accepts a parameter String or enum and based on that string/enum I will do some calculations in advice. So, any method defined in Controller having #RequiredPermissions should be validated first. Here is my code so far.
Aspect:
package com.myapp.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 org.springframework.stereotype.Component;
import com.myapp.security.RequirePermissionType;
import com.myapp.security.RequirePermission;
#Aspect
public class RequirePermissionAspect {
#Pointcut("within(#org.springframework.stereotype.Controller *)")
public void controllerBean() {}
#Pointcut("execution(#com.myapp.security.RequirePermission * com.myapp.controller.*.*(..))")
public void methodPointcut() {}
#Before("controllerBean() && methodPointcut() ")
public void afterMethodInControllerClass(com.myapp.security.RequirePermissionType name) {
System.out.println("before advice..");
System.out.println("before advice.."+name.name());
}
Annotation:
package com.myapp.security;
public enum RequirePermissionType {
VIEW, MANAGE, IMPORT, SUPER;
}
#Documented
#Target(ElementType.METHOD )
#Retention(RetentionPolicy.RUNTIME)
#Inherited
public #interface RequirePermission {
/**
*
* #return
*/
RequirePermissionType name() default RequirePermissionType.VIEW ;
}
Now if use the annotation in controller method as below the code works:
#RequestMapping(value={"create"})
#RequirePermission
public String createRegion(Model model){
}
But the following does not work:
#RequestMapping(value={"create"})
#RequirePermission(name=RequirePermissionType.VIEW)
public String createRegion(Model model){
}
Any one could guide me what I am missing and how do I achieve this.
#vamslip here is the updated aspect class. Rest all stays the same.
Updated Aspect:
package com.myapp.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 org.springframework.stereotype.Component;
import com.myapp.security.RequirePermissionType;
import com.myapp.security.RequirePermission;
#Aspect
public class RequirePermissionAspect {
#Pointcut("within(#org.springframework.stereotype.Controller *)")
public void controllerBean() {}
#Pointcut("execution(#com.myapp.security.RequirePermission * com.myapp.controller.*.*(..)) && #annotation(name)")
public void methodPointcut(com.myapp.security.RequirePermission name) {}
#Before("controllerBean() && methodPointcut(name) ")
public void afterMethodInControllerClass(com.myapp.security.RequirePermissionType name) {
System.out.println("before advice..");
System.out.println("before advice.."+name.name());
}
}

Categories