Wildfly: ExceptionMapper not triggered with RestEasy JSR-303 Bean Validation - json

I'm using Bean Validation with RestEasy in Wildfly 8.2.0.Final:
#Path("/user")
#Produces(MediaType.APPLICATION_JSON)
public class UserEndpoint
{
//more code
#GET
#Path("/encrypt/{email}")
public Response fetchEncryptedId(#PathParam("email") #NotNull String email)
{
String encryptedUserId = userService.getEncryptedUserId(email);
return Response.ok().entity(new UserBo(encryptedUserId)).build();
}
}
This basically works. Now I'd like to get the response as JSON object but I can't get it working. All my "application" exceptions are handled by my Exception Mapper, this works:
#Provider
public class DefaultExceptionMapper implements ExceptionMapper<Exception>
{
private static final String MEDIA_TYPE = "application/json";
private LoggingService loggingService;
#EJB
public void setLoggingService(LoggingService loggingService)
{
this.loggingService = loggingService;
}
#Override
public Response toResponse(Exception exception)
{
ResponseObject responseObject = new ResponseObject();
responseObject.registerExceptionMessage(exception.getMessage());
if (exception instanceof ForbiddenException)
{
loggingService.log(LogLevel.ERROR, ((ForbiddenException)exception).getUserId(), ExceptionToStringMapper.map(exception));
return Response.status(Status.FORBIDDEN).type(MEDIA_TYPE).entity(responseObject).build();
}
//more handling
loggingService.log(LogLevel.ERROR, "", ExceptionToStringMapper.map(exception));
return Response.status(Status.INTERNAL_SERVER_ERROR).type(MEDIA_TYPE).entity(responseObject).build();
}
}
But bean validation somehow bypasses it. Then I thought about using Throwable instead of Exception but it didn't help either. I guess the ExceptionMapper is not triggered because there is some life cycle problem with JAX-RS and JSR303. But how can I syncronize them to handle bean validation exceptions?
Additional information: The exception passes the javax.ws.rs.container.ContainerResponseFilter so I could write some workaround by implementing the filter method in a subclass, but this is not clean solution. The target is to handle the exceptions in the Exception mapper.

It's not always the case that your ExceptionMapper<Exception> will catch all exception under the Exception hierarchy. If there is another more specific mapper, say one for RuntimeException, that mapper will be used for all exception of RuntimeException and its subtypes.
That being said (assuming you're using resteasy-validation-provider-11), there is already a ResteasyViolationExceptionMapper that handles ValidationException.
#Provider
public class ResteasyViolationExceptionMapper
implements ExceptionMapper<ValidationException>
This mapper is automatically registered. It returns results in the form of a ViolationReport. The client needs to set the Accept header to application/json in order to see a response similar to
{
"exception":null,
"fieldViolations":[],
"propertyViolations":[],
"classViolations":[],
"parameterViolations":[
{
"constraintType":"PARAMETER",
"path":"get.arg0",
"message":"size must be between 2 and 2147483647",
"value":"1"}
],
"returnValueViolations":[]
}
You can see more at Violation reporting.
If you want to completely override this behavior, you can create a more specific mapper for ResteasyViolationException, which is the exception thrown by the RESTeasy validator
#Provider
public class MyValidationMapper
implements ExceptionMapper<ResteasyViolationException> {
#Override
public Response toResponse(ResteasyViolationException e) {
}
}

Related

Spring AMQP RPC consumer and throw exception

I have a consumer (RabbitListner) in RPC mode and I would like to know if it is possible to throw exception that can be treated by the publisher.
To make more clear my explication the case is as follow :
The publisher send a message in RPC mode
The consumer receive the message, check the validity of the message and if the message can not be take in count, because of missing parameters, then I would like to throw Exception. The exception can be a specific business exception or a particular AmqpException but I want that the publisher can handle this exception if it is not go in timeout.
I try with the AmqpRejectAndDontRequeueException, but my publisher do not receive the exception, but just a response which is empty.
Is it possible to be done or may be it is not a good practice to implement like that ?
EDIT 1 :
After the #GaryRussel response here is the resolution of my question:
For the RabbitListner I create an error handler :
#Configuration
public class RabbitErrorHandler implements RabbitListenerErrorHandler {
#Override public Object handleError(Message message, org.springframework.messaging.Message<?> message1, ListenerExecutionFailedException e) {
throw e;
}
}
Define the bean into a configuration file :
#Configuration
public class RabbitConfig extends RabbitConfiguration {
#Bean
public RabbitTemplate getRabbitTemplate() {
Message.addWhiteListPatterns(RabbitConstants.CLASSES_TO_SEND_OVER_RABBITMQ);
return new RabbitTemplate(this.connectionFactory());
}
/**
* Define the RabbitErrorHandle
* #return Initialize RabbitErrorHandle bean
*/
#Bean
public RabbitErrorHandler rabbitErrorHandler() {
return new RabbitErrorHandler();
}
}
Create the #RabbitListner with parameters where rabbitErrorHandler is the bean that I defined previously :
#Override
#RabbitListener(queues = "${rabbit.queue}"
, errorHandler = "rabbitErrorHandler"
, returnExceptions = "true")
public ReturnObject receiveMessage(Message message) {
For the RabbitTemplate I set this attribute :
rabbitTemplate.setMessageConverter(new RemoteInvocationAwareMessageConverterAdapter());
When the messsage threated by the consumer, but it sent an error, I obtain a RemoteInvocationResult which contains the original exception into e.getCause().getCause().
See the returnExceptions property on #RabbitListener (since 2.0). Docs here.
The returnExceptions attribute, when true will cause exceptions to be returned to the sender. The exception is wrapped in a RemoteInvocationResult object.
On the sender side, there is an available RemoteInvocationAwareMessageConverterAdapter which, if configured into the RabbitTemplate, will re-throw the server-side exception, wrapped in an AmqpRemoteException. The stack trace of the server exception will be synthesized by merging the server and client stack traces.
Important
This mechanism will generally only work with the default SimpleMessageConverter, which uses Java serialization; exceptions are generally not "Jackson-friendly" so can’t be serialized to JSON. If you are using JSON, consider using an errorHandler to return some other Jackson-friendly Error object when an exception is thrown.
What worked for me was :
On "serving" side :
Service
#RabbitListener(id = "test1", containerFactory ="BEAN CONTAINER FACTORY",
queues = "TEST QUEUE", returnExceptions = "true")
DataList getData() {
// this exception will be transformed by rabbit error handler to a RemoteInvocationResult
throw new IllegalStateException("mon expecion");
//return dataHelper.loadAllData();
}
On "requesting" side :
Service
public void fetchData() throws AmqpRemoteException {
var response = (DataList) amqpTemplate.convertSendAndReceive("TEST EXCHANGE", "ROUTING NAME", new Object());
Optional.ofNullable(response)
.ifPresentOrElse(this::setDataContent, this::handleNoData);
}
Config
#Bean
AmqpTemplate amqpTemplate(ConnectionFactory connectionFactory, MessageConverter messageConverter) {
var rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(messageConverter);
return rabbitTemplate;
}
#Bean
MessageConverter jsonMessageConverter() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
objectMapper.registerModule(new JavaTimeModule());
var jsonConverter = new Jackson2JsonMessageConverter(objectMapper);
DefaultClassMapper classMapper = new DefaultClassMapper();
Map<String, Class<?>> idClassMapping = Map.of(
DataList.class.getName(), DataList.class,
RemoteInvocationResult.class.getName(), RemoteInvocationResult.class
);
classMapper.setIdClassMapping(idClassMapping);
jsonConverter.setClassMapper(classMapper);
// json converter with returned exception awareness
// this will transform RemoteInvocationResult into a AmqpRemoteException
return new RemoteInvocationAwareMessageConverterAdapter(jsonConverter);
}
You have to return a message as an error, which the consuming application can choose to treat as an exception. However, I don't think normal exception handling flows apply with messaging. Your publishing application (the consumer of the RPC service) needs to know what can go wrong and be programmed to deal with those possibilities.

JAX-RS Exception Mapper not working in Grizzly container

Working on a Jersey web application with a team, as the project got bigger and bigger, we decided to switch from Tomcat to Grizzly to allow deploying parts of the project on different port numbers. What I've found out now, that the custom exception handling we have fails to work now, instead I always get the grizzly html page.
Example exception:
public class DataNotFoundException extends RuntimeException{
private static final long serialVersionUID = -1622261264080480479L;
public DataNotFoundException(String message) {
super(message);
System.out.println("exception constructor called"); //this prints
}
}
Mapper:
#Provider
public class DataNotFoundExceptionMapper implements ExceptionMapper<DataNotFoundException>{
public DataNotFoundExceptionMapper() {
System.out.println("mapper constructor called"); //doesnt print
}
#Override
public Response toResponse(DataNotFoundException ex) {
System.out.println("toResponse called"); //doesnt print
ErrorMessage errorMessage = new ErrorMessage(ex.getMessage(), 404, "No documentation yet.");
return Response.status(Status.NOT_FOUND)
.entity(errorMessage)
.build();
//ErrorMessage is a simple POJO with 2 string and 1 int field
}
}
I'm not sure where is the problem source, if needed I can provide more information/code. What's the problem, what can I try?
EDIT:
Main.class:
public class Main {
/**
* Main method.
* #param args
* #throws Exception
*/
public static void main(String[] args) throws Exception {
...
List<ServerInfo> serverList = new ArrayList<ServerInfo>();
serverList.add(new ServerInfo(
"api",8450,
new ResourceConfig().registerClasses(
the.package.was.here.ApiResource.class)
));
for(ServerInfo server : serverList) {
server.start();
}
System.out.println("Press enter to exit...");
System.in.read();
for(ServerInfo server : serverList) {
server.stop();
}
}
}
EDIT2:
based on this question I've tried using this ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR, "true"property, which only helped a little. I still get the html grizzly page when the exception happens, but now I see my exception (+stack trace) in the body of the page.
You're only registering one resource class for the entire application
new ResourceConfig().registerClasses(
eu.arrowhead.core.api.ApiResource.class
)
The mapper needs to be registered also
new ResourceConfig().registerClasses(
eu.arrowhead.core.api.ApiResource.class,
YourMapper.class)
)
You can also use package scanning, which will pick up all classes and automatically register them, if they are annotated with #Path or #Provider
new ResourceConfig().packages("the.packages.to.scan")

Owin self hosted web api handle exception in JsonConverter

If exception is thrown in my custom JsonConverter, I want to stop the request and return the error to the client. The problem is that if an exception happens in the converter, the controller method gets the parameter as null and I lose the exception. How can I stop the request from getting to the controller or get the occurred exception from JsonConverter in the controller? Or any other idea..
My converter similar to this post: How to implement custom JsonConverter in JSON.NET to deserialize a List of base class objects?
public class MyJsonConverter : JsonConverter
{
protected override Person Create(Type objectType, JObject jObject)
{
Throw new Exception();
}
}
My controller:
Public class MyController : ApiController
{
Public IHttpResuls Post(Person p)
{
...
}
}
I have a solution. Since the HttpContext.Current is null in OWIN web api. You can use this package - OwinRequestScopeContext which allows you to use the context which is also per request.
for example set the error in the JsonConverter:
OwinRequestScopeContext.Current.Items["error"] = exception;
And then get the error in your controller:
var ex = OwinRequestScopeContext.Current.Items["error"] as Exception;

Post processing of a Json response in spring MVC

I have several controllers that return the same generic Response object with #ResponseBody annotation, like this:
#RequestMapping(value = "/status", method = RequestMethod.GET)
#Transactional(readOnly = true)
public #ResponseBody Response<StatusVM> status()
I need to perform an operation on every controller, after the Response is returned. This operation will enrich the Response object with new data.
I don't want to duplicate code, so I need a single point of intervention. I thought I could do this with Interceptors, however, according to the docs http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-handlermapping-interceptor this does not work well with #ResponseBody:
Note that the postHandle method of HandlerInterceptor is not always ideally suited for use with #ResponseBody and ResponseEntity methods. In such cases an HttpMessageConverter writes to and commits the response before postHandle is called which makes it impossible to change the response, for example to add a header. Instead an application can implement ResponseBodyAdvice and either declare it as an #ControllerAdvice bean or configure it directly on RequestMappingHandlerAdapter.
I haven't been able to find an example of this tecnique, could anybody help me?
As an alternative I could work with aspects, but then I'd need to annotate every controller, which is something I'd like to avoid.
In the end I implemented ResponseBodyAdvice like this:
#ControllerAdvice
public class StatusAdvice implements ResponseBodyAdvice<Response<?>> {
#Override
public boolean supports(MethodParameter returnType,
Class<? extends HttpMessageConverter<?>> converterType) {
if (returnTypeIsReponseVM(returnType)&&responseConverterIsJackson2(converterType)){
return true;
}
return false;
}
....
#Override
public Response<?> beforeBodyWrite(Response<?> body, MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
....
return body;
}
}
So it was easier then expected.

Error Handling in Jersey

I have a Jersey application where I want to prevent the client from seeing any type of stacktrace if any type of Exception occurs.
How do I do this without changing any existing code?
You can register an exception mapper as follows to handle all exceptions and customize the HTTP response:
#Provider
public class MyExceptionMapper implements javax.ws.rs.ext.ExceptionMapper<Exception> {
#Override
public Response toResponse(Exception ex) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
}
}