EHCache and DropWizard: Returning no response - jersey

I have successfully integrated SimplePageCachingFilter with DropWizard.
However, there is one thing standing in my way. Whenever an exception in my application is thrown, instead of being routed to my ExceptionMapper I get "Response contains no data" in my API Browser. I also happen to see this pass by in the log.
WARN [2015-02-12 04:06:21,768] net.sf.ehcache.constructs.web.GenericResponseWrapper: Discarding message because this method is deprecated.
Traditionally my ExceptionMapper returns the appropriate Json Responses.
Has anyone else seen anything similar?

I would put a breakpoint in http://grepcode.com/file/repo1.maven.org/maven2/com.sun.jersey/jersey-server/1.18.1/com/sun/jersey/server/impl/application/WebApplicationImpl.java#WebApplicationImpl on line 1472 then trigger your error.
That's throwing an exception, and the mapping of the exception to the exception mapper happens in the catch a little deeper in the stack. Probably in ContainerResponse.mapException
Hopefully this will give you enough insight into why it's not being called.
FYI. I got to that code by putting a breakpoint in a exception mapper that was being fired, and looking at the call stack.

After attaching a debugger to DropWizard (per #CAB's advice) I discovered that the CachingFilter does not write out a response unless the status code is 200.
I extended CachingFilter and overrode the doFilter method and ignored the status check. This appears to have solved my problem.
#Override
protected void doFilter(final HttpServletRequest request,
final HttpServletResponse response, final FilterChain chain) throws Exception {
if (response.isCommitted()) {
throw new AlreadyCommittedException("Response already committed before doing buildPage.");
}
logRequestHeaders(request);
PageInfo pageInfo = buildPageInfo(request, response, chain);
writeResponse(request, response, pageInfo);
}

Related

How to forcefully end filter-chain in an early servlet/spring boot filter

Having a multi-tenancy application, I want to end any request without a tenant very early.
Setup / Scenario
I run spring boot (web) with spring session and spring security.
In my case, I have several strategies to determine the tenant, TenantFromX, TenantFromY .. all running on order Ordered.HIGHEST_PRECEDENCE - so as early as possible.
On order Ordered.HIGHEST_PRECEDENCE+1 I run the TenantMustExistFilter filter to validate, that any strategy actually did match / find a tenant definition
#Log4j2
#Component
#RequiredArgsConstructor
// After TenantFromX(min) but before SessionRepositoryFilter(Session) which is min+50
#Order(Ordered.HIGHEST_PRECEDENCE + 1)
public class TenantMandatoryFilter extends GenericFilterBean
{
#Override
public void doFilter(
ServletRequest request, ServletResponse response,
FilterChain filterChain
) throws ServletException, IOException
{
if (TenantStore.getInitMode().isEmpty()) {
throw new NoTenantException("No tenant identified for request - request blocked");
}
try {
filterChain.doFilter(request, response);
} finally {
TenantStore.clear();
}
}
}
Question / Problem
Eventhough I throw an Runtime exception NoTenantException all other filters after that filter are called anyway.
For example SessionRepositoryFilter is called on Ordered.HIGHEST_PRECEDENCE+50 and then the spring security FilterProxyChain is triggered.
How do I effectively stop the chain after TenantMandatoryFilter has failed to validate?
I expected either a return; or and Exception or just not calling filterChain.doFilter(request, response); to be just enough to end the chain execution.
Do I have a misunderstanding of the process or the idea here?
Attempts
I can successfully end the chain by replacing
if (TenantStore.getInitMode().isEmpty()) {
throw new NoTenantException("No tenant identified for request - request blocked");
}
with
if (TenantStore.getInitMode().isEmpty()) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return;
}
Interestingly the below will not work, even though it seems semantically more correct. It does not work means, it will call the SessionRepositoryFilter and the FilterChainProxy after that nevertheless.
if (TenantStore.getInitMode().isEmpty()) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid tenant");
return;
}
Which I assume is since sendError sets the response to suspended to complete any other chain (which is what I do not want).
It still does not seem right, so how to do this the right way? Or is this a conceptual flaw altogether?
Sending an error, either via an explicit call or by throwing an exception, will cause the request to be dispatched to the application’s error page handling which will invoke the filters again for that dispatch. Setting the status is the right thing to do.

POST request not getting forwarded from Filter to Controller class

From postman I am hitting a post request like http://localhost:8084/abc/api/v1/xyz having payload and header. we have configured a Filter class extending GenericFilterBean before it hits the Controller. Its executing all the codes of Filter class fine but while executing 'chain.doFilter(request, response);' in the end to forward request to controller method its throwing below exception.In Filter class we are reading the payload and saving in audit table. In Controller class method we have parameters like #RequestBody annotaion, #Context HttpServletRequest, BindingResult.
18:59:25,779 INFO [stdout] (default task-1) 18:59:25,778||WARN |AbstractHandlerExceptionResolver:197|Resolved [org.springframework.http.converter.HttpMessageNotReadableException: I/O error while reading input message; nested exception is java.io.IOException: UT010029: Stream is closed]
Kindly suggest on this.
Is your filter reading the contents of the request? If so then you'll need to look at alternatives to this, as the input stream is not likely to be reusable without some assistance.
Can you post some code from the filter method?
Spring provides at least one mechanism to work around this, which might be helpful here, specifically the ContentCachingRequestWrapper. You can either create a new Filter which wraps the actual request in this wrapper, or you can simply make use of it yourself in your filter.
You might update your filter with something like this
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
HttpServletRequest currentRequest = (HttpServletRequest) servletRequest;
ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(currentRequest);
// todo: your magic code, using wrappedRequest to access the body of the request
// note: passing through the wrapped request,
// which allows later handlers to also access the request
chain.doFilter(wrappedRequest, servletResponse);
}
Note the documentation for ContentCachingRequestWrapper notes
HttpServletRequest wrapper that caches all content read from the input stream and reader, and allows this content to be retrieved via a byte array.
The error that you're receiving indicates you're reading the InputStream of the request, and you should rather simply access the getContentAsByteArray method of the wrapper.

Spring - Changing Order of Global Error handler and Filter

I have a filter like:
#Component
#Order(8)
public class LogReqFilter extends OncePerRequestFilter
{
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException
{...}
}
it basically logs all requests and responses. When there is a 400 error however the response is blank. There is a global exception handler that replaces the body with the custom error:
#RestControllerAdvice
public class GlobalExceptHandler extends ResponseEntityExceptionHandler {
#Override
#ResponseStatus(HttpStatus.BAD_REQUEST)
protected ResponseEntity<Object> handleArgNotValid(MethodArgumentNotValidException ex
, HttpHeaders headers, HttpStatus status, WebRequest request) {...}
}
I noticed that the Global Exception handler is called after the filter and I think that this is why the response is blank in the filter. Is there a way to have the filter called after the Global Exception Handler?
Edited:
TLDR
No You can't change the order as per the OP Title because your GlobalError Handler is always in the layer that holds the ControllerAdvices and that is always between the RequestFilter layer and the Controller.
Long Version
You are somewhat right.
i refer you to this answer: https://stackoverflow.com/a/17716298/405749
expanding on this:
be aware that before and after depends from which you are looking from, ie.
inbound (before it reaches your controller code) OR
outbound (after the controller is done)
now you can plug in different components on this path, e.g. here RequestFilter and Controller Advice
depending on the method you implement, it gets called only on the inbound or outbound track or both, here:
doFilterInternal() is called inbound. Only if you call filterChain.doFilter(request, response); the request continues on the inbound track otherwise it stops here and the response is returned with whatever is already in its output stream or will be added by this filter or filters still be encountered on the way out
this is the call sequence assuming we only have this filter and advice
'inbound'-methods from request filters
'inbound'-methods from controller advice(s)
'outbound'-methods from controller advice(s)
'outbound'-methods from request filters
Now, the handleArgNotValid() only gets called in case such an exception is thrown and potentially adds content to the Response-Output stream. Your example doesnt show if you return an object here or not, but i guess from your OP that you dont. and as a consequence the output stream is empty.
Plz also note, that there is no easy way to dump/look into the output stream unless you wrap it, but that's another topic.

How to handle HttpMediaTypeNotAcceptableException by writing error content to the response body using exception handler annotation?

When a client request for a resource producing application/json content with Accept Header of application/xml. The request fails with HttpMediaTypeNotAcceptableException exception and is wrapped into error message body in the response entity object by using exception handler annotation as mentioned in below code. However, we receive HttpMediaTypeNotAcceptableException again when return values are written to the response with HttpMessageConverter. It is because it checks the producible content type for the response with the acceptable request type, but this is exactly something we are trying to communicate to the client using error message. How do I workaround this issue ? Btw, all the other exceptions are parsing fine to error message. Please advise.
#ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body,
HttpHeaders headers, HttpStatus status, WebRequest request) {
// Setting the response content type to json
headers.setContentType(MediaType.APPLICATION_JSON);
return ResponseEntity.status(status).headers(headers).body(body);
}
}
A few options come to my mind. One is that your controller method produces all content types and then you throw an exception in your method if the content type is not the one you are expecting, then the exception handler can take this exception and transform it. This is the only one that works with exception handlers, as exception handlers only deal with exceptions produced in the controller method.
The other options are:
Use an interceptor (but I'm not sure if this will work, as Spring might try to resolve first the controller method rather than invoking the interceptors).
Extend RequestMappingHandlerMapping to call the exception handler if it doesn't find a suitable method. You'll probably need to override the method handleNoMatch. In there you'll need to get a reference to the list of HandlerExceptionResolver
The first one is the simplest to understand, and the latest one might be the most 'extensible', but it also requires some understanding of the internals of Spring.
Resolved by setting different content negotiation strategy FixedContentNegotiationStrategy for ExceptionHandlerExceptionResolver and HeaderContentNegotiationStrategy for RequestResponseBodyMethodProcessor.
I have been using a serialized enum-based response (enum annotated with jackson #JsonFormat(shape = Shape.OBJECT) to standardize the error messages in my exception handler class and faced the same issue when it caught with a HttpMediaTypeNotAcceptableException.
The workaround is to set the media type you expect to return directly to the builder method available in the ResponseEntity.
The below code works fine for me.
#ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
public final ResponseEntity<ResponseMessagesEnum> handleHttpMediaTypeNotAcceptableException(
HttpMediaTypeNotAcceptableException e, HttpServletRequest request) {
logger.error("No acceptable representation found for [{}] | supported {}", request.getHeader("Accept"), e.getSupportedMediaTypes());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).contentType(MediaType.APPLICATION_JSON)
.body(ResponseMessagesEnum.EX_001);
}

Spring #Validated Causing HTTP 400

When I add the #validate annotaion to a method signature in my controller, I get a HTTP 400 with the description, "The request sent by the client was syntactically incorrect ().". Does anyone have an idea why?
Good Code
public ModelAndView startAccountSubmittal(
AccountCredential accountCred, HttpServletResponse response)
throws MessagingException {
Bad Code
public ModelAndView startAccountSubmittal(
#Validated(value={AccountStates.Submitted.class})
AccountCredential accountCred,
HttpServletResponse response, BindingResult result)
throws MessagingException {
Thanks in advance,
Joe
The uncaught exception I had configured was not catching the error. I added an exception handler to the controller and I was able to get the details. I had a few extra fields it was trying to validate, which contained nulls, but required values. This what caused the HTTP 400. Turning on the exception handler helped get the exception and stacktrace. I modified the code and added extra groups, then added those new groups to the object and controller. It works as planned.

Resources