I had written an aspect as part of an application using Spring AOP/AspectJ annotations similar to below aspect:
#Aspect
#Component
public class LoggingAspect {
#Around("#annotation(loggable)")
public Object log(final ProceedingJoinPoint joinPoint, final Loggable loggable) throws Throwable {
//log method arguments
try {
Object returnValue = joinPoint.proceed();
// log return value
return returnValue;
} catch (Exception ex) {
// publish exception metrics to some other system
throw ex;
}
}
}
Now I want to use this same aspect in another project, but this project uses Guice instead of Spring.
I was reading about Guice AOP which requires aspect to implement the MethodInterceptor interface and thus I will need to implement the below method:
Object invoke(MethodInvocation methodInvocation) throws Throwable;
What I was thinking was to modify the already existent aspect to implement the MethodInterceptor and internally call the log method. Something like below:
#Aspect
#Component
public class LoggingAspect implements MethodInterceptor {
#Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
// call already defined log method, but that method expects a ProceedingJoinPoint, however
// I get MethodInvocation as input parameter in this method
}
// already defined log method
#Around("#annotation(loggable)")
public Object log(final ProceedingJoinPoint joinPoint, final Loggable loggable) throws Throwable {
......
.....
}
But due to incompatible type between two methods, I am unable to proceed.
Is there a way I can reuse the existing code instead of writing a brand new aspect with duplicate code to support Guice?
If I understand correctly, you want to reverse the control flow, which can be done with callbacks.
#Aspect
#Component
class LoggingAspect implements MethodInterceptor {
#Around("#annotation(loggable)")
public Object log(final ProceedingJoinPoint joinPoint, final Loggable loggable) throws Throwable {
return log(joinPoint::getArgs, () -> joinPoint.proceed(joinPoint.getArgs()));
}
#Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
return log(methodInvocation::getArguments, methodInvocation::proceed);
}
public Object log(Supplier<Object[]> arguments, Supplier<Object[]> proceed) {
Object[] args = arguments.get();
//log method arguments
try {
Object returnValue = proceed.get();
// log return value
return returnValue;
} catch (Exception ex) {
// publish exception metrics to some other system
throw ex;
}
}
}
BTW do you intentionally catch only Exception and not Throwable? Errors would not be logged.
Related
I have this aspect that is called on every method annotated with #LogActivity annotation.
#Aspect
#Component
public class EndpointAspect {
#Around("#annotation(LogActivity)")
public Object logActivity(ProceedingJoinPoint joinPoint) throws Throwable {
Object result = joinPoint.proceed();
// I want to access message here. see below
return result;
}
}
And this is one annotated method:
public class contrlr {
#LogActivity
#GetRequest("/home")
public String aMethod() {
String message = "some RUNTIME-GENERATED message"; // I want to send it to aspect
return "home.html"
}
}
I want to pass the message to aspect. note that message may not be hard-coded constant. How to do that?
Trying to handle the global exception handling in Micronaut, but the exception stack trace and cause are not thrown to the ExceptionHandler.
public class GlobalException extends RuntimeException{
public GlobalException(Throwable throwable){}
}
#Produces
#Singleton
#Requires(classes = {GlobalException.class, ExceptionHandler.class})
public class GlobalExceptionHandler implements ExceptionHandler<GlobalException, HttpResponse> {
private static final Logger LOG = LoggerFactory.getLogger(GlobalExceptionHandler.class);
#Override
public HttpResponse handle(HttpRequest request, GlobalException exception) {
LOG.error(exception.getLocalizedMessage());
LOG.error(exception.getCause().getCause().getMessage());
Arrays.stream(exception.getStackTrace()).forEach(item -> LOG.error(item.toString()));
return HttpResponse.serverError(exception.getLocalizedMessage());
}
}
Controller
public Maybe<FindProductCommand> get(ProductSearchCriteriaCommand searchCriteria) {
LOG.info("Controller --> Finding all the products");
return iProductManager.find(searchCriteria)
.onErrorResumeNext(error -> { return Maybe.error(new GlobalException(error));});
}
The actual error are not mapped in GlobalExceptionHandler. exception.getLocalizedMessage() is null and LOG.error(exception.getCause().getCause().getMessage()) is throwing null pointer exception
GlobalException's constructor has a Throwable parameter and it's swallowing it (not doing anything with it). RuntimeException also has a single argument constructor that takes a Throwable, so GlobalException(Throwable throwable) is effectively hiding RuntimeException(Throwable throwable).
Thus when your controller gets to:
return Maybe.error(new GlobalException(error));
error is being swallowed
exception.getLocalizedMessage() returns null since the RuntimeException(Throwable throwable) constructor can't propogate throwable to Exception(Throwable throwable) (RuntimeException extends Exception) and GlobalException doesn't override Exception#getLocalizedMessage()
LOG.error(exception.getCause().getCause().getMessage()) is throwing
a NullPointerException because exception.getCause() is returning null due to error being swallowed (from list item 1)
In conclusion, either don't hide RuntimeException(Throwable throwable) in GlobalException, via:
public class GlobalException extends RuntimeException {}
Or just invoke RuntimeException(Throwable throwable) from GlobalException(Throwable throwable), via:
public class GlobalException extends RuntimeException {
public GlobalException(Throwable throwable) {
super(throwable);
}
}
I have a service class with #Async method and If it's calling method throwing any exception then the #ControllerAdvice will not call for global exception handling. But for other classes and services it will call advice and sending email properly.
#Service
public class FileScanServiceImpl implements FileScanService {
#Override
#Async
public void scanFileScheduler() throws MQException {
try{
messageProducer.putFileNameToMQ(fileName);
} catch (Exception e) {
ExceptionUtility.handleException(e, currentFile);
}
}
The ExceptionUtility is used for checking instance on exception and doing some functionality there and throwing custom exception.
public static void handleException(Exception e throws MQException {
String errMsg = "";
if (e instanceof MQException) {
// some functionality
throw new MQException(subject, errMsg);
}
}
And this is my #ControlleAdvice
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler(MQException.class)
#ResponseBody
public void handleMQException(HttpServletRequest request, MQException ex) {
// send email
}
}
It there any solution for #Async which will call #ControllerAdvice for global exception, also the existing functionality will not break.
#ExceptionHandler was created to catch only "synchronous exceptions". If it had the ability to catch exceptions from asynchronous threads, then when several threads start and if any of them fail, the request to the server would be interrupted completely and the system could remain in an inconsistent state (due to many other active threads generated by this request)
For handling asynchronous exceptions Spring has the AsyncUncaughtExceptionHandler interface:
public class YourAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
#Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
// Your exception handling logic
}
}
More information can be found here in the Exceptions section: https://www.baeldung.com/spring-async
I have an interface:
public interface ThirdPartySystemCaller {
void sendRequest(String request) throws ThirdPartySystemException;
}
And implementation:
#Slf4j
#Service
public class ThirdPartySystemCallerImpl implements ThirdPartySystemCaller {
#Override
public void sendRequest(String request) throws ThirdPartySystemException {
if (request == null) throw new ThirdPartySystemException();
log.info("send: {}", request);
}
}
And I have a CryptoService witch can sign request:
public interface CryptoService {
String signRequest(String request) throws CryptoException;
}
And It implementation:
#Slf4j
#Service
public class CryptoServiceImpl implements CryptoService {
#Override
public String signRequest(String request) throws CryptoException {
if (request.length() > 100) throw new CryptoException(); //just for example
return "signed " + request;
}
}
Now, I can use these services:
String signedRequest = cryptoService.signRequest("Hello");
thirdPartySystemCaller.sendRequest(signedRequest);
But I need to call both services each time. I want to create Proxy:
#Slf4j
#Service
public class ThirdPartySystemCallerSignedProxy implements ThirdPartySystemCaller {
private final ThirdPartySystemCaller thirdPartySystemCaller;
private final CryptoService cryptoService;
public ThirdPartySystemCallerSignedProxy(ThirdPartySystemCaller thirdPartySystemCaller, CryptoService cryptoService) {
this.thirdPartySystemCaller = thirdPartySystemCaller;
this.cryptoService = cryptoService;
}
#Override
public void sendRequest(String request) throws ThirdPartySystemException {
String signedRequest = cryptoService.signRequest(request);
thirdPartySystemCaller.sendRequest(signedRequest);
}
}
But my ThirdPartySystemCallerSignedProxy implement ThirdPartySystemCaller interface and sendRequest method throw only ThirdPartySystemException. But if cryptoService throw CryptoException I need throw it too.
How can I do it?
I was thinking to make unchecked exceptions, But I need to be checked.
Create base exception
You can create abstract exception BusinessException which can be a base exception for ThirdPartySystemException and CryptoException. Now, you can define that sendRequest method throws BusinessException and real exception depends from given problem.
Facade
ThirdPartySystemCallerSignedProxy is a bad name because it reminds Proxy pattern which is not what you have implemented. This class reminds Facade pattern because you want to create unified interface with simpler API for two different interfaces. In that case you can wrap CryptoException if it will be thrown into ThirdPartySystemException or also create base exception and declare it in method. It is even better because you do not know which kind of exception will be thrown but for sure it will be BusinessException.
Chain of Responsibility
Many libraries use Chain of Responsibility to handle request -> response communication. All chain cells need to implement the same interface with base exception in declaration if needed. You can build the chain in bean definition. It is a little bit easier to maintain because all cells are independent and does not have to know about each other as in Facade. You can build chain in #Bean method declaration like below:
#Bean
public ServiceChainCell thirdPartyCaller() {
CryptoService crypto = cryptoService();
ThirdPartySystemCaller caller = thirdPartySystemCaller();
// Create chain
crypto.setNext(caller);
return crypto;
}
setNext method comes from ServiceChainCell interface which also should have sendRequest(String request) method.
Read more about these patterns and you will find the best solution for you.
I'm trying to enrich the SLF4J MDC on each request with the user's ID. The problem is that the ID can be passed in many ways, sometimes as a path parameter, sometimes in the body, and sometimes injected by a custom ValueFactoryProvider that first decrypts it.
If I could somehow access all the injected (i.e. already deserialized) parameter values, I could handle all these cases easily.
E.g.
For a resource such as:
#GET
//#Encrypted params are injected by a custom ValueFactoryProvider
public Something getSomething(#Encrypted("userId") String userId) {
return ...;
}
#POST
public Something getSomething(#RequestBody RequestWithUserId requestWithUserId) {
return ...;
}
I could have a filter such as:
public class MdcFilter implements ContainerRequestFilter, ContainerResponseFilter {
#Context
private ResourceInfo resourceInfo;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
Method theMethod = resourceInfo.getResourceMethod();
for (Parameter parameter : theMethod.getParameters()) {
//Deal with the #Encrypted case
if (parameter.isAnnotationPresent(Encrypted.class) && parameter.getAnnotation(Encrypted.class).value().equals("userId")) {
MDC.put("userId", somehowGetTheValue());
}
//Deal with the #RequestBody case
if (parameter.isAnnotationPresent(RequestBody.class) && parameter.getType().equals(RequestWithUserId.class)) {
MDC.put("userId", ((RequestWithUserId)somehowGetTheValue()).getUserId());
}
... //other possibilities
}
}
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
MDC.clear();
}
}
But I don't see a way to implement somehowGetTheValue either from a ContainerRequestFilter an interceptor or anything else...
Jersey uses HK2 under the hood for dependency injection. And HK2 has AOP support. One option for your use case would be use this AOP support. All you need to do is implement a MethodInterceptor and an InterceptionService. In the MethodInterceptor, you can get all the arguments from the MethodInvocation and you can get parameter annotation from the Method
class MyMethodInteceptor implements MethodInterceptor {
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
Object[] args = invocation.getArguments();
// do your logging or whatever with the args.
// invoke method and get return value.
Object returnValue = invocation.proceed();
// if you want to do something with the return
// value before returning it, you can.
return returnValue;
}
}
To use the interceptor, you configure the InterceptionService.
public class MyInterceptionService implements InterceptionService {
private final static MethodInterceptor METHOD_INTERCEPTOR
= new MyMethodInterceptor();
private final static List<MethodInterceptor> METHOD_LIST
= Collections.singletonList(METHOD_INTERCEPTOR);
#Override
public Filter getDescriptorFilter() {
return BuilderHelper.allFilter();
}
#Override
public List<MethodInterceptor> getMethodInterceptors(Method method) {
// you implement shouldIntercept
if (shouldIntercept(method)) {
return METHOD_LIST;
}
return null;
}
#Override
public List<ConstructorInterceptor> getConstructorInterceptors(Constructor<?> constructor) {
return null;
}
}
You determine which method should be intercepted in the getMethodInterceptors() method. If the method should be intercepted, then return a list of interceptors, otherwise return null. A common way of handling this is to create a custom annotation and just annotate the method. The in the above method, just check
if (method.isAnnotationPresent(YourAnno.class)) {
return METHOD_LIST;
}
To make it all work, you just need to register the InteceptionService with HK2. You can do that in an AbstractBinder, which is what is used in a Jersey app to configure your DI.
ResourceConfig config = new ResourceConfig();
config.register(new AbstractBinder() {
#Override
protected void configure() {
bind(MyInterceptionService.class)
.to(InterceptionService.class)
.in(Singleton.class);
}
});
You can see a complete example in this GitHub repo. There is also an official example in the HK2 site. Just see "AOP support" the link at the top of the post.
You can get it like this
StringWriter stringWriter = new StringWriter();
IOUtils.copy(new InputStreamReader(requestContext.getEntityStream()), stringWriter);
System.out.println(stringWriter.toString());// String representation of the payload
requestContext.setEntityInputStream(new ByteArrayInputStream(requestEntity));
Basically the idea is to copy the stream and do any processing and then set the stream back. Because if you don't do that, then in your controller method you would get null, becuase the stream was already read.