Reputation: 187
I have a glassfish 5 with jersey 2.27 server application that has a custom response filter that in some cases throws a custom business exception.
The response filter looks like :
public class CustomResponseFilter implements ContainerResponseFilter {
....
@Override
public void filter(ContainerRequestContext request, ContainerResponseContext response) {
....
if (something_is_true) {
throw new CustomException(A_CUSTOM_MESSAGE);
}
}
...
}
The Exception and the Exception Mapper are define like:
public class CustomException extends RuntimeException {
public CustomException(String message) {
super(message);
}
}
@Provider
public class CustomException400Mapper implements ExceptionMapper<CustomException> {
...
@Override
public Response toResponse(CustomException t) {
Error entity = new Error(HttpServletResponse.SC_BAD_REQUEST,
HEADER_INVALID, CONTRACT_NUMBER_HEADER);
return Response.status(HttpServletResponse.SC_BAD_REQUEST).
entity(entity).
type(MediaType.APPLICATION_JSON).
build();
}
}
A request goes through the endpoint and then when it reaches the custom response filter it throws the Custom Exception above.
The Exception is catch-ed and handled by the jersey framework. It is converted into a response using the exception mapper.
My problem is that the response is not returned to the client but instead a processResponse(response)
method is called witch calls all the response filters again. This basically throws the same error again, witch is caught and wrapped into an Internal Server Error.
You can see the jersey framework code bellow with some comments from my side.
package org.glassfish.jersey.server;
...
public class ServerRuntime {
...
public void process(final Throwable throwable) {
final ContainerRequest request = processingContext.request();
...
ContainerResponse response = null;
try {
final Response exceptionResponse = mapException(throwable);
try {
try {
response = convertResponse(exceptionResponse);
if (!runtime.disableLocationHeaderRelativeUriResolution) {
ensureAbsolute(response.getLocation(), response.getHeaders(), request,
runtime.rfc7231LocationHeaderRelativeUriResolution);
}
processingContext.monitoringEventBuilder().setContainerResponse(response)
.setResponseSuccessfullyMapped(true);
} finally {
processingContext.triggerEvent(RequestEvent.Type.EXCEPTION_MAPPING_FINISHED);
}
// response is correctly mapped from CustomException400Mapper
// this triggers the response filters to be runned again
// throws the same Exception again
processResponse(response);
} catch (final Throwable respError) { // the exception is catched here and retrown again
LOGGER.log(Level.SEVERE, LocalizationMessages.ERROR_PROCESSING_RESPONSE_FROM_ALREADY_MAPPED_EXCEPTION());
processingContext.monitoringEventBuilder()
.setException(respError, RequestEvent.ExceptionCause.MAPPED_RESPONSE);
processingContext.triggerEvent(RequestEvent.Type.ON_EXCEPTION);
throw respError;
}
} catch (final Throwable responseError) { // the exception is catched here
if (throwable != responseError
&& !(throwable instanceof MappableException && throwable.getCause() == responseError)) {
LOGGER.log(Level.FINE, LocalizationMessages.ERROR_EXCEPTION_MAPPING_ORIGINAL_EXCEPTION(), throwable);
}
// I`ve tried to use processResponseError method to return the response exception but it calls processResponse(...) method witch call`s the response filters and it fails and returns false.
if (!processResponseError(responseError)) {
// Pass the exception to the container.
LOGGER.log(Level.FINE, LocalizationMessages.ERROR_EXCEPTION_MAPPING_THROWN_TO_CONTAINER(), responseError);
try {
// here it returns the exception mapped into an Internal Server Error Response
request.getResponseWriter().failure(responseError);
} finally {
completionCallbackRunner.onComplete(responseError);
}
}
} finally {
release(response);
}
}
My question is how can I return a response from a custom exception thrown in my response filters ?
Thx!
Upvotes: 0
Views: 571