AspectJ: polymorphic logging of methods. Pointcut signature issues - java

I'm having issues with a few pointcut signatures inside my aspect.
1.. Messages are entering our system via 2 exposed interfaces(IIncoming, IOutgoing) and then they are processed by a PartsManager component like:
PartsManager pmanager = new PartManagerImpl();
pmanager.process(message);
public class PartManagerImpl implements PartsManager{
public boolean process(Message message){
//do some messsage processing..
return true;
}
}
2.. All processed messages are then logged via the following aspect.
#Aspect
public class OldMessageLogging {
private static final Logger LOGGER = Logger.getLogger(OldMessageLogging.class);
#Before("execution(* org.company.PartManagerImpl.process(..))")
public void processMessageCalled(final JoinPoint joinPoint) throws Throwable {
LOGGER.info("Message processed.");
return;
}
}
3.. But, now I want to log processed messages but with one additional information.
Instead of Message processed. I want the log entry to become Incoming message processed. or Outgoing message processed.
4.. So, I have defined two additional interfaces and changed PartManagerImpl.java like this:
public class PartManagerImpl implements PartsManager, IncommingMessageProcessor, OutgoingMessageProcessor {
public boolean process(Message message){
//do some messsage processing..
return true;
}
}
public interface IncommingMessageProcessor {
boolean process(Message message);
}
public interface OutgoingMessageProcessor {
boolean process(Message message);
}
5.. Now, message processing is performed like this:
IncommingMessageProcessor inProcessor = new PartManagerImpl();
inProcessor.process(message);
OutgoingMessageProcessor outProcessor = new PartManagerImpl();
outProcessor.process(message);
6.. A new aspect was created to reflect my new logging needs.
#Aspect
public class NewMessageLogging {
private static final Logger LOGGER = Logger.getLogger(NewMessageLogging.class);
#Before("execution(* org.company.IncommingMessageProcessor.process(..))")
public void processIncomingCalled(final JoinPoint joinPoint) throws Throwable {
LOGGER.info("Incoming message processed.");
return;
}
#Before("execution(* org.company.OutgoingMessageProcessor.process(..))")
public void processOutgoingCalled(final JoinPoint joinPoint) throws Throwable {
LOGGER.info("Outgoing message processed.");
return;
}
}
THE CATCH:
Whenever I'm processing a message like IncommingMessageProcessor inProcessor = new PartManagerImpl(); inProcessor.process(message); the message is being logged twice, once as an incoming and once as an outgoing message.
I have expected only a call to the processIncomingCalled method.
But both of my pointcuts were called!
My pointcut signatures are obviously wrong :/
I tried various approaches but to no avail.
Any suggestions?
SOLUTION
Changing the Message class was not an option in my case, but was an viable solution!
I took a different approach to make code changes minimal.
The method public boolean process(Message message) in both interfaces was renamed to processIncoming(Message message) and processOutgoing(Message message).
public interface IncommingMessageProcessor {
boolean processIncoming(Message message);
}
public interface OutgoingMessageProcessor {
boolean processOutgoing(Message message);
}
PartsManager interface now extends both interfaces class PartsManager extends IncommingMessageProcessor, OutgoingMessageProcessor {...}
And the process(Message message) method is now private but invoked by calls to the inherited methods processIncoming(...) and processOutgoing(...)
so my pointcuts now target calls to that methods.
Altered aspect (changed method names)
#Aspect public class NewMessageLogging {
private static final Logger LOGGER = Logger.getLogger(NewMessageLogging.class);
#Before("execution(* org.company.PartsManager.processIncoming(..))")
public void processIncomingCalled(final JoinPoint joinPoint) throws Throwable {
LOGGER.info("Incoming message processed.");
return;
}
#Before("execution(* org.company.PartsManager.processOutgoing(..))")
public void processOutgoingCalled(final JoinPoint joinPoint) throws Throwable {
LOGGER.info("Outgoing message processed.");
return;
}
}

I assume that incoming messages implement IIncoming and outgoing ones implement IOutgoing. Then you can try:
#Before("execution(* org.company.PartManagerImpl.process(..)) && args(message)")
public void processIncomingCalled(final JoinPoint joinPoint, IIncoming message)
and
#Before("execution(* org.company.PartManagerImpl.process(..)) && args(message)")
public void processOutgoingCalled(final JoinPoint joinPoint, IOutgoing message)
A class implementing two interfaces which both define the same method signature seems very iffy to me and you probably should not do that. If both incoming and outgoing messages are handled by the same message handler (and even the same method) there should only be one interface for the handler. The distinguishing factor should be the messages and their hierarchy/interfaces. That's what the args-part of my suggested pointcut is supposed to check for (I don't have access to a compiler with AspectJ at the moment, so I haven't been able to test it myself yet).

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?

Exception Handler not working in a spring boot app with messaging

I've seen many a posts and Q&As on StackOverflow for custom error handling for a REST/MVC applications.
My situation is slightly different though. My application uses Java Messaging (with ActiveMQ and SpringIntegration) and
the method below is triggered as a response to a JMessage:
public BenefitVersion getVersionAt(LocalDate referenceDate) {
return versions.stream()
.filter(benefitVersion -> !benefitVersion.getStartDate().isAfter(referenceDate))
.reduce(maxBy(getBenefitVersionStartDateComparator()
.thenComparingLong(BenefitVersion::getId)))
.orElseThrow(() -> new IllegalBenefitVersionException(guid,
format("Benefit %s does not have active version on %s", guid, referenceDate)));
}
I've defined custom exception as below:
public class IllegalBenefitVersionException extends RuntimeException {
private UUID benefitId;
public IllegalBenefitVersionException(final UUID benefitId, final String message) {
super(message);
this.benefitId = benefitId;
}
}
And a custom handler for that exception:
#ControllerAdvice
public class IllegalBenefitVersionExceptionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(IllegalBenefitVersionExceptionHandler.class);
private final DlqExceptionDetailService exceptionDetailService;
#Autowired
public IllegalBenefitVersionExceptionHandler(DlqExceptionDetailService exceptionDetailService) {
this.exceptionDetailService = exceptionDetailService;
}
#ExceptionHandler(IllegalBenefitVersionException.class)
public void handleException(Throwable t) {
IllegalBenefitVersionException exception = (IllegalBenefitVersionException) t;
exceptionDetailService.newWithBenefitIdAndSave(exception.getBenefitId(), t.getMessage());
LOGGER.error("From ExceptionHandler: {}", t.getMessage(), t);
}
}
But that never gets called. Is the reason because I use the #ControllerAdvice whereas there is no controller?
Or perhaps I need to add special component scan somewhere?
If so, how do I wire up a custom exception handler in a messaging-based, as opposed to, REST, application?

OSGi AspectService get Service Properties of the Aspected Service

Given a service Sender with properties, and an aspect service LogSender, how does LogSender get the service properties of the current Sender? I'd like to add a property to optionally log the data that a specific Sender is sending.
component.getServiceProperties(); seems to return the LogSender's service properties instead of Sender's properties.
I've looked at ConfigAdmin but I don't see a way to associate the Sender that LogSender aspected, with the specific configuration used.
I'm using Apache Felix as my OSGi container if that's relevant.
Here's the Activator's init method after adding ConfigurationAdmin to the dependency list.
public void init(BundleContext context, DependencyManager manager) throws Exception {
manager.add(createAspectService(Sender.class, null, 10).setImplementation(LogSender.class)
.add(createServiceDependency().setService(ConfigurationAdmin.class)
.setRequired(true)));
.add(createServiceDependency().setService(LogService.class).setRequired(true)));
}
To inject the service properties of the original Sender into the LogSender aspect, you can use the signature in DependencyActivatorBase (or DependencyManager), which allows to specify "add/change/remove" LogSender aspect callback methods:
DependencyActivatorBase.createAspectService(
Class<?> serviceInterface,
String serviceFilter,
int ranking,
String add,
String change,
String remove);
Then the LogSenderAspect callbacks method signature can take as arguments the Sender service, as well as the Sender service properties Map.
Now, the second (simpler) solution is to specify a service filter for your aspect, and in this case; no need to specify any callbacks.
let's take a look at the first solution with callbacks, where the LogSender aspect defines a "setSender(Sender, Map)" method, and the aspect will then only log the "send" method of Sender services having "foo=bar" service properties (here, we ignore service change/removed callbacks):
public class Activator extends DependencyActivatorBase{
public void init(BundleContext ctx, DependencyManager dm) throws Exception {
Component logSender = createAspectService(Sender.class, null, 10, "setSender", null, null)
.setImplementation(LogSender.class)
.add(createServiceDependency().setService(LogService.class).setRequired(true));
dm.add(logSender);
}
}
class LogSender implements Sender {
volatile Sender sender;
volatile Map<String, Object> senderProperties;
volatile LogService log;
void setSender(Sender sender, Map<String, Object> senderProperties) {
this.sender = sender;
this.senderProperties = senderProperties;
}
#Override
public void send() {
if ("bar".equals(senderProperties.get("foo"))) {
log.log(LogService.LOG_DEBUG, "calling method send called on Sender service having properties foo=bar");
}
this.sender.send();
}
}
Now, a simpler solution consists in using a service filter "(foo=bar)" when defining your aspect, and in this case, no need to use callback:
public class Activator extends DependencyActivatorBase{
public void init(BundleContext ctx, DependencyManager dm) throws Exception {
Component logSender = createAspectService(Sender.class, "(foo=bar)", 10)
.setImplementation(LogSender.class)
.add(createServiceDependency().setService(LogService.class).setRequired(true));
dm.add(logSender);
}
}
class LogSender implements Sender {
volatile Sender sender;
#Override
public void send() {
log.log(LogService.LOG_DEBUG, "calling method send called on Sender service having properties foo=bar");
this.sender.send();
}
}
does this help ?
/Pierre

Need to implement Custom Logger Wrapper in log4j2

I need to have an Custom Wrapper around Log4j2. The basic requirement is that. My application should only use My CustomLogger everywhere. instead of Log4j2 logger so in future if needed i can remove 3rd party library like log4j2 etc dependency easily.
How can i do this ??
Log4j2 comes with a tool for generating custom logger wrappers:
See http://logging.apache.org/log4j/2.0/manual/customloglevels.html#CustomLoggers
This tool was intended for use with custom log levels but you can also use it for your purpose. There are a few methods you may want to remove if you want to completely remove all references to the log4j2 api, but it will still save you a lot of work.
The Log interface
First, you requires an interface to be used in each class of application. e.g.:
public interface Log {
boolean isInfoEnabled();
void info(String str);
void info(String str, Throwable t);
}
The wrapper class
Create a class that implements that interface. It's the wrapper for log4j, e.g.:
class Log4jWrapper implements Log {
private static final String FQCN = Log4jWrapper.class.getName();
private ExtendedLoggerWrapper log;
public Log4jWrapper(Class<?> clazz) {
Logger logger = LogManager.getLogger(clazz);
log = new ExtendedLoggerWrapper((ExtendedLogger) logger,
logger.getName(), logger.getMessageFactory());
}
public boolean isInfoEnabled() {
return log.isInfoEnabled();
}
public void info(String str) {
log.logIfEnabled(FQCN, Level.INFO, null, new SimpleMessage(str), null);
}
public void info(String str, Throwable t) {
log.logIfEnabled(FQCN, Level.INFO, null, new SimpleMessage(str), t);
}
}
The LogFactory class.
To create each Log, use a factory. e.g.:
public class LogFactory {
public static Log getLog(Class<?> clazz) {
return new Log4jWrapper(clazz);
}
}
Usage example
Use this factory for each instance of Log into your application. e.g.:
public class Test {
private static final Log LOG = LogFactory.getLog(Test.class);
public static void main(String[] args) {
LOG.info("This is a test... :-)");
}
}

Categories