Logging with aspects - java

Have a RESTful web-serice with Facade layer, Service layer and Dao layer.
Trying to journalize all invokes of all methods of classes, marked with annotation #Log
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface Log {
}
Here's the aspect code:
public class LoggingAspect {
#Around("#target(Log)")
public Object log(ProceedingJoinPoint pjp) throws Throwable {
log.debug("Start " + pjp.getSignature().getName());
Object result = pjp.proceed();
log.debug("End " + pjp.getSignature().getName());
return result;
}
}
Facade, Service and Dao are marked with #Log.
Some example:
public Obj Facade.createObj(String name){ //1
return service.createObj(name);
}
public Obj Service.createObj(String name){ //2
return dao.createObj(name);
}
public Obj Dao.createObj(String name){ //3
Long idOfCreatedObj = /*code that creates an object and returns it's ID*/;
return loadObjById(idOfCreatedObj); /* load the created object by id */
}
public Obj Dao.loadObjById(Long id){ //4
return /* code that loads the object by it's id */;
}
In this example methods 1, 2, 3 are logged successfully. But the nested dao method (loadObjById) is not logged.
WHY?
P.S. In spring-config.xml there's
<aop:aspectj-autoproxy proxy-target-class="true"/>

The issue is self calls (this.methodcall()) bypass the dynamic/cglib proxy that is created by Spring to handle the cross cutting concern.
The fix is either to use full Aspectj (compile time or load time weaving) or make the call by getting hold of the proxy (instead of alling this.methodCall(), use proxy.methodCall()
You can get hold of the proxy this way:
<aop:aspectj-autoproxy expose-proxy="true"/>
And in your code: AopContext.currentProxy() will give you a reference to the proxy. Here is one blog article on this if you are interested - http://www.java-allandsundry.com/2012/07/reference-to-dynamic-proxy-in-proxied.html

Related

How to audit methods in Java Spring Boot

I am writing a Spring Boot Application. I want to audit methods with my annotation #AuditMetod: For example I have method foo() with the annotation:
#AuditMetod(name = "SomeValue")
foo() {...}
I want to handle and audit such methods like this (the simplest example):
auditMethod(Method method) {
if (method.hasAnnotation(AuditMethod.class)) {
System.out.println (method.getName() + " was called at " + new Date())
}
}
upd
Thanks to #Karthikeyan #Swapnil Khante and #misha2048 I understood, that I need to use AOP. But I have 2 problems:
The only method in Aspect class in not being called and I don't see the inscription "----------ASPECT METHOD IS CALLED-----------" in log
How can I check in aspect method what method it is intercepting. To get an instance of Method class.
Now I have the following code:
Controller:
#PostMapping
#LoggingRest(executor = "USER", method = "CREATE", model = "SUBSCRIPTION")
public ResponseEntity<?> create(#Valid #RequestBody SubscriptionRequestDto dto) {
...
}
Aspect:
`#Aspect
#Slf4j
#Component
public class AuditAspect {
#Pointcut(value = "#annotation(com.aspect.annotations.LoggingRest)")
public void auditMethod(ProceedingJoinPoint proceedingJoinPoint) {
log.info("----------ASPECT METHOD IS CALLED------------");
}`
And annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface LoggingRest {
String executor() default "SYSTEM";
String method() default "";
String model() default "";
}
Auditing is a cross-cutting concern and can be handled using AOP.
Another solution would be to use a low-level solution by writing a custom annotation and using a Spring interceptorto write your business logic.
To use the Spring interceptor you will need to implement the HandlerInterceptor interface
Example of the annotation
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Audit {
boolean active() default true;
}
Interceptor example
#Component
public class AuditInterceptor implements HandlerInterceptor {
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Audit annotation = handlerMethod.getMethodAnnotation(Audit.class);
if (annotation != null && annotation.active()) {
// your business logic
}
}
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
check this interceptor example
I think one of the solutions here, as #Karthikeyan mentioned, is to use Spring AOP.
If you are not aware a brief introduction - spring-aop module implements the aspect oriented programming paradigm. We extract some common functionality, that we generally want to apply to some subset of functions/methods, to an entity called Aspect (see class annotated with #Aspect). This class will contain out cross-cutting functionality - such as auditing, for instance we want to audit the methods execution time, lets say. We just put the code to be executed, the condition, which tell the spring what exact beans methods should be affect by this aspect, see below.
For example, if I can audit the method execution duration with the following very simple example (in my case I said that any public method, returning void inside the Class com.example.stackoverflow.BusinessLogicClass must be inspected by this Aspect):
#SpringBootApplication
#EnableAspectJAutoProxy
public class StackoverflowApplication implements ApplicationRunner {
#Autowired
private BusinessLogicClass businessLogicClass;
public static void main(String[] args) {
SpringApplication.run(StackoverflowApplication.class, args);
}
#Override
public void run(ApplicationArguments args) throws Exception {
businessLogicClass.test();
}
}
#Aspect
#Component
class MyAspectLogicClass {
#Around("execution(public void com.example.stackoverflow.BusinessLogicClass.*(..))")
public Object hangAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long before = System.currentTimeMillis();
Object returnedValue = proceedingJoinPoint.proceed();
long after = System.currentTimeMillis();
System.out.printf("Retruned in '%s' ms %n", (after - before));
return returnedValue;
}
}
#Component
class BusinessLogicClass {
public void test() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
In my case, I will get the time before method execution, then by the means of
proceedingJoinPoint.proceed() call I delegate the execution to the real method, and then, once I get the response back, I will get the current system time and calculate the execution time, fairly simple.
I hope I have at least directed you somewhere, if you are looking for documentation, this are the resources I suggest you should look for:
https://docs.spring.io/spring-framework/docs/2.5.x/reference/aop.html offical spring doc (stale a bit, but there are some valuable things to learn)
https://docs.spring.io/spring-framework/docs/4.3.15.RELEASE/spring-framework-reference/html/aop.html is more fresh doc
Hope it helped :)
The problem was in right annotation. In Aspect class I tried #Around and everything works as I need.
#Aspect
#Slf4j
#Component
public class AuditAspect {
#Around(value = "#annotation(com.aspect.annotations.LoggingRest)")
public void auditMethod(ProceedingJoinPoint proceedingJoinPoint) {
var method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();
log.info("----------ASPECT METHOD IS CALLED------------");
}
}
For getting a Method instance I use fallowing code
Method method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();

AOP and annotation 'inheritance'

let's consider the following situation.
#interface LoggedMethodInvocation{}
#LoggedMethodInvocation
#interface MonitoredMethodInvocation{}
I would like the #MonitoredMethodInvocation annotation implying the #LoggedMethodInvocation annotation.
class LoggingAOPConfig {
#Pointcut("#annotation(LoggedMethodInvocation)")
public void servicePointcut() {
}
#Around("servicePointcut()")
public Object logMethodInvocation(ProceedingJoinPoint pjp) throws Throwable {
// log the method invocation...
}
}
class MonitoringAOPConfig {
#Pointcut("#annotation(MonitoredMethodInvocation)")
public void servicePointcut() {
}
#Around("servicePointcut()")
public Object monitorResponseTime(ProceedingJoinPoint pjp) throws Throwable {
// add some meters to the method invocation
}
}
Now I would like to introduce some method, which shall be both monitored and logged. And I would like to annotate the method only with one annotation, namely #MonitoredMethodInvocation.
class SomeService {
#MonitoredMethodInvocation
Object someMethod(Object requestPayload) {
// ...
return responsePayload;
}
}
However it doesn't play, the logging aspect is not taken into the account.
There is spring's AnnotationUtils.findAnnotation which offers the needed functionality (of recognizing, whether the #LoggedMethodInvocation shall be considered). However, I don't know how to put this into the pointcut configuration.
How shall I modify the logging AOP config so it will recognize the logging annotation even if it is hidden behind the #MonitoredMethodInvocation?

Why is caching not working on wrapped reactive Methods [duplicate]

Spring cache is not working when calling cached method from another method of the same bean.
Here is an example to explain my problem in clear way.
Configuration:
<cache:annotation-driven cache-manager="myCacheManager" />
<bean id="myCacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="myCache" />
</bean>
<!-- Ehcache library setup -->
<bean id="myCache"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:shared="true">
<property name="configLocation" value="classpath:ehcache.xml"></property>
</bean>
<cache name="employeeData" maxElementsInMemory="100"/>
Cached service :
#Named("aService")
public class AService {
#Cacheable("employeeData")
public List<EmployeeData> getEmployeeData(Date date){
..println("Cache is not being used");
...
}
public List<EmployeeEnrichedData> getEmployeeEnrichedData(Date date){
List<EmployeeData> employeeData = getEmployeeData(date);
...
}
}
Result :
aService.getEmployeeData(someDate);
output: Cache is not being used
aService.getEmployeeData(someDate);
output:
aService.getEmployeeEnrichedData(someDate);
output: Cache is not being used
The getEmployeeData method call uses cache employeeData in the second call as expected. But when the getEmployeeData method is called within the AService class (in getEmployeeEnrichedData), Cache is not being used.
Is this how spring cache works or am i missing something ?
I believe this is how it works. From what I remember reading, there is a proxy class generated that intercepts all requests and responds with the cached value, but 'internal' calls within the same class will not get the cached value.
From https://code.google.com/p/ehcache-spring-annotations/wiki/UsingCacheable
Only external method calls coming in through the proxy are
intercepted. This means that self-invocation, in effect, a method
within the target object calling another method of the target object,
will not lead to an actual cache interception at runtime even if the
invoked method is marked with #Cacheable.
Since Spring 4.3 the problem could be solved using self-autowiring over #Resource annotation:
#Component
#CacheConfig(cacheNames = "SphereClientFactoryCache")
public class CacheableSphereClientFactoryImpl implements SphereClientFactory {
/**
* 1. Self-autowired reference to proxified bean of this class.
*/
#Resource
private SphereClientFactory self;
#Override
#Cacheable(sync = true)
public SphereClient createSphereClient(#Nonnull TenantConfig tenantConfig) {
// 2. call cached method using self-bean
return self.createSphereClient(tenantConfig.getSphereClientConfig());
}
#Override
#Cacheable(sync = true)
public SphereClient createSphereClient(#Nonnull SphereClientConfig clientConfig) {
return CtpClientConfigurationUtils.createSphereClient(clientConfig);
}
}
The example below is what I use to hit the proxy from within the same bean, it is similar to #mario-eis' solution, but I find it a bit more readable (maybe it's not:-). Anyway, I like to keep the #Cacheable annotations at the service level:
#Service
#Transactional(readOnly=true)
public class SettingServiceImpl implements SettingService {
#Inject
private SettingRepository settingRepository;
#Inject
private ApplicationContext applicationContext;
#Override
#Cacheable("settingsCache")
public String findValue(String name) {
Setting setting = settingRepository.findOne(name);
if(setting == null){
return null;
}
return setting.getValue();
}
#Override
public Boolean findBoolean(String name) {
String value = getSpringProxy().findValue(name);
if (value == null) {
return null;
}
return Boolean.valueOf(value);
}
/**
* Use proxy to hit cache
*/
private SettingService getSpringProxy() {
return applicationContext.getBean(SettingService.class);
}
...
See also Starting new transaction in Spring bean
Here is what I do for small projects with only marginal usage of method calls within the same class. In-code documentation is strongly advidsed, as it may look strage to colleagues. But its easy to test, simple, quick to achieve and spares me the full blown AspectJ instrumentation. However, for more heavy usage I'd advice the AspectJ solution.
#Service
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
class AService {
private final AService _aService;
#Autowired
public AService(AService aService) {
_aService = aService;
}
#Cacheable("employeeData")
public List<EmployeeData> getEmployeeData(Date date){
..println("Cache is not being used");
...
}
public List<EmployeeEnrichedData> getEmployeeEnrichedData(Date date){
List<EmployeeData> employeeData = _aService.getEmployeeData(date);
...
}
}
If you call a cached method from same bean it will be treated as a private method and annotations will be ignored
Yes, the caching will not happen because of the reasons that were already mentioned in the other posts. However I would solve the problem by putting that method to its own class (service in this case). With that your code will be easier to maintain/test and understand.
#Service // or #Named("aService")
public class AService {
#Autowired //or how you inject your dependencies
private EmployeeService employeeService;
public List<EmployeeData> getEmployeeData(Date date){
employeeService.getEmployeeData(date);
}
public List<EmployeeEnrichedData> getEmployeeEnrichedData(Date date){
List<EmployeeData> employeeData = getEmployeeData(date);
...
}
}
#Service // or #Named("employeeService")
public class EmployeeService {
#Cacheable("employeeData")
public List<EmployeeData> getEmployeeData(Date date){
println("This will be called only once for same date");
...
}
}
In my Case I add variable :
#Autowired
private AService aService;
So I call the getEmployeeData method by using the aService
#Named("aService")
public class AService {
#Cacheable("employeeData")
public List<EmployeeData> getEmployeeData(Date date){
..println("Cache is not being used");
...
}
public List<EmployeeEnrichedData> getEmployeeEnrichedData(Date date){
List<EmployeeData> employeeData = aService.getEmployeeData(date);
...
}
}
It will use the cache in this case.
Better approach should be creating another service like ACachingService and call ACachingService.cachingMethod() instead of self Autowiring ( or any other approach trying to self inject). This way you do not fall into Circular dependency, which may be resulted in warning/error when upgrade to newer Spring ( Spring 2.6.6 in my case ) :
ERROR o.s.boot.SpringApplication - Application run failed
org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'webSecurityConfig':
Requested bean is currently in creation: Is there an unresolvable circular reference?
We looked at all the solutions here and decided to use a separate class for the cached methods because Spring 5 doesn't like circular dependencies.
Use static weaving to create proxy around your bean. In this case even 'internal' methods would work correctly
I use internal inner bean (FactoryInternalCache) with real cache for this purpose:
#Component
public class CacheableClientFactoryImpl implements ClientFactory {
private final FactoryInternalCache factoryInternalCache;
#Autowired
public CacheableClientFactoryImpl(#Nonnull FactoryInternalCache factoryInternalCache) {
this.factoryInternalCache = factoryInternalCache;
}
/**
* Returns cached client instance from cache.
*/
#Override
public Client createClient(#Nonnull AggregatedConfig aggregateConfig) {
return factoryInternalCache.createClient(aggregateConfig.getClientConfig());
}
/**
* Returns cached client instance from cache.
*/
#Override
public Client createClient(#Nonnull ClientConfig clientConfig) {
return factoryInternalCache.createClient(clientConfig);
}
/**
* Spring caching feature works over AOP proxies, thus internal calls to cached methods don't work. That's why
* this internal bean is created: it "proxifies" overloaded {#code #createClient(...)} methods
* to real AOP proxified cacheable bean method {#link #createClient}.
*
* #see Spring Cache #Cacheable - not working while calling from another method of the same bean
* #see Spring cache #Cacheable method ignored when called from within the same class
*/
#EnableCaching
#CacheConfig(cacheNames = "ClientFactoryCache")
static class FactoryInternalCache {
#Cacheable(sync = true)
public Client createClient(#Nonnull ClientConfig clientConfig) {
return ClientCreationUtils.createClient(clientConfig);
}
}
}
I would like to share what I think is the easiest approach:
Autowire the controller and use to call the method it instead of using the class context this.
The updated code would look like:
#Controller
public class TestController {
#Autowired TestController self;
#RequestMapping("/test")
public String testView(){
self.expensiveMethod();
return "test";
}
#Cacheable("ones")
public void expensiveMethod(){
System.out.println("Cache is not being used");
}
}
The default advice mode for processing caching annotation is “proxy”. At the startup of an application, all the caching annotations like #Caching, #Cacheable, #CacheEvict etc. are scanned and a target proxy class is generated for all of these classes. The proxy allows for intercepting the calls to these cacheable methods, which adds the caching advice/behavior.
So when we invoke the cacheable methods from the same class, as shown below, calls from the clients don’t get intercepted in a way that allows for caching advice to be added to them. Hence, every single time there is an unexpected cache miss.
Solution: Invoke the Cacheable methods from a different bean to use proxy class with caching advice.

Method invocation based on custom annotation in Spring?

I have a custom annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Controller {
EventType[] events() default EventType.MESSAGE;
}
And there are methods in class B using them like below:
#Controller(events = {EventType.MESSAGE, EventType.DIRECT_MESSAGE})
public void onMessage(Message msg) { }
#Controller(events = {EventType.STAR_ADDED})
public void onStarAdded(Message msg) { }
Now, I want to invoke the above methods based on the annotation events value from another class A. In other words, when class A receives an event of type STAR_ADDED, I want to invoke all methods in class B with annotation #Controller(events = {EventType.STAR_ADDED}).
I know how to do this in Java but does Spring provide any API to do this? If yes, a code snippet would be helpful too.
Solution 1:
You could also do something like this:
enum EventType {
MESSAGE {
#Override
public void handleMessage(Service service, Message message) {
service.onMessage(message);
}
},
STAR_ADDED {
#Override
public void handleMessage(Service service, Message message) {
service.onStarAdded(message);
}
public abstract void handleMessage(Service service, Message message);
}
}
In your other class, where you know what is the "active" event:
yourEvent.handleMessage(service, message);
Solution 2:
I don't know if spring has anything precisely for that, otherwise you could also use reflection. Here's an example using reflection (I much prefer the solution above => enum without reflection):
for(Method method: Service.class.getDeclaredMethods()){
Controller annotation = m.getAnnotation(Controller.class);
for(EventType event: annotation.events()){
if(event.equals(yourActiveEventType)){
method.invoke(service, message);
}
return ...
}
}
Hint (not a solution) 3:
I really don't think the following applies for your scenario, but I thought I'd mention it... Spring AOP lets you trigger some code when an annotated method is called (it's kind of the opposite of your scenario), check this answer, but it may be worth the read for you: aspectj-pointcut-for-all-methods-of-a-class-with-specific-annotation
#Around("execution(#Controller * com.exemple.YourService.*(..))")
public Object aroundServiceMethodAdvice(final ProceedingJoinPoint pjp)
throws Throwable {
// perform actions before
return pjp.proceed();
// perform actions after
}
Solution 4: (added after comments)
Using org.reflections
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.10</version>
</dependency>
example:
Service service = ...;
Message message = ...;
Set<Method> methods =
ReflectionUtils.getMethods(Service.class, ReflectionUtils.withAnnotation(Controller.class),ReflectionUtils.withParametersAssignableTo(Message.class));
for(Method m: methods){
Controller controller = m.getAnnotation(Controller.class);
for(EventType eventType: controller.value()){
if(EventType.MESSAGE.equals(eventType)){
m.invoke(service, message);
}
}
}
This assumes that you already hold the reference to the Service object (where your methods are).
Since you are using Spring, if your 'Services' are spring managed, you may get the instance from spring's context, you'll have to try it out for yourself, as this is somewhat bound to your design:
#Autowired
private ApplicationContext appContext;
Reflections r = new Reflections(new MethodAnnotationsScanner(), "com.your.package");
Set<Method> methods = r.getMethodsAnnotatedWith(Controller.class);
for(Method m: methods){
Controller controller = m.getAnnotation(Controller.class);
for(EventType eventType: controller.value()){
if(EventType.MESSAGE.equals(eventType)){
String className = m.getDeclaringClass().getSimpleName();
className = className.replaceFirst(className.substring(0,1), className.substring(0,1).toLowerCase());
Object service = appContext.getBean(className);
m.invoke(service, message);
}
}
}
This works if your Class is spring managed and is added to the context using its default camelcase name.
You may simplify the logic, but I believe the principal elements are there.

Statically finding methods that need #Transactional but don't have it

In order to avoid database connection leaks, it's possible to configure c3p0 to let you know when they occur, and point to the problem code:
<property name="unreturnedConnectionTimeout" value="900"/> <!-- seconds -->
<property name="debugUnreturnedConnectionStackTraces" value="true"/>
It'd be better to catch these before they are ever deployed. That is, catch them statically at build time. It seems that it would possible to 1) Identify classes that are DAO-type classes, either annotated as such or via an hbm file, and then 2) trace call trees upward and flag methods that are not marked #Transactional. Even if there were some false positives, that'd be a useful tool to help eliminate this issue. IDEs such as IntelliJ and Eclipse already know how to find a method's callers.
Is there an opensource tool that does something like this? Even if it didn't do the first step, it's easy enough to identify DAOs manually. It's the second part that would benefit most from an automated solution.
I faced a similar problem in a Spring application, and wrote a bean processer that ran at application startup, and examined each bean after it was initialized. It's not a compile-time solution, but assuming you run the application somewhere before production, you'll have an opportunity to find methods lacking the annotation in question.
It looked for classes with the #Service annotation, iterated over interfaces implemented by that class, and inspected each interface's methods. It looked for methods that either didn't have any method-level security defined, or had a custom #Unsecured annotation, indicating we had already discovered that method lacked security but determined that it didn't need security. Any methods that lacked security and the #Unsecured annotation were logged.
The following code worked at one point, but was used with an older version of Spring. I don't know if it will work with Spring 3.x, but if not, a similar approach should still work. You might need to adjust the inspection logic to suite your needs (e.g. look for classes with a different class-level annotation, inspect methods on the class itself instead of methods in interfaces implemented by the class, etc). Note that my approach didn't try to traverse call trees, so you'd probably get more false positives (i.e. not all service methods end up calling DAO methods).
public class UnsecuredServiceMethodProcessor implements BeanPostProcessor {
private static final Logger logger = LogManager.getLogger(UnsecuredServiceMethodProcessor.class);
private final MethodSecurityInterceptor interceptor;
public UnsecuredServiceMethodProcessor(MethodSecurityInterceptor interceptor) {
this.interceptor = interceptor;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
Class<?> beanClass = bean.getClass();
if (logger.isInfoEnabled()) {
logger.info("checking bean " + beanName + " of type " + beanClass.getName());
}
for (Class<?> interfaceClass: beanClass.getInterfaces()) {
checkClass(beanClass, interfaceClass);
}
return bean;
}
/**
* #param beanClass
* #param interfaceClass
*/
private void checkClass(Class<?> beanClass, Class<?> interfaceClass) {
if (interfaceClass.isAnnotationPresent(Service.class)) {
if (logger.isDebugEnabled()) {
logger.debug("found service implementation: " + interfaceClass + " on " + beanClass);
}
for (Method method: interfaceClass.getMethods()) {
if (!method.isAnnotationPresent(Unsecured.class)) {
if (logger.isDebugEnabled()) {
logger.debug("checking " + method.getName());
}
MethodSecurityMetadataSource msms = interceptor.getSecurityMetadataSource();
Collection<ConfigAttribute> atts = msms.getAttributes(method, interfaceClass);
if (atts == null || atts.size() == 0) {
logger.warn("unsecured method: " + method.getDeclaringClass().getName() + "." + method.getName());
}
}
}
}
}
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
}
You'll need to either explicitly define a bean of this type in the application context, or add the #Component class-level annotation and scan for it.

Categories