Random NullPointerExceptions with missing stacktrace in DispatcherServlet - spring

I see random NullPointerException when one of my endpoints is accessed.
I know what an NPE is, but please bear with me, I have many issues here :
problem happens maybe once in every 10000 calls
if I replay the same request a second time it works OK
it looks as if it doesn't get to the code in my controller (no statements are logged)
and, most annoying... I don't have a stacktrace to help me pinpoint the problem.
This is what it is logged :
[p-nio-80-exec-4] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause
java.lang.NullPointerException: null
Annnnnd, that's it, nothing more...
The code of the controller is rather straithforward, it looks like this :
#RepositoryRestController
#ResponseBody
public class AnimalImportEndpoint {
#Inject
private animalImportService;
#RequestMapping(value = "/animals/import", method = RequestMethod.POST)
public AnimalImportResult import(#Valid #RequestBody Animal animal) {
return animalImportService.import(animal);
}
#ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "Illegal arguments")
#ExceptionHandler(IllegalArgumentException.class)
public void illegalArguments() {
}
}
The class is annoted with #RepositoryRestController instead of #RestController because Spring Data Rest also exposes /animals.
Any pointers or advice on how to troubleshoot this ?
Edit : other interesting fact, I use sleuth to trace requests. When I get this NullPointerException, I can see that there is no spanId/traceId in my logs ...

Add this JVM argument to disable stacktrace removal optimization
-XX:-OmitStackTraceInFastThrow

Related

How to check Bad request for #Min on request param in spring controller?

I am pretty new to spring controller. I am trying to write unit test for invalid parameter. I have an api that has #RequestParam("id") #Min(1) long id and in my unit test, I pass in "-1". Here is my test:
#Test
public void searchWithInvalidIbId() throws Exception {
mockMvc.perform(get(BASE_URL)
.param(COLUMN_IB_ID, INVALID_IB_ID_VALUE) // = "-1"
.param(COLUMN_TIME_RANGE, TIME_RANGE_VALUE)
.param(COLUMN_TIME_ZONE, TIME_ZONE_VALUE)
.accept(PowerShareMediaType.PSH_DISPATCH_REPORTER_V1_JSON)
.contentType(PowerShareMediaType.PSH_DISPATCH_REPORTER_V1_JSON))
.andExpect(status().isBadRequest());
}
When I run this, I get
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is javax.validation.ConstraintViolationException: search.arg2: must be greater than or equal to 1
It makes sense, but I am not sure how to test this is BadRequest. I tried #Test(expected = NestedServletException.class), and it passed, but I don't think it is checking what I want to check. What is the right approach to check this?
You can have your custom exception handler annotated with #ControllerAdvice and handle ConstraintViolationException in that class. You can throw your custom exception with additional details if you wish.
Here is an example approach:
#ControllerAdvice
public class MyCustomExceptionHandler {
#ResponseBody
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ExceptionHandler(ConstraintViolationException.class)
ApiError constraintViolationException(ConstraintViolationException e) {
return BAD_REQUEST.apply(e.getBindingResult());
}
}
Here ApiError is a custom class to represent your error response, it can be anything else you want. You can add timestamp, http status, your error message etc.

Spring boot exception handler : throw an exception

Using this works fine
#ResponseStatus(value = HttpStatus.NOT_FOUND)
#ExceptionHandler(value = IoTException.class)
public void IoTError() {
}
But when I try to convert to another homemade exception
#ExceptionHandler(value = IoTException.class)
public void IoTError() {
throw new IoTConnectionException();
}
Exception handler is ignored, i.e. IoTException are sent to the view without being converted to IoTConnectionException. But placing a breakpoint showed me enter the IoTError method.
Any idea why ? Thanks :)
The docs about exception handling state:
If an exception occurs during request mapping or is thrown from a request handler such as an #Controller, the DispatcherServlet delegates to a chain of HandlerExceptionResolver beans to resolve the exception and provide alternative handling, which typically is an error response.
At the point where you are throwing the IoT exception, the delegation to the chain of HandlerExceptionResolver has already been taken place and it will not be executed again. If this exception would trigger another exception handling dispatch it could cause exception cycles. Imagine you would have defined another exception handler method for IoTConnectionException and this would throw IoTException. You would end with a StackOverflowException.
In this section Docs Exception handler methods all supported return values of an exception handler method are described.

Method: Head on /refresh is returning 500

/refresh endpoint is added, exposed and I can call directly , but it is not avaialble via the HTTP Method Head ?
2017-02-21 15:00:08.913 INFO [-,,,] 4597 --- [main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/refresh || /refresh.json],methods=[POST]}" onto public java.lang.Object org.springframework.cloud.endpoint.GenericPostableMvcEndpoint.invoke()
Exception:
org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'HEAD' not supported
at org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.handleNoMatch(RequestMappingInfoHandlerMapping.java:20
I think that this problem only happens when in your micro-service has a #RestController.
As I can see, here you are the difference handles in a micro-service with a #RestController:
2017-02-22 13:54:38.382 **ERROR** [micro-service-1,e9a053f5ffa72714,3980777f97a147f9,true] 10956 --- [http-nio-auto-3-exec-1] c.s.e.c.e.DefaultExceptionHandler
: ErrorResponse [errorCode=90000, description=Request method 'HEAD' not supported, correlationId=e9a053f5ffa72714, externalErrorCode=null, additionalProperties={}]
org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'HEAD' not supported
at org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.handleNoMatch(RequestMappingInfoHandlerMapping.java:207)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lookupHandlerMethod(AbstractHandlerMethodMapping.java:374)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:314)
...
And here you are another one but in this case, has not a #RestController:
2017-02-22 15:14:49.795 **WARN** [micro-service-2,897590499c7c776c,
74a8108edccafb5e,true] 27623 --- [http-nio-auto-2-exec-5]
o.s.web.servlet.PageNotFound : Request method 'HEAD' not supported
I was able to resolve this with the following:
#Component
public class ActuatorHeadEndpoint extends EndpointMvcAdapter {
public ActuatorHeadEndpoint(RefreshEndpoint delegate) {
super(delegate);
}
#RequestMapping(method = RequestMethod.HEAD)
#ResponseBody
#Override
public Object invoke() {
if (!getDelegate().isEnabled()) {
return new ResponseEntity<>(Collections.singletonMap(
"message", "This endpoint is disabled"), HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>("", HttpStatus.METHOD_NOT_ALLOWED);
}
}
fran thanks you are right, that solved the problem its because the class has a #ExceptionHandler(Exception.class) , which handles the unspecified exceptions by returning 500 and considers it as internal server error. explicitly defining the HttpRequestMethodNotSupportedException as #ExceptionHandler in the advice and handle it with the default actuator JSON response (405) which the spring boot admin expects , everything is normal.
This is a bug in Spring Boot Actuator. When the management port is different to server port the management stays without the DefaultHandlerExceptionResolver, which is responsible for returning error 405 when the method is not allowed.
This causes problems in Spring Boot Admin because it expects error 405 in some cases instead of any other else. It uses the HEAD method to verify if the endpoint exists. Call the endpoint with the correct method would execute the code, and the idea is only to verify if it exists.
https://github.com/spring-projects/spring-boot/issues/14084

How can I get HttpServletRequest and HttpServletResponse object in Spring AOP

I want to get the response object in spring AOP before advice. If the session is invalidate I want to redirect to the login page, but unable to get the HttpServletResponse object in the Before advice method.
Tried with the following way.
#Autowired
private HttpServletResponse response;
public void setResponse(HttpServletResponse response) {
this.response = response;
}
Stacktrace:
caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: javax.servlet.http.HttpServletResponse com.****.****.aspect.LogProvider.response; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [javax.servlet.http.HttpServletResponse] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:506)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:284)
... 33 more
Any help will be appreciated.
You can get response by under method:
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
HttpServletResponse response = ((ServletRequestAttributes)requestAttributes).getResponse();
Basically we do redirect from a jsp page i.e. from UI layer we handle such kind of operation(redirection). So, I hope you will be using some restful services in your application. And for most of the restful services we go for Asynchronous request. If it is combination of Asynchronous and restful services; and I am sure you will be using this in your application. If your session is invalid and you try to access perform any operation on 'session' then it will land you in 'IllegalStateException'. For such type of scenario please follow the below centralized 'Exception Handling' mechanism provided by JAX-RS: javax.ws.rs.ext.ExceptionMapper.
Please follow below steps:
step-1: Create a user-defined unchecked exception like MyApplicationException:
public class MyApplicationException extends RuntimeException {
public MyApplicationException() {super();}
// implement other methods of RuntimeException as per your requirement
}
step-2: Create a user-defined type of ExceptionMapper
public class MyApplicationExceptionHandler implements ExceptionMapper<MyApplicationException>
{
#Override
public Response toResponse(MyApplicationException exception)
{
return Response.status(Status.FORBIDDEN).entity(exception.getMessage()).build();
// set any Status code of 4XX as this is client side error not server side
}
}
step-3: In all your ajax request in the UI code check this Status Code and redirect to the login page.
That's it and you are done with a finer implementation. Guaranteed...
/**
* #return the HttpServletResponse handled by the current thread
*/
public static Optional<HttpServletResponse> getThreadLocalResponse() {
return Optional.ofNullable(RequestContextHolder.getRequestAttributes())
.filter(ra -> ra instanceof ServletRequestAttributes)
.map(ServletRequestAttributes.class::cast)
.map(ServletRequestAttributes::getResponse);
}
To get the response object you can use this code:
ServletWebRequest servletWebRequest=new ServletWebRequest(request);
HttpServletResponse response=servletWebRequest.getResponse();
To get the request object:
HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getR‌equest();
If you get a null response then I can see the response is not yet formed when the control is returned. Then the only way ahead is to go with interceptors.

Spring Asynchronous Processing Does not Return To View

I'm using the Spring MVC asyncronous processing but the controller does not return a view on web browser.
#RequestMapping(value = "/generateGM", method = RequestMethod.POST)
public Callable<ModelAndView> generateGMReport(#RequestParam("countryCode") int countryCode, ModelAndView mv) {
Callable<ModelAndView> c = new GenericCallable(countryCode, reportDao, mv);
return c;
}
#Override
public ModelAndView call() throws Exception {
List<CostReport> gmList = reportDao.generateGrossMarginReport(countryCode);
mv.setViewName("gmReport");
mv.addObject("gmList", gmList);
return mv;
}
I had tried to modify the code to return Callable but it still does not return to the specified view name.
I'm using JBoss 7.1 as.
There is warning during deployment :
WARN [org.jboss.as.ee] (MSC service thread 1-7)
JBAS011006: Not installing optional component
org.springframework.web.context.request.async.StandardServletAsyncWebRequest
due to exception: org.jboss.as.server.deployment.DeploymentUnitProcessingException:
JBAS011054:
Could not find default constructor for class
org.springframework.web.context.request.async.StandardServletAsyncWebRequest
Reason: Perhaps sitemesh cannot set the response object from Spring MVC framework (AsynContext).
What is the reason ?
Please help.
Thanks.
Since the Sitemesh filter does some post-processing at the end of a request, it needs to support the Servlet 3 async request feature in order for this to work. When the initial Servlet container thread exits and the response remains open. If the Sitemesh filter is unaware of this, it will attempt to complete processing to early.
I am not an expect on sitemesh. But it's a servlet also so they follow the "chain of command" pattern which means it's possible it fail to transfer the correct url you need. can you post you config for async spring and sitemesh config in web.xml
It may be helpful. Return as a String instead of ModelAndView.

Resources