What comes first - InjectableProvider(s) or ExceptionMapper(s)? - java

I haven't found an answer to the question above. How can I throw an exception from InjectableProvider, and get caught in ExceptionMapper, in order to provide a custom response?

Alex, Pavel,
I have tried this myself with:
a Joda DateTime injectable provider: PerRequestTypeInjectableProvider<PathParam, DateTime>
a mapper that converts exceptions into objects serializable in a format accepted by the client (XML, JSON, etc.): ExceptionMapper<WebApplicationException>
Based on my experiment, it seems that any WebApplicationException thrown in the InjectableProvider bypasses the exception mapper, and is therefore not properly formatted.
I have also tried:
adding an ExceptionMapper<ParamException>,
renaming the classes hoping the order in which these are wired follows alphabetical order,
but no luck so far.
#Pavel / anyone from Jersey: any advice would be welcome!
Thanks,
Marc.
UPDATE
If you wrap the logic in your InjectableProvider in a try/catch block, then in the catch block, the trick is to:
create your serializable POJO / your custom response,
wrap it into a javax.ws.rs.core.Response with the appropriate status code and/or media type,
pass it to the WebApplicationException, as the entity parameter.
#Pavel / anyone from Jersey: any alternative which would avoid this additional logic and make things "just work"?

Related

Java Micrometer #Counted - exception="none" and result="success" for #ExceptionHandler?

Quick question regarding Java Micrometer with #Counted and #ExceptionHandler please.
I have a very straightforward #ExceptionHandler:
#ExceptionHandler
#Counted(value = "MY_EXCEPTION", description = "SOME_DESCRIPTION")
public Mono<ResponseEntity<String>> myCoolExceptionHandler(final RuntimeException runtimeException) {
System.err.println("an exception!" + runtimeException);
return Mono.just("bad");
}
I think this combination is quite interesting, as it gives visibility on exception happening. We can build dashboard, alerts, etc, quite cool.
Unfortunately, when I looked at the metric generated, it was something like:
# HELP MY_EXCEPTION_total SOME_DESCRIPTION
# TYPE MY_EXCEPTION_total counter
MY_EXCEPTION_total{class="package.MyController",exception="none",method="myCoolExceptionHandler",result="success",} 3.0
I am quite puzzled on exception="none" and result="success"
May I ask how those values got into the metric in the first place?
Also, how to change them into something more meaningful, such as the exception class for instance?
Thank you!
The annotated method itself does not throw an exception and always completes normally. Therefore, the intercepting code installed by the annotation will never record an exception (exception=none) and the outcome will always be good (result=success). Only exceptions thrown from within the annotated method will be recorded as an error outcome.
You can always manually record metrics by injecting the MetricRegistry and then registering a metric with the appropriate name and tags.

The type Response.Response builder is not visible

I am writing a RESTful service in java, but when I try using Resource class, the following error is shown: The type Response.Response builder is not visible. I don't understand what the problem might be, since I have already imported the needed jars and configured the classpath. Does anyone have an idea what might be causing the issue?
This is the method I am using to get a list of events, and I am getting the error wherever Response is used:
#GET
#Path("/active")
#Produces(MediaType.APPLICATION_JSON)
public Response getActiveEvents() {
ArrayList<Event> list = EventSetup.getActiveEvents();
if(list.size() > 0) return Response.status(200).entity(list).build();
else return Response.status(404).entity("NULL").build();
}
The code you listed is correct. As you specified JSON as output format, JAX-RS will serialize the result object (ArrayList<Event>) using an object mapper, usually Jackson.
I assume the error occured during serialization.
are there any custom or special serializers used (in the configuration of the ObjectMapper, or as Jackson annotations)? It's possible that one of these serializers make use of the Response builder, so they may be to blame.
did you import the correct Response class (javax.ws.rs.core.Response)? You might have accidentally imported a Response class from another package.
The stack trace is immensly helpful to locate the source of the Exception - please always include the stack trace when asking for help with exceptions you're getting.
I found the solution to this problem. All the jar files included in the project need to be in the WebContent/WEB-INF/lib folder, or Eclipse won't see them and will create an error.

Catching any exception Jackson throws with a single ExceptionMapper

I have a JAX-RS project that uses Jackson to handle JSON conversions.
When Jackson throws an exception, it automatically returns a string with the error description.
Since I want to return a custom JSON object, I created an ExceptionMapper.
The problem is, it only catches the exception when I specify exactly the type of the Exception being thrown.
For example, when the JSON sent to a method contains an unknown property, this works:
public class MyExceptionMapper implements ExceptionMapper<UnrecognizedPropertyException>
But if I change UnrecognizedPropertyException to PropertyBindingException (which the first extends), it won't work.
In short:
How can I create a generic exception mapper that catches any exception thrown by Jackson (or any other component of my app, for that matter)?
try with
public class MyExceptionMapper implements ExceptionMapper<Exception>
This should be the fallback for all Exceptions.
Jackson is looking for the the hierarchie from the exception up if it finds a suitable ExceptionMapper.
It looks as long as there is something in the type hierarchy.
So UnrecognizedPropertyException will be handled by PropertyBinding-Exception mapper but not the other way round because UnrecognizedPropertyException Mapper is more specific there could be many subclasses and then there cannot be determined which Mapper to take. So it only works upwards.
An because Exception is the base Exception everything ends up there.

JAX-RS : Suppress Error Message

I have a class which takes enum values like Male,Female #POST . when I sent a wrong value like 'male' instead of 'Male' it shows me 400 Bad Request with this message in rest client : Can not construct instance of constants.Constants$GenderEnum from String value 'male': value not one of declared Enum instance names
at [Source: org.apache.catalina.connector.CoyoteInputStream#718a453d; line: 7, column: 23] (through reference chain: valueobjects.ConsumerValueObject["gender"])
My Rest End Point Looks like below :
#Consumes("application/json")
#Produces("application/json")
#POST
public Response addConsumer(ConsumerValueObject consumerVO)
Here ConsumerValueObject holds the enum.
How to suppress that error message in Rest client? I tried with ExceptionMapper but it did not help!I need to suppress the message due to security issues!
This is the Jackson response from either JsonParseExceptionMapper or JsonMappingExceptionMapper. These classes come with the dependency
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>${2.x.version}</version>
</dependency>
Whether you have this explicit dependency or you have the resteasy-jackson2-provider (which uses the above under the hood), most likely the mappers are registered implicitly through classpath scanning. For instance you have an empty Application class.
#ApplicationPath("/")
public class ResteasyApplication extends Application {}
This will cause disovery/registration through classpath scanning. If you don't have either of those dependencies, and if you are in Wildfly, I am not exactly sure how they are registered, but that is what's happening.
You could write/register your own ExceptionMappers for the JsonParseException and JsonMappingException
#Provider
public class JsonMappingExceptionMapper
implements ExceptionMapper<JsonMappingException> {
#Override
public Response toResponse(JsonMappingException e) {
return Response.status(Response.Status.BAD_REQUEST).build();
}
}
but from what I have tested, it's a tossup as to which one will be registered, yours or Jackson's. The mappers are put into a Set (so unordered), then pushed into a Map, so only one get's pushed in. The order in which they are pushed in like I said is a tossup.
I guess this is really only a partial answer, as I have not been able to find a solution that is guaranteed to use your mapper, aside from registering all your classes explicitly (ultimately disabling the classpath scanning), but that is a hassle.
But now the fight has been narrowed down. I will try again some more if I get a chance later
UPDATE
So this is not a solution, just a semi-proof-of-concept to show how we can get it to use our ExceptionMapper.
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.my.pkg.JsonMappingExceptionMapper;
#Path("/init")
public class InitResource {
#GET
public Response init() {
ResteasyProviderFactory factory = ResteasyProviderFactory.getInstance();
factory.getExceptionMappers().put(JsonMappingException.class,
new JsonMappingExceptionMapper());
return Response.ok("Done!").build();
}
}
Once we hit the init endpoint for first time, our JsonMappingExcpetionMapper will register, and override the existing one, whether it is Jackson's or ours.
Of course we would not want to do this for real, it's just showing how to override the mapper. The thing I can't figure out is where to put this code. I've tried a ServletContextListener, in the Application constructor, in a Feature with a low priority. I can't figure it out. None of the above occur before RESTeasy does its final registration.
Do you really want to supress the error message or do you want to fix the actual probelm?
You can actually catch all thrown exception with a custom exception mapper like
#Provider
public class CustomExceptionMapper implements ExceptionMapper<Throwable> {
#Override
public Response toResponse(Throwable t) {
return Response.ok().build();
}
}
though, this will handle all caught exceptions and return a 200 OK which tricks clients to think that the request actually succeeded - which was not the case! Instead of Throwable you should be able to catch the concrete exception (even if it is a RuntimeException) as well - maybe you have not declared it as provider or did not specify the correct exception class?
Though, as already mentioned returning a different status code for an exception is generally bad practice and should be avoided. Fixing the actual problem is probably more suitable in that case.
JAX-RS provides MessageBodyReader and MessageBodyWriter interfaces which you can declare to un/marshall an inputstream to an object or an object to return to an output-stream. The official documentation on MessageBodyReader has more detailed information on that regard.
One implementation therefore could be the following steps:
Read the input-stream to f.e. string
Replace all "male" or "female" tokens with their upper-case version
Parse the string to a json-representation (using org.json.JSONObject f.e)
Use ObjectMapper to convert the JSON representation to a Java object
return the mapped object
This works if the input failure is just a simple upper/lower case issue. If there are typos or semantically alternative available, which are not yet in your enum, you need to put in a bit more effort.
If you, however, fail to create a proper object representation, you should return a user-failure (something in the 400 range) to the client to inform the client that something went wrong.

Spring MVC Binding Command Object Using Get Request

I need to implement a controller that has a command object that is backing a filtering form for a search across multiple entries.
The problem is that the i was asked to do that without using POST request, instead using GET request only, and there before loosing the functionality of the default data binding that springs makes happily for us.
So i tried to implement a method, inside my controller, that looks like this:
#Override
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception {
if (isSearchRequest(request)) {
MyCommandObject myCommandObject = (MyCommandObject) getCommand(request);
System.out.println(managePositionsForm);
}
return super.handleRequestInternal(request, response);
}
But the getCommand returns me a brand new CommandObject with no values, despite that the values are present in the request object (i could retrieve then using the getParameter method of HttpServletRequest). But there isn't any binding.
So the question :
1) Is there any way to archive this?
2) Also is very important, that all the values in the form, are lost and, eventually (if this problem is solved) i will need to "persist" the filters for the users in order to avoid re entering after the first search.
Auto Response : setSessionForm(true); looks like can do the work! (According to javadoc)
Thanks to all!
Greetings
Victor.
Okey, i found a way to archive what a was looking for.
I will explain for the sake of those have the same problem before, and hoping to find a experienced user to validate this method... some quiet common is there a multiple ways to do a same thing and as human beings is very difficult to know without proper acknowledge the right path.. so this i a found looking inside the AbstractFormController (that is excellently documented with javadoc).
So what i did was the following, on my controller constructor i add these lines at the end :
setSessionForm(true);
setBindOnNewForm(true);
That all the magic!
But is not enought with setSessionForm(true). According to javadoc the setBindOnNewForm(boolean) method does the following :
/**
* Set if request parameters should be bound to the form object
* in case of a non-submitting request, i.e. a new form.
*/
So my guess are that these two flags are necessary to be marked as true, because :
The setSessionForm makes posible to store as a session attribute the form object, so "is stored in the session to keep the form object instance between requests, instead of creating a new one on each request" (according to javadoc of the setSessionForm method).
The setBindOnNewForm allows the population of the form object with the initial request (despites what type of request method we have). According the javadoc found the AbstractFormController "Only if bindOnNewForm is set to true, then ServletRequestDataBinder gets applied to populate the new form object with initial request parameters..."
But still i noticed, following the controller flow with a debugger, that the population is happening inside the method "getErrorsForNewForm(HttpServletRequest request)".. that is where a concrete object of type ServletRequestDataBinder is used IF the setBindOnNewForm is true, and later (as the javadoc stated) the onBindOnNewForm method is invoked, allowing the programmer to overwrite it with custom behavior, the default behavior is just empty (again this was double checked against the code of AbstractFormController).
I have an strong felling to validate my thoughts here, so if anyone can help me, that would be alright, besides the problem is solved!
Thanks to all in advance!
Greetings.

Categories