Jersey does not see my MessageBodyReader - java

I'm trying to use jersey with my own json MessageBodyReader/MessageBodyWriter (as I am not use #XmlRootElement... annotations on my domain classes).
#Provider
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public final class MyGsonMessageBodyHandler implements MessageBodyWriter<Object>, MessageBodyReader<Object> {
...
}
Jersey uses this class as messagebodywriter (as it stops at breakpoint in the implemented method writeTo). Hovewer it does not see this class as messagebodyreader (and even when I break up this class to the separate implementations of the messagebodyreader/messagebodywriter it still refuses to use my messagebodyreader).
The testing code looks as follows (jersey-grizzly):
final Greeting greeting = resource.path("/greeting")
.queryParam("name", name)
.accept(MediaType.APPLICATION_JSON)
.type(MediaType.APPLICATION_JSON)
.get(Greeting.class);
The error I got looks as follows:
A message body reader for Java class test.Greeting, and Java type class test.Greeting, and MIME media type application/json was not found
I'm wondering what kind of magic is required for writing own MessageBodyReader?

After a while I found a root cause of the issue. My implementation of MessageBodyReader/Writer is OK (and I it works fine with RESTlet), but IF YOU USE JerseyTest, DO NOT FORGET TO ADD YOUR MessageBodyReader/Writer to it's ClientConfig:
/**
* Creates custom REST client config which is mandatory since we don't use any JSON providers.
* #return Jersey Client Config with the required classes to read/write in(out)coming data.
*/
private static ClientConfig createClientConfig() {
final ClientConfig config = new DefaultClientConfig();
config.getClasses().add(GsonMessageBodyHandler.class);
config.getClasses().add(GsonAwareContextResolver.class);
return config;
}
/**
* Public ctor
* #throws com.sun.jersey.test.framework.spi.container.TestContainerException On error
*/
public MyRestExposureTest() throws TestContainerException {
super(new WebAppDescriptor.Builder("my.rest.package")
.clientConfig(createClientConfig())
.contextPath("/")
.build());
}
Otherwise your client code would be unable to read/write your POJOs.

This is what I'm using and it's working (currently with Jersey 1.8).
public static Client createMyClient() {
ClientConfig cc = new DefaultClientConfig();
cc.getClasses().add(MyProviderClass1.class);
cc.getClasses().add(MyProviderClass2.class);
cc.getClasses().add(MyProviderClass3.class);
return Client.create(cc);
}

I'm not sure if it's your case, but the common mistake is incorrect implementation of isReadable method.
Did you implemented it?
Do you stop there, when debugging?
Does it return true?

After trying Alex' solution, this finally worked for me:
public IntegrationTest() throws TestContainerException {
super(new LowLevelAppDescriptor.Builder(createResourceConfig())
.build());
}
private static ResourceConfig createResourceConfig() {
ResourceConfig rc = new PackagesResourceConfig("com.github.joschi.jersey.security.smime");
rc.getSingletons().add(new EnvelopedWriter());
rc.getSingletons().add(new SignedWriter());
return rc;
}

Related

Feign client body type serialization failure with Jackson encoder

I'm working on implementing a Spring service and client and would like to use OpenFeign for the client. The client will be deployed with legacy applications that do not want to incur a dependency on Spring, so I'm using OpenFeign directly instead of via Spring Cloud.
I've run into an issue with the Jackson encoder and the Body type. It seems that the Jackson encoder cannot serialize an interface implementation to an interface type for the client method. e.g. if my client method is createFoo(Foo interface) where Foo is an interface calling the method with createFoo((FooImpl)fooImpl) where FooImpl implements the Foo interface then I get an encoder exception.
I've created an MCCE Gradle project demonstrating the issue here
The client definition is this:
public interface FooClient {
#RequestLine("POST /submit")
#Headers("Content-Type: application/json")
Response createFoo(Foo foo);
#RequestLine("POST /submit")
#Headers("Content-Type: application/json")
Response createFooImpl(FooImpl foo);
interface Foo { int id(); }
record FooImpl(int id) implements Foo { }
}
And the failing test demonstrating the issue is this:
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class FooClientTest {
#LocalServerPort int port;
#Test
public void clientTest() {
final FooClient lClient = Feign.builder()
.encoder(new JacksonEncoder(List.of(
// Possibly this would be necessary with the original encoder implementation.
// new FooModule()
)))
.target(FooClient.class, String.format("http://localhost:%s", port));
Response response = lClient.createFooImpl(new FooImpl(10));
assertThat(response.status()).isEqualTo(404);
response = lClient.createFoo(new FooImpl(10));
assertThat(response.status()).isEqualTo(404); // <<===== This fails with the exception below.
}
public static class FooModule extends SimpleModule {
{
addAbstractTypeMapping(Foo.class, FooImpl.class);
}
}
}
The exception is:
feign.codec.EncodeException: No serializer found for class
codes.asm.feign.mcce.client.FooClient$FooImpl
and no properties discovered to create
BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
This issue was introduced in this commit. It seems to have somehow removed Jackson's ability to map the encoder for the interface to the implementation by explicitly calling for the interface encoder.
JavaType javaType = mapper.getTypeFactory().constructType(bodyType);
template.body(mapper.writerFor(javaType).writeValueAsBytes(object), Util.UTF_8);
Based on some experiments I think the original code would work fine, potentially with some configuration of the encoder via a Module.
As demonstrated in the test, I can work around the issue by typing the client method with the interface implementation, but this is undesirable for a number of reasons in my context.
I've figured out a workaround, but it's quite ugly. Create a Module and add the following serializer. I expect this to be extremely brittle and will likely just abandon the interface and go with a concrete record definition as a DTO.
addSerializer(new JsonSerializer<Foo>() {
#Override
public Class<Foo> handledType() {
return Foo.class;
}
/**
* This is an ugly hack to work around this: https://github.com/OpenFeign/feign/issues/1608
* Alternative would be to just make Foo a concrete record
* instead of an interface. That may be better.
*/
#Override
public void serialize(Foo value, JsonGenerator gen,
SerializerProvider serializers) throws IOException {
gen.writeStartObject();
final Method[] methods = Foo.class.getMethods();
for (Method method : methods) {
try {
final Object result = method.invoke(value);
gen.writePOJOField(method.getName(), result);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new IllegalArgumentException(String.format("Class %s has method %s which is not an accessible no argument getter", value.getClass(), method.getName()));
}
}
gen.writeEndObject();
}
});

NPE in ModelAttribute while sending empty list

I'm experiencing problem with feign client (spring-boot-starter 2.4.4, spring-cloud-starter-openfeign 3.0.2). When I'm trying to send an empty list inside #ModelAttribute annotated object feign client throws feign.codec.EncodeException with NullPointerException cause. Problem does not occur when list has at least one element.
Does anybody know how to properly override feign encoder to enable passing empty list without errors?
You will want to create a class that implements the feign Encoder (https://github.com/OpenFeign/feign/blob/master/core/src/main/java/feign/codec/Encoder.java)
e.g.
public class EnableEmptyListEncoder implements Encoder {
#Override
public void encode(Object object, Type bodyType, RequestTemplate template) {
// empty list encode logic here
}
}
How you point to that encoder depends on your setup:
-- via the application.yml:
feign:
client:
config:
feignName:
encoder: com.example.EnableEmptyListEncoder
-- via a buider:
Feign.builder()
.encoder( new EnableEmptyListEncoder() )
-- a bean in the config class
#Bean
public EnableEmptyListEncoder encoder() {
return new EnableEmptyListEncoder();
}

You must use at least one, but no more than one http method annotation on for reaseasy proxy client

I am trying to implement a simple client in rest easy, but I am getting an error saying "You must use at least one, but no more than one http method annotation". In my server implementation, I have added a http annotation on my method.
#Path("/")
public class TestResource
{
#GET
#Path("/domain/{value}")
public String get(#PathParam("value") final String value) {
return "Hello" + value;
}
}
I debugged it through, the first time it is not hitting the runtime exception, However, it is making a second call to it and failing, not sure why and how.
My client as junit test:
#Test
public void testPerformRestEasy() {
ResteasyClient client = new ResteasyClientBuilder().build();
ResteasyWebTarget target = client.target("http://localhost:8080/");
TestResource proxy = target.proxy(TestResource.class);
String response = proxy.get("user");
Assert.assertEquals("Hellouser", response);
}
The code where it is failing
private static <T> ClientInvoker createClientInvoker(Class<T> clazz, Method method, ResteasyWebTarget base, ProxyConfig config)
{
Set<String> httpMethods = IsHttpMethod.getHttpMethods(method);
if (httpMethods == null || httpMethods.size() != 1)
{
throw new RuntimeException("You must use at least one, but no more than one http method annotation on: " + method.toString());
}
ClientInvoker invoker = new ClientInvoker(base, clazz, method, config);
invoker.setHttpMethod(httpMethods.iterator().next());
return invoker;
}
Error:
java.lang.RuntimeException: You must use at least one, but no more than one http method annotation on: public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
at org.jboss.resteasy.client.jaxrs.ProxyBuilder.createClientInvoker(ProxyBuilder.java:76)
at org.jboss.resteasy.client.jaxrs.ProxyBuilder.proxy(ProxyBuilder.java:52)
at org.jboss.resteasy.client.jaxrs.ProxyBuilder.build(ProxyBuilder.java:120)
at org.jboss.resteasy.client.jaxrs.internal.ClientWebTarget.proxy(ClientWebTarget.java:72)
Does anyone know what the issue is here?
The Resteasy JAXRS 2 client does not seem to accept implementation classes directly. To make it work, you have to create a properly annotated interface. It is used by Resteasy to generate a client proxy and your server must implement exactly the same interface.
So in your case, you have to split your code into an interface and a separate implementation class:
#Path("/")
public interface TestResource {
#GET
#Path("/domain/{value}")
String get(#PathParam("value") final String value);
}
public class TestResourceImpl implements TestResource {
#Override String get(final String value) {
return "Hello" + value;
}
}
I'm not sure if this is Resteasy-specific or required by the specification, but solved the same issue for me. You can find the section that gave me the hint here in the documentation.
You have to define the MIME media type resource representation of resource(#Produces/#Consumes) from client. Like -
#Path("/")
public class TestResource
{
#GET
#Produces("text/plain")
#Path("/domain/{value}")
public String get(#PathParam("value") final String value) {
return "Hello" + value;
}
}
The Jboss Client framework Doc will help you more.
In my case the developer of the Rest Client Interface had wrongly extended RestEasyClientProxy. It wasn't the methods in the Rest Interface that were missing the http annotations, but the inherited methods.
Removing extends RestEasyClientProxy from the Rest Client Interface code fixed the issue.

DropWizard/Jersey API Clients

DropWizard uses Jersey under the hood for REST. I am trying to figure out how to write a client for the RESTful endpoints my DropWizard app will expose.
For the sake of this example, let's say my DropWizard app has a CarResource, which exposes a few simple RESTful endpoints for CRUDding cars:
#Path("/cars")
public class CarResource extends Resource {
// CRUDs car instances to some database (DAO).
public CardDao carDao = new CarDao();
#POST
public Car createCar(String make, String model, String rgbColor) {
Car car = new Car(make, model, rgbColor);
carDao.saveCar(car);
return car;
}
#GET
#Path("/make/{make}")
public List<Car> getCarsByMake(String make) {
List<Car> cars = carDao.getCarsByMake(make);
return cars;
}
}
So I would imagine that a structured API client would be something like a CarServiceClient:
// Packaged up in a JAR library. Can be used by any Java executable to hit the Car Service
// endpoints.
public class CarServiceClient {
public HttpClient httpClient;
public Car createCar(String make, String model, String rgbColor) {
// Use 'httpClient' to make an HTTP POST to the /cars endpoint.
// Needs to deserialize JSON returned from server into a `Car` instance.
// But also needs to handle if the server threw a `WebApplicationException` or
// returned a NULL.
}
public List<Car> getCarsByMake(String make) {
// Use 'httpClient' to make an HTTP GET to the /cars/make/{make} endpoint.
// Needs to deserialize JSON returned from server into a list of `Car` instances.
// But also needs to handle if the server threw a `WebApplicationException` or
// returned a NULL.
}
}
But the only two official references to Drop Wizard clients I can find are totally contradictory to one another:
DropWizard recommended project structure - which claims I should put my client code in a car-client project under car.service.client package; but then...
DropWizard Client manual - which makes it seem like a "DropWizard Client" is meant for integrating my DropWizard app with other RESTful web services (thus acting as a middleman).
So I ask, what is the standard way of writing Java API clients for your DropWizard web services? Does DropWizard have a client-library I can utilize for this type of use case? Am I supposed to be implementing the client via some Jersey client API? Can someone add pseudo-code to my CarServiceClient so I can understand how this would work?
Here is a pattern you can use using the JAX-RS client.
To get the client:
javax.ws.rs.client.Client init(JerseyClientConfiguration config, Environment environment) {
return new JerseyClientBuilder(environment).using(config).build("my-client");
}
You can then make calls the following way:
javax.ws.rs.core.Response post = client
.target("http://...")
.request(MediaType.APPLICATION_JSON)
.header("key", value)
.accept(MediaType.APPLICATION_JSON)
.post(Entity.json(myObj));
Yes, what dropwizard-client provides is only to be used by the service itself, most likely to communicate other services. It doesn't provide anything for client applications directly.
It doesn't do much magic with HttpClients anyway. It simply configures the client according to the yml file, assigns the existing Jackson object mapper and validator to Jersey client, and I think reuses the thread pool of the application. You can check all that on https://github.com/dropwizard/dropwizard/blob/master/dropwizard-client/src/main/java/io/dropwizard/client/JerseyClientBuilder.java
I think I'd go about and structure my classes as you did using Jersey Client. Following is an abstract class I've been using for client services:
public abstract class HttpRemoteService {
private static final String AUTHORIZATION_HEADER = "Authorization";
private static final String TOKEN_PREFIX = "Bearer ";
private Client client;
protected HttpRemoteService(Client client) {
this.client = client;
}
protected abstract String getServiceUrl();
protected WebResource.Builder getSynchronousResource(String resourceUri) {
return client.resource(getServiceUrl() + resourceUri).type(MediaType.APPLICATION_JSON_TYPE);
}
protected WebResource.Builder getSynchronousResource(String resourceUri, String authToken) {
return getSynchronousResource(resourceUri).header(AUTHORIZATION_HEADER, TOKEN_PREFIX + authToken);
}
protected AsyncWebResource.Builder getAsynchronousResource(String resourceUri) {
return client.asyncResource(getServiceUrl() + resourceUri).type(MediaType.APPLICATION_JSON_TYPE);
}
protected AsyncWebResource.Builder getAsynchronousResource(String resourceUri, String authToken) {
return getAsynchronousResource(resourceUri).header(AUTHORIZATION_HEADER, TOKEN_PREFIX + authToken);
}
protected void isAlive() {
client.resource(getServiceUrl()).get(ClientResponse.class);
}
}
and here is how I make it concrete:
private class TestRemoteService extends HttpRemoteService {
protected TestRemoteService(Client client) {
super(client);
}
#Override
protected String getServiceUrl() {
return "http://localhost:8080";
}
public Future<TestDTO> get() {
return getAsynchronousResource("/get").get(TestDTO.class);
}
public void post(Object object) {
getSynchronousResource("/post").post(object);
}
public void unavailable() {
getSynchronousResource("/unavailable").get(Object.class);
}
public void authorize() {
getSynchronousResource("/authorize", "ma token").put();
}
}
if anyone is trying to use DW 0.8.2 when building a client, and you're getting the following error:
cannot access org.apache.http.config.Registry
class file for org.apache.http.config.Registry not found
at org.apache.maven.plugin.compiler.AbstractCompilerMojo.execute(AbstractCompilerMojo.java:858)
at org.apache.maven.plugin.compiler.CompilerMojo.execute(CompilerMojo.java:129)
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:132)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:208)
... 19 more
update your dropwizard-client in your pom.xml from 0.8.2 to 0.8.4 and you should be good. I believe a jetty sub-dependency was updated which fixed it.
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-client</artifactId>
<version>0.8.4</version>
<scope>compile</scope>
</dependency>
You can integrated with Spring Framework to implement

Jersey Exception mappers not working when jackson deserialization fails

I am using Jersey 2.10 with Jackson serialization/deserialization feature in my REST API.
My idea is to make my REST API to always return a standard JSON error response. For that I have ExceptionMapper classes that build proper json error responses for any exception being thrown in the Jersey application. I also have a jsp which produces the same kind of JSON response, which I registered as error-page in the web.xml that covers all the errors that could come before Jersey being loaded.
But there is one case in which neither my Exception mappers nor my json producing jsp are working, that is when sending a bad formed json to a POST REST endpoint which just returns the following message:
HTTP/1.1 400 Bad Request
Server: Apache-Coyote/1.1
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, DELETE, PUT
Content-Type: text/plain
Content-Length: 210
Date: Tue, 24 Jun 2014 22:14:11 GMT
Connection: close
Can not deserialize instance of com.example.rest.User[] out of START_OBJECT token
at [Source: org.glassfish.jersey.message.internal.EntityInputStream#1dcccac; line: 1, column: 1]
How can I make Jersey to return my custom error response instead of this?
UPDATE:
Based on the answer by #Lucasz, I did more research and found that there are two Exception mappers defined inside the package com.fasterxml.jackson.jaxrs.base (https://github.com/FasterXML/jackson-jaxrs-providers/tree/master/base/src/main/java/com/fasterxml/jackson/jaxrs/base) JsonMappingExceptionMapper and JsonParseExceptionMapper that seem to be shadowing my custom mappers.
How can I unregister those mappers?
This is how I am currently registering the mappers:
#ApplicationPath("/")
public class MyApp extends ResourceConfig{
public SyntheticAPIApp() {
packages("com.example.resource", "com.example.mapper");
register(org.glassfish.jersey.jackson.JacksonFeature.class);
}
}
I tested it with an exception mapper like below:
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import com.fasterxml.jackson.core.JsonProcessingException;
#Provider
public class JsonProcessingExceptionMapper implements ExceptionMapper<JsonProcessingException>{
public static class Error {
public String key;
public String message;
}
#Override
public Response toResponse(JsonProcessingException exception) {
Error error = new Error();
error.key = "bad-json";
error.message = exception.getMessage();
return Response.status(Status.BAD_REQUEST).entity(error).build();
}
}
and it worked.
Update: changed JsonParseException to JsonProcessingException (more general)
Update2:
In order to avoid registering the unwanted mappers replace
register(org.glassfish.jersey.jackson.JacksonFeature.class);
with
register(com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider.class);
Look at the source code of JacksonFeature and you'll understand what's happening.
Starting with Jersey 2.26 (1, 2) it should be enough to annotate the custom exception mapper with a sufficiently high Priority (high here meaning a low, strictly positive number). To override the “default” mappers provided by org.glassfish.jersey.media:jersey-media-json-jackson (to register(JacksonFeature.class)) we only provide these two custom mappers:
#Provider
#Priority(1)
public class JsonMappingExceptionMapper implements ExceptionMapper<JsonMappingException> {
/* ... */
}
#Provider
#Priority(1)
public class JsonParseExceptionMapper implements ExceptionMapper<JsonParseException> {
/* ... */
}
Unfortunately JAX-RS 2 Spec disregards priorities and only states:
When choosing an exception mapping provider to map an exception,
an implementation MUST use the provider whose generic type is the nearest superclass of the exception.
Not registering JacksonFeature.class and registering JacksonJaxbJsonProvider.class instead as mentioned in another answer did not lead to consistent results.
I had the same problem, and the previous answer led me to the solution, but was not forking for me current Jersey (2.22). At first, I needed to use the org.glassfish.jersey.spi.ExtendedExceptionMapper like described in https://jersey.java.net/documentation/latest/representations.html.
Furthermore, Jersey is checking for an exception mapper, which is as close as possible to the thrown exception (from org.glassfish.jersey.internal.ExceptionMapperFactory):
for (final ExceptionMapperType mapperType : exceptionMapperTypes) {
final int d = distance(type, mapperType.exceptionType);
if (d >= 0 && d <= minDistance) {
final ExceptionMapper<T> candidate = mapperType.mapper.getService();
if (isPreferredCandidate(exceptionInstance, candidate, d == minDistance)) {
mapper = candidate;
minDistance = d;
if (d == 0) {
// slight optimization: if the distance is 0, it is already the best case, so we can exit
return mapper;
}
}
}
}
Therefore I needed to map exactly the exception and not a more general exception.
In the end, my provider looks as follows:
#Provider
public final class JsonParseExceptionExceptionHandler implements ExtendedExceptionMapper<JsonParseException> {
#Override
public Response toResponse(final JsonParseException exception) {
exception.printStackTrace();
return Response.status(Response.Status.BAD_REQUEST).entity("JSON nicht in korrektem Format.").build();
}
#Override
public boolean isMappable(final JsonParseException arg0) {
return true;
}
}
I used "jackson-jaxrs-json-provider 2.8.8" and JAX-RS 2.0
Application class - you needs to register your ExceptionMapper implementation class:
#ApplicationPath("pathApplication")
public class ApplicationConfiguration extends Application{
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new HashSet<>();
resources.add(YourJAXRSClass.class);
resources.add(JsonJacksonEM.class); //ExceptionMapper class implementation
//others resources that you need...
return resources;
}
}
ExceptionMapper class implementation:
#Provider
public class JsonJacksonEM implements ExceptionMapper<JsonParseException>{
#Override
public Response toResponse(JsonParseException exception) {
//you can return a Response in the way that you want!
return Response.ok(new YourObject()).build();
}
}
I had the same problem and solve overriding the ExceptionMapper. Perfect! One extra thing that I needed to do and were not understanding 100% was how to override the JacksonProvider for my application (I don't know if it was related to Jersey's version that I was using - 2.19). Here's my web.xml part that overrides it:
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>
com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider
</param-value>
</init-param>
I have faced this issue recently, and the solution of registering com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider doesn't helped much. When I dig deeper I came to know that Jersey by default org.glassfish.jersey.jackson.JacksonFeature is registered by Jersey, if the dependency jersey-media-json-jackson even without the explicit declaration of registering it(Don't sure from which version the auto register is implemented, I guess atleast from Jersey 2.29) present in the classpath.
The JacksonFeature inturn registers JsonParseExceptionMapper and JsonMappingExceptionMapper automatically. Because of these default JSON exception mappers, all JSON related exceptions are not redirected to the custom exception mapper
Fortunately, Jersey 2.29.1 added support for registering JacksonFeature without the exception handlers. link feature request link, code changes.
#Provider
public class ApplicationConfig extends Application {
#Override
public Set<Object> getSingletons() {
Set<Object> singletons = super.getSingletons();
singletons.add(JacksonFeature.withoutExceptionMappers());
return singletons;
}
}
The above code snippet will override the default JacksonFeature registered by Jersey. By doing so, all JSON-related exceptions will be redirected to custom exception mappers present in the application.

Categories

Resources