Jersey unable to catch any Jackson Exception - json

For my REST api I'm using jersey and ExceptionMapper to catch global exceptions.
It works well all the exception my app throws but I'm unable to catch exception thrown by jackson.
For example one of my endpoint accept an object that contains an enum. If the Json in the request has a value that is not in the enum jersey throw this exception back
Can not construct instance of my.package.MyEnum from String value 'HELLO': value not one of declared Enum instance names: [TEST, TEST2]
at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream#5922e236; line: 3, column: 1] (through reference chain: java.util.HashSet[0]->....)
Even though I have created this mapper
#Provider
#Component
public class JacksonExceptionMapper implements ExceptionMapper<JsonMappingException> {
#Override
public Response toResponse(JsonMappingException e) {
....
}
}
The code never reach this mapper.
Is there anything we need to do in order to catch these exceptions?
EDIT
Note: I have jus tried being less general and instead of JsonMappingException I use InvalidFormatException in this case the mapper is called. But I still don't understand because InvalidFormatException extends JsonMappingException and should be called as well

Had the same problem.
The problem is that JsonMappingExceptionMapper kicks in before your mapper. The actual exception is of class com.fasterxml.jackson.databind.exc.InvalidFormatException and the mapper defines com.fasterxml.jackson.jaxrs.base.JsonMappingException, so it's more specific to the exception.
You see, Jersey's exception handler looks to find the most accurate handler (see org.glassfish.jersey.internal.ExceptionMapperFactory#find(java.lang.Class, T)).
To override this behavior, simply disable the mapper from being used:
Using XML:
<init-param>
<param-name>jersey.config.server.disableAutoDiscovery</param-name>
<param-value>true</param-value>
</init-param>
Using code: resourceConfig.property(CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE, true); where resourceConfig is of type org.glassfish.jersey.server.ServerConfig.
You can also write your own specific mapper:
public class MyJsonMappingExceptionMapper implements ExceptionMapper<JsonMappingException>
But I think it's an over kill.

Hi it seems to exits an alternative answer now that does not require to disable Jersey AUTO_DISCOVERY feature.
Just annotate your own exception mapper with a #Priority(1) annotation. The lower the number, the higher the priority. Since Jackson's own mappers do not have any priority annotation, yours will be executed:
#Priority(1)
public class MyJsonMappingExceptionMapper implements ExceptionMapper<JsonMappingException>

Starting in version 2.29.1 [1], if you're registering the JacksonFeature, you can now do so without registering the exception mappers [2]:
register(JacksonFeature.withoutExceptionMappers());
[1] https://github.com/eclipse-ee4j/jersey/pull/4225
[2] https://eclipse-ee4j.github.io/jersey.github.io/apidocs/2.34/jersey/org/glassfish/jersey/jackson/JacksonFeature.html#withoutExceptionMappers--

Related

When to use ResponseStatusException and ControllerAdvice

Spring5 has introduced ResponseStatusException, which has put me in a dilemma as to in what scenario I can use a ResponseStatusException and ControllerAdvice as both of them are quiet similar.
Can anyone help me with this.
Thanks in advance.
Lets first understand what is ResponseStatusException and ControllerAdvice
ResponseStatusException is a programmatic alternative to #ResponseStatus and is the base class for exceptions used for applying a status code to an HTTP response.
#GetMapping("/actor/{id}")
public String getActorName(#PathVariable("id") int id) {
try {
return actorService.getActor(id);
} catch (ActorNotFoundException ex) {
throw new ResponseStatusException(
HttpStatus.NOT_FOUND, "Actor Not Found", ex);
}
}
The #ControllerAdvice annotation allows us to consolidate multiple, scattered #ExceptionHandlers into a single, global error handling component.
#ControllerAdvice
public class RestResponseEntityExceptionHandler
extends ResponseEntityExceptionHandler {
#ExceptionHandler(value
= { IllegalArgumentException.class, IllegalStateException.class })
protected ResponseEntity<Object> handleConflict(
RuntimeException ex, WebRequest request) {
return ResponseEntity<Object>;
}
}
Coming back to your questions of when to use what:
If you want to provide a unified and global way of exception handling make use of
ControllerAdvice. It also eliminates code duplication which might be caused by ResponseStatusException.
In order to throw different error code and responses for the same exception, don't want to create custom exception classes and to avoid tight coupling make use of ResponseStatusException.
References:
Spring ResponseStatusException
Error Handling for REST with Spring
Overall it is better to use #ControllerAdvice if you are looking for a more unified solution but ResponseStatusException is also handy too in case you don't want to make different Exception classes and want to keep it simple.
for examples and more info you can refer to the following articles:
Spring Boot Exception Handling — #ControllerAdvice
Spring Boot Exception Handling — ResponseStatusException

Corda query throws "com.fasterxml.jackson.databind.JsonMappingException: object is not an instance of declaring class"

I'm developing cordapp using the example-cordapp project as a reference. I've been able to commit a transaction to the ledger and even run querias on the node to see if it's really there. However, when I try to run query from my Spring Boot application, I get this error.
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request
processing failed; nested exception is
org.springframework.http.converter.HttpMessageConversionException: JSON mapping problem:
java.util.Collections$UnmodifiableRandomAccessList[0]->net.corda.core.contracts.StateAndRef["state"]-
>net.corda.core.contracts.TransactionState["data"]-
>com.mypackage.states.MyState["party"]; nested exception is
com.fasterxml.jackson.databind.JsonMappingException: object is not an instance of declaring class
(through reference chain: java.util.Collections$UnmodifiableRandomAccessList[0]-
>net.corda.core.contracts.StateAndRef["state"]->net.corda.core.contracts.TransactionState["data"]-
>com.mypackage.states.MyState["party"])] with root cause
java.lang.IllegalArgumentException: object is not an instance of declaring class
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_251]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_251]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~
[na:1.8.0_251]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_251]
Here's the request code
#GetMapping(value = [ "/api/v1/states" ], produces = [MediaType.APPLICATION_JSON_VALUE])
fun getMyIOUs(): ResponseEntity<List<StateAndRef<MyState>>> {
val myStates = proxy.vaultQueryBy<MyState>().states
return ResponseEntity.ok(myStates)
}
And here's the state code
#BelongsToContract(com.sentinel.contract.SharingInformationContract::class)
class SharingInformationState(
val party: Party,
val dataOwnerId: Long,
val dataBuyerId: Long,
override val linearId: UniqueIdentifier = UniqueIdentifier()) : LinearState, QueryableState {
override val participants: List<AbstractParty> = listOf(party)
override fun generateMappedObject(schema: MappedSchema): PersistentState {
return when (schema) {
SharingInformationSchemaV1 -> SharingInformationSchemaV1.PersistentSharingInformation(
party,
dataOwnerId,
dataBuyerId,
linearId.id
)
else -> throw IllegalArgumentException("Unrecognised schema $schema")
}
}
override fun supportedSchemas(): Iterable<MappedSchema> = listOf(SharingInformationSchemaV1)
}
There's little information about this issue on the internet. Some suggest it is connected to the classpath, that something is duplicated there, but I don't know how to check. Also, this error isn't connected to the Party type. I've tried to add #JsonIgnore on a party, but then it throws on the other field. Persistence of this field in mapping schema also doesn't matter. I've tried persisting and not persisting, it changes nothing. Thanks in advance!
I believe this is because you are missing Corda Jackson support library which is required to convert Corda objects to json.
Add this to your dependencies in the build.gradle
compile "net.corda:corda-jackson:$corda_release_version"
https://github.com/corda/samples-java/blob/master/Advanced/auction-cordapp/client/build.gradle#L19
Also, make sure you have a MappingJackson2HttpMessageConverter bean configured.
#Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(){
ObjectMapper mapper = JacksonSupport.createDefaultMapper(partyAProxy());
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(mapper);
return converter;
}
https://github.com/corda/samples-java/blob/master/Advanced/auction-cordapp/client/src/main/java/net/corda/samples/client/AppConfig.java#L48
The Exception java.lang.IllegalArgumentException: object is not an instance of declaring class is something that happens if a method is called by reflection on an object which is of the wrong type.
In conjunction with jackson that may happen because a generic is lying to you. Here is an example:
class A (val x: String)
class B (val y: String)
class C (val z: List<A>)
ObjectMapper().writeValueAsString(C(listOf(B("x")) as List<A>))
This causes a compile warning, but it compiles and initially runs because of type erasure. However we forcefully injected a List<B> in a place where actually a List<A> is expected. While type erasure does remove quite a bit of information, it does not do so completely. Reflection can still be used to determine that C.z is actually of type List<A>. Jackson uses this information and tries to serialize an object of type A but instead finds an object of type B in the list and fails with the given message.
Check that your data structure actually contains the types that you expect!

Error thrown: "No serializer found for class java.lang.Long..." from controller while serializing JPA entity containing lazy "many-to-one" property

I am on Spring Boot 2.0.6, where an entity pet do have a Lazy many-to-one relationship to another entity owner
Pet entity
#Entity
#Table(name = "pets")
public class Pet extends AbstractPersistable<Long> {
#NonNull
private String name;
private String birthday;
#JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
#JsonIdentityReference(alwaysAsId=true)
#JsonProperty("ownerId")
#ManyToOne(fetch=FetchType.LAZY)
private Owner owner;
But while submitting a request like /pets through a client(eg: PostMan), the controller.get() method run into an exception as is given below:-
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class java.lang.Long and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.ArrayList[0]->com.petowner.entity.Pet["ownerId"])
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77) ~[jackson-databind-2.9.7.jar:2.9.7]
at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1191) ~[jackson-databind-2.9.7.jar:2.9.7]
Controller.get implementation
#GetMapping("/pets")
public #ResponseBody List<Pet> get() {
List<Pet> pets = petRepository.findAll();
return pets;
}
My observations
Tried to invoke explicitly the getters within owner through pet to force the lazy-loading from the javaassist proxy object of owner within the pet. But did not work.
#GetMapping("/pets")
public #ResponseBody List<Pet> get() {
List<Pet> pets = petRepository.findAll();
pets.forEach( pet -> pet.getOwner().getId());
return pets;
}
Tried as suggested by this stackoverflow answer at https://stackoverflow.com/a/51129212/5107365 to have controller call to delegate to a service bean within the transaction scope to force lazy-loading. But that did not work too.
#Service
#Transactional(readOnly = true)
public class PetServiceImpl implements PetService {
#Autowired
private PetRepository petRepository;
#Override
public List<Pet> loadPets() {
List<Pet> pets = petRepository.findAll();
pets.forEach(pet -> pet.getOwner().getId());
return pets;
}
}
It works when Service/Controller returning a DTO created out from the entity. Obviously, the reason is JSON serializer get to work with a POJO instead of an ORM entity without any mock objects in it.
Changing the entity fetch mode to FetchType.EAGER would solve the problem, but I did not want to change it.
I am curious to know why it is thrown the exception in case of (1) and (2). Those should have forced the explicit loading of lazy objects.
Probably the answer might be connected to the life and scope of that javassist objects got created to maintain the lazy objects. Yet, wondering how would Jackson serializer not find a serializer for a java wrapper type like java.lang.Long. Please do rememeber here that the exception thrown did indicate that Jackson serializer got access to owner.getId as it recognised the type of the property ownerId as java.lang.Long.
Any clues would be highly appreciated.
Edit
The edited part from the accepted answer explains the causes. Suggestion to use a custom serializer is very useful one in case if I don't need to go in DTO's path.
I did a bit of scanning through the Jackson sources to dig down to the root causes. Thought to share that too.
Jackson caches most of the serialization metadata on first use. Logic related to the use case in discussion starts at this method com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serializeContents(Collection<?> value, JsonGenerator g, SerializerProvider provider). And, the respective code snippet is:-
The statement serializer = _findAndAddDynamic(serializers, cc, provider) at Line #140 trigger the flow to assign serializers for pet-level properties while skipping ownerId to be later processed through serializer.serializeWithType at line #147.
Assigning of serializers is done at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.resolve(SerializerProvider provider) method. The respective snippet is shown below:-
Serializers are assigned at line #340 only for those properties which are confirmed as final through the check at line #333.
When owner comes here, its proxied properties are found to be of type com.fasterxml.jackson.databind.type.SimpleType. Had this associated entity been loaded eagerly, the proxied properties obviously won't be there. Instead, original properties would be found with the values that are typed with final classes like Long, String, etc. (just like the pet properties).
Wondering why can't Jackson address this from their end by using the getter's type instead of using that of the proxied property. Anyway, that could be a different topic to discuss :-)
This has to do with the way that Hibernate (internally what spring boot uses for JPA by default) hydrates objects. A lazy object is not loaded until some parameter of the object is requested. Hibernate returns a proxy which delegates to the dto after firing queries to hydrate the objects.
In your scenario, loading OwnerId does not help because it is the key via which you are referencing the owner object i.e. the OwnerId is already present in the Pet object, so the hydration will not take place.
In both 1 and 2, you have not actually loaded the owner object, so when Jackson tries to serialize it at the controller level it fails. In 3 and 4, the owner object has been loaded explicitly, which is why Jackson does not run into any issues.
If you want 2 to work then load some parameter of owner, other than id, and hibernate will hydrate the object, and then jackson will be able to serialize it.
Edited Answer
The problem here is with the default Jackson serializer. This inspects the class returned and fetches the value of each attribute via reflection. In the case of hibernate entities, the object returned is a delegator proxy class in which all parameters are null, but all getters are redirected to the contained instance. When the object is inspected, the values of each attribute are still null, which is defaulted to an error as explained here
So basically, you need to tell jackson how to serialize this object. You can do so by creating a serializer class
public class OwnerSerializer extends StdSerializer<Owner> {
public OwnerSerializer() {
this(null);
}
public OwnerSerializer(Class<Owner> t) {
super(t);
}
#Override
public void serialize(Owner value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException {
jgen.writeStartObject();
jgen.writeNumberField("id", value.getId());
jgen.writeStringField("firstName", value.getFirstName());
jgen.writeStringField("lastName", value.getLastName());
jgen.writeEndObject();
}
}
And setting it as the default serializer for the object
#JsonSerialize(using = OwnerSerializer.class)
public class Owner extends AbstractPersistable<Long> {
Alternatively, you can create a new Object of type Owner from the proxy class, manually populate it and set it in the response.
It is a little roundabout, but as a general practice you should not expose your DTO's externally anyway. The controller/domain should be decoupled from the storage layer.

Spring MVC controller Json response, hibernate proxy error

I have a Spring controller annotated class which implements this method:
#RequestMapping(value = "/event/eventList", method = RequestMethod.GET)
public #ResponseBody List<Event> listEvents() {
System.out.println("############ LIST EVENTS ############");
List<Event> events = eventService.listAllEvents();
for(Event event : events) {
Hibernate.getClass(event);
System.out.println(event);
}
return events;
}
when I call the page (localhost:8080/myapp/event/eventList) from browser, the method will be called correctly i see all the logs and the events are printed correctly meaning the event list is not empty and valid, but I get the error:
GRAVE: Servlet.service() for servlet [dispatcher] in context with path [/myapp] threw exception [Request processing failed; nested exception is java.lang.UnsupportedOperationException: Attempted to serialize java.lang.Class: org.hibernate.proxy.HibernateProxy. Forgot to register a type adapter?] with root cause
java.lang.UnsupportedOperationException: Attempted to serialize java.lang.Class: org.hibernate.proxy.HibernateProxy. Forgot to register a type adapter?
It does not return any Json representation.
I changed the method to return a string like:
#RequestMapping(value = "/event/eventList", method = RequestMethod.GET)
public #ResponseBody String listEvents() {
return "{'pippo':1}";
}
In this case the browser show the string correctly.
did I miss something?
The exception is thrown by com.google.gson.internal.bind.TypeAdapters when GSON is trying to serialize variable 'events' to Json.
This happens, cause
eventService.listAllEvents()
returns not a list already containing all events, but hibernate proxy that will do that lazy, when the list is actually used.
GSON does not know how to serialize that proxy.
Hibernate.getClass should initialize the underlying object as a side effect.
You need to call it also for the List 'events' itself, not only for every single event. The List can be a hibernate proxy also.
You may find more info on that topic at
Could not serialize object cause of HibernateProxy

Custom ExceptionMapper for Jersey not working for invalid JSON input

I have the following resource that consumes a JSON being mapped to a POJO.
#Path("example")
public class ExampleResource {
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response addThesis(MyObject myObject) {
return Response.ok().entity("Test").build();
}
}
Here's the POJO class:
public class MyObject {
private String title;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
When I send a POST request with the body {"title":"Test title"} everything works fine. The response is Test, as expected. However, when I change the request to {"titlee":"Test title"} the server replies with this:
Unrecognized field "titlee" (class com.my.package.MyObject), not marked as ignorable (one known property: "title"])
at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream#6dc6a46a; line: 2, column: 11] (through reference chain: com.my.package.MyObject["titlee"])
Obviously this is an exception thrown and returned by Jersey. How can I intercept this exception and return a custom status code and message?
What I've tried so far is to implement my own ExceptionMapper:
#Provider
public class MyJsonExceptionMapper implements ExceptionMapper<JsonProcessingException> {
public Response toResponse(JsonProcessingException e) {
return Response.status(400).entity("JSON Processing Error").build();
}
}
Unfortunately the response stays the same. When I implement an ExceptionMapper for a custom exception and throw the corresponding exception in the resource method though, everything works fine. I assume this has to do with the default ExceptionMapper for JsonProcessingException overriding my own one. Then I tried to create a generic mapper ("implements ExceptionMapper"), but again no success.
I've looked literally everywhere and tried many things including extending ResourceConfig and registering my mapper, but nothing has worked so far.
Some more information that might help to narrow the problem down: I am using Grizzly2 as the HTTP server which I am deploying as a Fat JAR.
The dependency part of my pom.xml looks like this:
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.24</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-http</artifactId>
<version>2.24</version>
</dependency>
</dependencies>
Any advice is highly appreciated.
Ok, this is dumb and hack-ish, but worked for me:
register(JacksonJaxbJsonProvider.class);
This is due to the following "nice default behavior" in the Jackson feature entry point:
if (!config.isRegistered(JacksonJaxbJsonProvider.class)) {
// add the default Jackson exception mappers
context.register(JsonParseExceptionMapper.class);
context.register(JsonMappingExceptionMapper.class);
:(
But, I'd still prefer an answer that fixes the problem "for real" - ie. without pre-registering components so that the feature cannot configure them properly...
I also faced this issue. If JacksonFeature is registered, you can simply register JacksonJaxbJsonProvider as a workaround.
When the JacksonFeature is in the classpath, it is automatically discovered by Jersey. Another approach to fix it is disabling auto discovery by setting ServerProperties.FEATURE_AUTO_DISCOVERY_DISABLE to true. As a result of this, you would need to register other features manually.
Alternatively you can get rid of the jersey-media-json-jackson artifact and use jackson-jaxrs-json-provider instead. With this, you will get rid of JacksonFeature and then you can register your own exception mappers.
One last option and probably what seems to be the correct solution (as pointed in Kysil Ivan's answer) you can write your own exception mapper and then give it a high priority, such as 1. If you use auto discovery, just annotate it with #Provider and #Priority:
#Provider
#Priority(1)
public class JsonParseExceptionMapper implements ExceptionMapper<JsonParseException> {
...
}
If you manually register your provider, you can give your provider a binding priority:
#ApplicationPath("/")
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig() {
register(JsonParseExceptionMapper.class, 1);
}
}
See this answer for more details.
We use JAX-RS on Wildfly to implement our web services and use the following to accomplish what you are trying to do with Jersey on Glassfish. Maybe it has similar features which you could look up. Our steps are:
The service is a stateless EJB, use EJB interceptor to trap exception
and populate request scoped object with details
Implement a PostProcessInterceptor which reads from request scoped object and modifies response before service returns. (This is specific to JAX-RS)