Central error handling on Jersey JAX-RS - filter

I am doing jaxrs with jersey. I am experience with cxf but not so much with Jersey. Is there a way to set an out-interceptor equivalent to handle exception throws by the resource methods in Jersey?

In case you play with Jersey 2.x, you can use ExceptionMapper to handle all exceptions (or particular one) produced by resource methods.
#Provider
public class MyExceptionMapper implements ExceptionMapper<java.lang.Exception> {
public Response toResponse(java.lang.Exception ex) {
return Response.status(500)
.entity(ex.getMessage())
.type("text/plain")
.build();
}
}

Related

Spring Reactive - Exception Handling for Method Not Allowed Exception not triggering post Spring 3.0.0 & Java 17 upgrade

We recently upgraded our Spring Reactive APIs that were running on Java 11 and Spring 2.7.x. Exceptions in the Controller layer are handled by a Global Exception Handler which also handled the Method Not Supported exception. Post the upgrade, we are getting internal server error instead of Method not allowed exception when we try a different HTTP verb other that the one that a specific endpoint is designated to.
Our application has both of the below dependencies:
spring-boot-starter-web
spring-boot-starter-webflux
Searched for some stack overflow links and tried adding the below piece of code but didn't help either.
#Component
#Order(-2)
public class RestWebExceptionHandler implements ErrorWebExceptionHandler {
#Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
if (ex instanceof HttpRequestMethodNotSupportedException) {
exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
// marks the response as complete and forbids writing to it
return exchange.getResponse().setComplete();
}
return Mono.error(ex);
}
#ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public ResponseEntity<PlanResponse> handleHttpRequestMethodNotSupportedException(
final HttpRequestMethodNotSupportedException exception) {
return responseBuilderRegistry.getResponseBuilderByType(HttpRequestMethodNotSupportedResponseBuilder.class)
.buildResponse(exception);
That was a common issue that was "recently" addressed on Spring MVC and Spring Webflux.
If you are interested in it, this issue was discussed here https://github.com/spring-projects/spring-framework/issues/22991
Just created a project to test this, can you please try the following
#RestControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler(MethodNotAllowedException.class)
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public Mono<Void> handle(Exception e, ServerWebExchange exchange) {
return exchange.getResponse().setComplete();
}
}

Spring AOP Advice for ResponseEntityExceptionHandler.handleException

I am new at Spring AOP. I try to write advice for ResponseEntityExceptionHandler.handleException method to logging exception info. After hours of searching for solutions, I'm stuck.
This is my Apect component
#Log4j2
#Aspect
#Component
public class LogginAspect {
#Pointcut(value = "execution(* org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler.handleException(..)) && args(ex, request)")
public void callSpringExceptionHandler() {}
#Before("callSpringExceptionHandler()")
public void logBeforeError(JoinPoint joinPoint, Exception ex, WebRequest request) {
log.error(ex.getMessage(), ex);
}
}
I have tried different patterns of pointcut but with no luck.
My advice logBeforeError does not ever called at all. Please help me with my problem
From the Spring documentation :5.8. Proxying Mechanisms
If the target object to be proxied implements at least one interface,
a JDK dynamic proxy is used. All of the interfaces implemented by the
target type are proxied. If the target object does not implement any
interfaces, a CGLIB proxy is created.
and
With CGLIB, final methods cannot be advised, as they cannot be
overridden in runtime-generated subclasses.
ResponseEntityExceptionHandler is an abstract class which does not implement any interface and ResponseEntityExceptionHandler.handleException() is a final method. In short Spring AOP will not be able to advice that method execution.
You will be able to achieve advicing a final method using AspectJ though. Please go through the detailed answer from #kriegaex

How implement Error decoder for multiple feign clients

I have multiple feign clients in a Spring Boot application. I am using a Controller Advice for handling custom exceptions for each feign client.
Here my controller advice that handles two custom exceptions (one for each client: client1 and client2):
#ControllerAdvice
public class ExceptionTranslator implements ProblemHandling {
#ExceptionHandler
public ResponseEntity<Problem> handleCustomClient1Exception(CustomException1 ex, NativeWebRequest request) {
Problem problem = Problem.builder()
.title(ex.getTitle())
.detail(ex.getMessage())
.status(ex.getStatusType())
.code(ex.getCode())
.build();
return create(ex, problem, request);
}
#ExceptionHandler
public ResponseEntity<Problem> handleCustomClient2Exception(CustomException2 ex, NativeWebRequest request) {
Problem problem = Problem.builder()
.title(ex.getTitle())
.detail(ex.getMessage())
.status(ex.getStatusType())
.code(ex.getCode())
.build();
return create(ex, problem, request);
}
}
I have implemented an error decoder for feign client1.
public class ClientErrorDecoder implements ErrorDecoder {
final ObjectMapper mapper;
public ClientErrorDecoder() {
this.mapper = new ObjectMapper();
}
#Override
public Exception decode(String methodKey, Response response) {
ExceptionDTO exceptionDTO;
try {
exceptionDTO = mapper.readValue(response.body().asInputStream(), ExceptionDTO.class);
} catch (IOException e) {
throw new RuntimeException("Failed to process response body.", e);
}
return new CustomException1(exceptionDTO.getDetail(), exceptionDTO.getCode(), exceptionDTO.getTitle(), exceptionDTO.getStatus());
}
}
I have also configured feign for using that error decoder for that specific client like this:
feign:
client:
config:
client1:
errorDecoder: feign.codec.ErrorDecoder.Default
My question is: what is the best approach for handling more than one feign client exceptions? Should I use the same error decoder and treat their responses as a generic exception? Or should I create an error decoder for each feign client?
Quick Answer
If you work with different APIs, error responses will not be formatted the same way. Hence handling them separately seems to be the best approach.
Remarks
From your example, it seems like you defined a custom ErrorDecoder that may
be not used because you also configured feign to use default error decoder for you client1 in properties file.
Even if you defined a #Configuration class somewhere with a bean for your custom ClientErrorDecoder,
Spring Cloud documentation mentions that configuration properties take precedence over #Configuration annotation
If we create both #Configuration bean and configuration properties,
configuration properties will win. It will override #Configuration
values. But if you want to change the priority to #Configuration, you
can change feign.client.default-to-properties to false.
Example
Here is a hypothetical pruned configuration to handle multiple feign clients with different error decoders :
Client1:
You tell feign to load beans defined in CustomFeignConfiguration class for client1
#FeignClient(name = "client1", configuration = {CustomFeignConfiguration.class})
public interface Client1 {...}
Client2:
Client2 will use default Feign ErrorDecoder because no configuration is specified. (Will throw a FeignException on error)
#FeignClient(name = "client2")
public interface Client2 {...}
Configuration: Be carefull here, if you add #Configuration to CustomFeignConfiguration, then ClientErrorDecoder bean will be used for every loaded feign clients (depending on your application component scanning behaviour)
public class CustomFeignConfiguration {
#Bean
public ClientErrorDecoder clientErrorDecoder(ObjectMapper objectMapper) {
return new ClientErrorDecoder(objectMapper);
}
}
This configuration could be done with properties file aswell.
Side remark
From my point of view, you don't even need controller advice. If you use Spring Web #ResponseStatus annotation, you can tell which HTTP status code should be sent back with exception body thrown by your custom ErrorDecoder.
Helpeful resources
Spring Cloud Documentation
GitHub issue related to the subject

Use Jersey Filter in Spring Boot Jersey

I have a Jersey rest API which we are planning to migrate to Spring boot.
I have a filter that implements ContainerRequestFilter and had #Provider annotation in the filter. I registered the filter in ResourceConfig. But still i don't see the filter executing.
However I do get a warning message:-
A provider "My Filter class" registered in SERVER runtime does not implement any provider interfaces applicable in the SERVER runtime. Due to constraint configuration problems the provider "My Filter class" will be ignored.
I wanted to use jersey as servlet so changing the jersey to behave as filter is not working for my app.
Can someone help me on this?
Here is my code
Jersey filter
#Provider
public class CustomJerseyLoggingFilter implements ContainerRequestFilter, ContainerResponseFilter {
#Override
public ContainerRequest filter(ContainerRequest request) { }
#Override
public ContainerResponse filter(ContainerRequest request, ContainerResponse response) { }
}
#Component
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(CustomJerseyLoggingFilter.class);
}
}
You're implementing the wrong ContainerRequestFilter. The one you are using is from Jersey 1.x. I don't know why you even have Jersey 1.x jars in your project. The ContainerRequestFilter (for 2.x) that you should be implementing is
javax.ws.rs.container.ContainerRequestFilter
javax.ws.rs.container.ContainerResponseFilter

What does the #Secure annotation do and what package is it apart of

I'm writing an API using Java EE, JAX-RS, Jersey. In doing this I've implemented my own security context and security filter.
Looking at questions like this one (How to get MIME type of uploaded file in Jersey) I've seen the #Secure annotation but what does it do? My hope was that is was an annotation that queries the isSecure method of the security context in the same way that #RolesAllowed does for checking if a user has the right to access a particular method. If so is there such a way of doing so with annotations or am I stuck to using the #Context to get the security context and just from that.
The #Secure annotation seems to be a custom one. JAX-RS/Jersey does not support such feature out-of-the-box but it's not that hard to implement. Lets say you have your own #Secure annotation and you want to do checks whether a communication channel is secure for methods annotated with this annotation. You need to create a custom ResourceFilterFactory in which you'll assign a special filter for such methods:
public class IsSecureResourceFilterFactory implements ResourceFilterFactory {
private class IsSecureFilter implements ResourceFilter, ContainerRequestFilter {
// ResourceFilter
#Override
public ContainerRequestFilter getRequestFilter() {
return this;
}
#Override
public ContainerResponseFilter getResponseFilter() {
return null;
}
// ContainerRequestFilter
#Override
public ContainerRequest filter(final ContainerRequest request) {
// Check whether the channel is secure.
if (request.isSecure()) {
return request;
}
// Throw an exception if it's not.
throw new WebApplicationException(Response.Status.FORBIDDEN);
}
}
#Override
public List<ResourceFilter> create(final AbstractMethod abstractMethod) {
// Add IsSecureFilter for resource methods annotated with #Secure annotation (ignore other resource methods).
return abstractMethod.isAnnotationPresent(Secure.class)
? Collections.<ResourceFilter>singletonList(new IsSecureFilter()): null;
}
}
Now you need to tell Jersey about this ResourceFilterFactory. There are 2 ways:
via web.xml
<init-param>
<param-name>com.sun.jersey.spi.container.ResourceFilters</param-name>
<param-value>my.package.IsSecureResourceFilterFactory</param-value>
</init-param>
or via META-INF/services mechanism - you need to create a file called META-INF/services/com.sun.jersey.spi.container.ResourceFilterFactory which would contain a fully qualified name of your factory (in this case my.package.IsSecureResourceFilterFactory) and make sure this file is on the class-path of your application.

Resources