Spring #Async error handling with database access - spring

I'm able to intercept Async exceptions using the following class.
I need to register exceptions in a database,is there a way to use autowiring in this class ? It seems not to support it.
(Tried #Controller and #Service, does not work)
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
#Autowired
private IDBEventService dbEventService;
#Override
public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
System.out.println("Exception message - " + throwable.getMessage());
System.out.println("Method name - " + method.getName());
for (Object param : obj) {
System.out.println("Parameter value - " + param);
}
dbEventService.recordEvent("Something happened");
}
}

A standard way to achieve what you are trying to do would be to log the exception and configure a database appender for the logger.

Related

Returning proper value from #AfterThrowing

I am new to String, SpringBoot.
Can we suppress thrown exception in a method annotated with #AfterThrowing?
I mean when an exception is thrown, it will suppress that and will return a default value on behalf of the invoking method?
Say, I have a controller -
#RestController
public class MyRestController implements IRestController{
#Override
#GetMapping("hello-throw")
public String mustThrowException(#RequestParam(value = "name")final String name) throws RuntimeException {
System.out.println("---> mustThrowException");
if("Bakasur".equals(name)) {
throw new RuntimeException("You are not welcome here!");
}
return name + " : Welcome to the club!!!";
}
}
I have created a #AspectJ, as follows -
#Aspect
#Component
public class MyAspect {
#Pointcut("execution(* com.crsardar.handson.java.springboot.controller.IRestController.*(..))")
public void executionPointcut(){
}
#AfterThrowing(pointcut="executionPointcut()",
throwing="th")
public String afterThrowing(JoinPoint joinPoint, Throwable th){
System.out.println("\n\n\tMyAspect : afterThrowing \n\n");
return "Exception handeled on behalf of you!";
}
}
If I run this & hit a ULR like - http://localhost:8080/hello-throw?name=Bakasur
I will get RuntimeException, but, I want to return a default message like - Exception handeled on behalf of you!, can we do it using #AfterThrowing?
I know it can be done using #Around, but around will be called on every hit of the url, that I do not want
What you want to do is Exception Handling on the controller. You don't need to build it yourself, Spring already supports you with some annotations like #ExceptionHandler and #ControllerAdvice. Best would be to follow this example: https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc#using-controlleradvice-classes
#ControllerAdvice
class GlobalControllerExceptionHandler {
#ResponseStatus(HttpStatus.CONFLICT) // 409
#ExceptionHandler(DataIntegrityViolationException.class)
public void handleConflict() {
// Nothing to do
}
}
#ControllerAdvice
class GlobalDefaultExceptionHandler {
public static final String DEFAULT_ERROR_VIEW = "error";
#ExceptionHandler(value = Exception.class)
public ModelAndView
defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
// If the exception is annotated with #ResponseStatus rethrow it and let
// the framework handle it - like the OrderNotFoundException example
// at the start of this post.
// AnnotationUtils is a Spring Framework utility class.
if (AnnotationUtils.findAnnotation
(e.getClass(), ResponseStatus.class) != null)
throw e;
// Otherwise setup and send the user to a default error-view.
ModelAndView mav = new ModelAndView();
mav.addObject("exception", e);
mav.addObject("url", req.getRequestURL());
mav.setViewName(DEFAULT_ERROR_VIEW);
return mav;
}
}
You should use the fully qualified name of the class before method's name when you're referring to a pointcut. So, you should change #AfterThrowing something like this.
#AfterThrowing(pointcut="packageName.MyAspect.executionPointcut()",
throwing="th")
Please note that packageName is full package name of MyAspect.

Spring Boot Exception(Error) Handling for RESTful Services

I have the following RESTful Services method :
#PostMapping("/ajouterNewField")
public String ajouterField(#Valid #ModelAttribute("field") Fields field, Model model) throws IOException {
fieldDao.save(field);
// SOME CODE
return displayListeChamps( model);
}
The method is working fine and my question is how to handle any error (database not connected ...) or every issue that can happen durring the execution of this RESTful Services method.
You can use #ControllerAdvice
Refer to the code below
#ControllerAdvice
public String NyExceptionHandlerAdvice {
private final Logger logger = ...;
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler({MyRunTimeException.class})
public void handleMyRunTimeException(Exception e) {
logger.error("Exception : ", e);
}
return MY_ERROR_STRING;
}
Best Practice is:
You can have your code throw RunTimeExceptions and handle all of them together or separately in handler methods similar to handleMyRunTimeException above.
You can decide what status code your request should return upon exception.
Basically you'll have to a sort of exception handler for any kind of exception your method might throw:
public class FooController{
// ...
#ExceptionHandler({ CustomException1.class, CustomException2.class })
public void handleException() {
//
}
}
Here's a nice article about that: https://www.baeldung.com/exception-handling-for-rest-with-spring

#Async - In Spring independent thread closing before completing task

I have a requirement where I do some operation on GUI and once I save the data in DB I need to send a http request to a webservice. But the response to GUI should not wait for result from webservice request.
For this I am using #Async , annotation of Spring.
here is my structure
MyConroller.java
calls a method
goSaveAndCreate
(not Async) in
ServiceA.java
ServiceA has a ServiceB bean injected in it. A method ,
#Async
create()
in ServiceB is annotated with Async.
Now ServiceA.goSaveAndCreate calls a method in itself , save and calls ServiceB.create() (which is Async).
I can see in logs the a new thread is created which is executing create method. But all of a sudden logs after a particular point stop and that thread seems to have got killed or comlpeted.
#Service("MarginCalculationService")
public class ServiceA implements ServiceAI {
private static final String APPROVED = "APPROVED";
public static final String SUCCESS = "SUCCESS";
....
#Autowired
ServiceB serviceB;
public List<VV> goSaveAndCreate(String[] ids,List<XX> calList) throws Exception, DataAccessException {
try {
Pair<List<VG>,List<UU>> resultList = save(ids);
vvList = resultList.getFirst();
/*
* HKAPIL - Send Message to webService callingserviceB
*/
if(resultList.getSecond() != null){
serviceB.create(resultList.getSecond());
}
} catch (DataAccessException e) {
e.printStackTrace();
logger.error("Data Access Exception thrown during - " , e);
throw e;
} catch (Exception e){
e.printStackTrace();
logger.error("Exception thrown during " , e);
throw e;
}
System.out.println("Exiting the method in MCSERVICE");
return vvList;
}
private save(){
...
...
}
}
Second service
#Service("wireInstructionMessageService")
public class ServiceB implements ServiceBI {
#Async
#Override
public void create(List<Ralc> calcList) {
String threadName = Thread.currentThread().getName();
logger.info("Inside a different thread [" + threadName + " ] to send message ." );
..
...
otherMethod(Obj);
}
private otherMethod(Obj obj){
...
...
..
//tills this point logs are getting printed nothing after this
..
...
}
}
applciationContext.xml entry
<!-- Defines a ThreadPoolTaskExecutor instance with configurable pool size, queue-capacity, keep-alive,
and rejection-policy values. The id becomes the default thread name prefix -->
<task:executor id="MyMessageExecutor"
pool-size="5-25"
queue-capacity="100"/>
<task:annotation-driven executor="MyMessageExecutor"/>
Now I have two question
1) is there a way I can add some logs in some method which tell taht the new thread from MyExecutor is getting killed or MyExecutor is getting closed (the way we have in normal Java ExecutorSerrvice)
2) Am I using the Asyn in wrong way? Is it possible that as soon as method returns from Controller or ServiceA , ServiceB instance also is getting cleaned?
Thanks
HKapil

Map UnsupportedMediaTypeException using ExceptionMapper

Is there a place where it is clearly documented that I cannot map UnsupportedMediaTypeException (because it's a rest easy exception and not custom application exception) using the javax.ws.rs.ext.ExceptionMapper?
I want to prove that to my client. Or another thing I would like to do is map this exception to a Response that can be fetched at the client to show the error. Right now when this exception is thrown it provides no information to the client as the application ends abruptly.
Any help would be appreciated.
Thanks
You can map this exception. Why not? Do you get an error?
This code should do the job
#Provider
public class EJBExceptionMapper implements ExceptionMapper<org.jboss.resteasy.spi.UnsupportedMediaTypeException>{
Response toResponse(org.jboss.resteasy.spi.UnsupportedMediaTypeException exception) {
return Response.status(415).build();
}
}
Don't forget to declare that provider in Spring configuration file.
If you want to provide more information to the client create class
#XmlRootElement
public class Error{
private String message;
//getter and setter for message field
}
and then you can
#Provider
public class EJBExceptionMapper implements ExceptionMapper<org.jboss.resteasy.spi.UnsupportedMediaTypeException>{
Response toResponse(org.jboss.resteasy.spi.UnsupportedMediaTypeException exception) {
Error error = new Error();
error.setMessage("Whatever message you want to send to user");
return Response.entity(error).status(415).build();
}
}
If you don't want to use Error entity simply pass a string to Response.entity() call.
If you want to catch whatever is thrown in you application create generic exception mapper:
#Provider
public class ThrowableMapper implements ExceptionMapper<Throwable> {
public Response toResponse(Throwable t) {
ErrorDTO errorDTO = new ErrorDTO(code);
return Response.status(500).build();
}
}

Spring Remoting HTTP invoker - exception handling

I'm using Spring's 'HTTP Invoker' remoting solution to expose DAOs to many different applications, but have all database access in a single server.
This works well, but if the server throws, say, a HibernateSystemException, Spring serializes that and sends it over the wire back to the client. That doesn't work because the client doesn't (and shouldn't) have HibernateSystemException in its classpath.
Might there be a way to have Spring Remoting wrap my exception in something that I specify that would be common between client and server to avoid issues like this?
I know that I could do that in my server code by wrapping everything the DAO does in a try/catch, but that's admittedly sloppy.
Thanks,
Roy
I ran into this issue as well; I am exposing a service via HTTP Invoker that accesses a database using Spring 3.1, JPA 2, and Hibernate as the JPA provider.
To work around the problem, I wrote a custom Interceptor and an exception called WrappedException. The interceptor catches exceptions thrown by the service and converts the exceptions and causes to WrappedException using reflection and setters. Assuming the client has the WrappedException on its class path, the stack trace and original exception class names are visible to the client.
This relaxes the need for the client to have Spring DAO on its class path and as far as I can tell, no original stack trace information is lost in the translation.
Interceptor
public class ServiceExceptionTranslatorInterceptor implements MethodInterceptor, Serializable {
private static final long serialVersionUID = 1L;
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
try {
return invocation.proceed();
} catch (Throwable e) {
throw translateException(e);
}
}
static RuntimeException translateException(Throwable e) {
WrappedException serviceException = new WrappedException();
try {
serviceException.setStackTrace(e.getStackTrace());
serviceException.setMessage(e.getClass().getName() +
": " + e.getMessage());
getField(Throwable.class, "detailMessage").set(serviceException,
e.getMessage());
Throwable cause = e.getCause();
if (cause != null) {
getField(Throwable.class, "cause").set(serviceException,
translateException(cause));
}
} catch (IllegalArgumentException e1) {
// Should never happen, ServiceException is an instance of Throwable
} catch (IllegalAccessException e2) {
// Should never happen, we've set the fields to accessible
} catch (NoSuchFieldException e3) {
// Should never happen, we know 'detailMessage' and 'cause' are
// valid fields
}
return serviceException;
}
static Field getField(Class<?> clazz, String fieldName) throws NoSuchFieldException {
Field f = clazz.getDeclaredField(fieldName);
if (!f.isAccessible()) {
f.setAccessible(true);
}
return f;
}
}
Exception
public class WrappedException extends RuntimeException {
private static final long serialVersionUID = 1L;
private String message = null;
public void setMessage(String message) {
this.message = message;
}
#Override
public String toString() {
return message;
}
}
Bean Wiring
<bean id="exceptionTranslatorInterceptor" class="com.YOURCOMPANY.interceptor.ServiceExceptionTranslatorInterceptor"/>
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="YOUR_SERVICE" />
<property name="order" value="1" />
<property name="interceptorNames">
<list>
<value>exceptionTranslatorInterceptor</value>
</list>
</property>
</bean>
I can understand you don't want your clients to have HibernateSystemException in their classpath, but I'd argue they should, if you're using HTTPInvoker appropriately. It's not designed to be a service facade / interface layer: all it's meant to do is let you run Java methods on a remote JVM, using HTTP instead of RMI.
So if you really don't want the clients to have a dependency on Hibernate, your try/catch block is the way to go. (I'd argue against that though, since it'll make debugging a pain: your stack trace will now be divided between the client and the server).
I haven't used it myself but you could try the org.springframework.remoting.support.RemoteExporter.setInterceptors(Object[]) method to add an aspect to catch that particular exception in just one place rather than adding try/catches all over the place.
I would argue a try/catch in a Facade layer in front of your DAOs is exactly what you want, in order to gain full control over the exceptions you return. I agree it initially feels ugly, but it's a vital layer between client and DAO, in my opinion.
You might even return a OperationStatus object of some sort, rather than use void return types, to convey both outcome (worked, didn't) and error message, for store-data API calls.
I used a solution similar to N1H4L's but with AspectJ.
First I made all the exceptions I want the client to be aware of to extend the class BusinessException (which in my case is a very simple subclass of RuntimeException in the jar with the service interface and DTOs).
Since I don't want the client to know much about the internals of the service I just say "Internal server error".
package com.myproduct.myservicepackage;
import com.myproduct.BusinessException;
import org.aspectj.lang.*;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class InternalServerErrorExceptionAspect {
#Pointcut("execution(public * com.myproduct.myservicepackage..*Service.*(..))")
public void publicServiceMethod() {}
#Around("publicServiceMethod()")
public Object hideNonBusinessExceptions(ProceedingJoinPoint jp) throws Throwable {
try {
return jp.proceed();
} catch (BusinessException e) {
throw e;
} catch (RuntimeException e) {
e.printStackTrace();
throw new RuntimeException("Internal server error.")
}
}
}
Here's the BusinessException class:
package com.myproduct.BusinessException;
public class BusinessException extends RuntimeException {
private static final long serialVersionUID = 8644864737766737258L;
public BusinessException(String msg) {
super(msg);
}
}
I was using AspectJ to wrap exception but it does not work for exception that occur in Spring proxy, e.g. annotation #Transactional when the connection to the database fails.
However, the method setInterceptor on RmiServiceExporter works perfectly.

Resources