28

I am new at RESTful webservices and was trying to update my @OneToMany relationship from a standalone client application, but I am not able to do that. I am using the Jersey implementation of JAX-RS that ships with Glassfish 3.1.1.

I have a class A that has a @OneToMany relationship with class B.

MyRestClient is my standalone client that is calling my RESTful webservice which has been deployed on Glassfish 3.1.1.

MyRestClient.java

public class MyRestClient {
    public static void main(String[] args) {    
        Client client = Client.create();        
        WebResource resource = client.resource("http://localhost:8080/myapp/rest/a/update/123");    
        B b1 = new B("debris");     
        ClientResponse response = resource.put(ClientResponse.class, b1);
        System.out.println(response.getEntity(A.class).getTitle() + " has " + response.getEntity(A.class).getBList().size() + " Bs.");
    }
}

AResource is an EJB session bean which I am using as RESTful webservice.

AResource.java

@Stateless
@Path("/a")
public class AResource {

    @EJB
    private AManager aManager;

    @PUT
    @Consumes(MediaType.APPLICATION_XML)
    @Produces(MediaType.APPLICATION_XML)
    @Path("/update/{id}")
    public Response updateA(B b, @PathParam("id") int id) {
        A a = aManager.findAById(id);
        a.addB(b);
        return Response.status(Status.OK).entity(a).build();
    }
}

When I run the client I get the following error message: com.sun.jersey.api.client.ClientHandlerException: A message body writer for Java type, class myPackage.B, and MIME media type, application/octet-stream, was not found.

Following are the domain objects in my standalone client application which is making a call to the AResource EJB session bean which I am using as the RESTful webservice.

A.java

@XmlRootElement
public class A implements Serializable{ 

    private List<B> bList = new ArrayList<B>();
    public List<B> getBList() {
        return bList;
    }
    //remaining code

}

B.java

public class B implements Serializable {

    private String text;
    private A a;    


    @XmlTransient
    public A getA() {
        return a;
    }

    public void afterUnmarshal(Unmarshaller u, Object parent) {
        this.a = (A) parent;
    }
    //remaining code

}

Could someone help me understand why this is happening and how I should solve this problem?

Patrick
  • 2,672
  • 3
  • 31
  • 47
skip
  • 12,193
  • 32
  • 113
  • 153

10 Answers10

30

In your client code you are not specifying the content type of the data you are sending - so Jersey is not able to locate the right MessageBodyWritter to serialize the b1 object.

Modify the last line of your main method as follows:

ClientResponse response = resource.type(MediaType.APPLICATION_XML).put(ClientResponse.class, b1);

And add @XmlRootElement annotation to class B on both the server as well as the client sides.

Martin Matula
  • 7,969
  • 1
  • 31
  • 35
  • I made the suggested changes but it still gives me the same error. – skip Oct 20 '11 at 20:52
  • 3
    Oh, and you are missing @XmlRootElement annotation on class B - add that as well. – Martin Matula Oct 20 '11 at 21:11
  • Could I have more than one `@XmlRootElement` in a `@OneToMany` relationship? I made the suggested changed u asked me to make and now I am getting `Exception in thread "main" com.sun.jersey.api.client.ClientHandlerException: A message body reader for Java class myPackage.A, and Java type class myPackage.A, and MIME media type text/html was not found`. Is `@Consumes(MediaType.APPLICATION_XML)` not sufficient for Jersey to know the the representation it is going to consume? – skip Oct 20 '11 at 21:30
  • The error came from the last statement I added in the `MyRestClient.java`. From `System.out.println(response.getEntity(A.class).getTitle() + " has " + response.getEntity(A.class).getBList().size() + " Bs.");` statement. Thanks. – skip Oct 20 '11 at 21:40
  • 2
    This may be due to an error on the server. Try to read the response entity as a string (instead of A) and print it out to see what came back from the server. Also check the server log for exceptions. – Martin Matula Oct 20 '11 at 21:45
  • Yep, the error on the server end is `A message body reader for Java class myPackage.B, and Java type class myPackage.B, and MIME media type application/xml was not found`. I am totally lost now. – skip Oct 20 '11 at 21:59
  • Looks like your service war deployed to GF still has class B *without* the @XmlRootElement annotation. Make sure you have it on the server side as well. – Martin Matula Oct 20 '11 at 22:08
  • Now its giving me `org.xml.sax.SAXParseException: Premature end of file.` error at the client side. The previous error on the server side is gone. – skip Oct 20 '11 at 22:41
  • Which line? Are you reading it as string as opposed to the class A and printing it out as I suggested earlier? How does it look like? – Martin Matula Oct 20 '11 at 22:46
  • Its the `System.out.println(response.getEntity(A.class).getTitle() + " has " + response.getEntity(A.class).getBList().size() + " Bs.");` statement from `MyRestClient.java`. – skip Oct 20 '11 at 22:53
  • I mean you should print out the entity like this to see what it looks like: System.out.println(response.getEntity(String.class)); – Martin Matula Oct 20 '11 at 22:54
  • Once that works, get back to reading it as A.class, but avoid calling getEntity() twice on the same response (just to be safe). – Martin Matula Oct 20 '11 at 22:59
  • `System.out.println(response.getEntity(String.class));` and `System.out.println(response.getEntity(Movie.class).getTitle());` but not `System.out.println(response.getEntity(Movie.class).getTitle() + " has " + response.getEntity(Movie.class).getReviewList().size() + " reviews.");`. Could you help me understand why can't i call `getEntity()` twice? Could u suggest any good resources on the topic? – skip Oct 20 '11 at 23:15
  • 1
    This is because Jersey does not cache entities once read. getEntity() reads the response stream and passes it to the message body readers for processing, once processed, it closes the stream and returns it - subsequent calls to getEntity() will fail, cause the stream has been read and it can't go back to read it again. – Martin Matula Oct 21 '11 at 07:45
  • 1
    I agree this is not obvious and should be documented - I filed a bug: http://java.net/jira/browse/JERSEY-795 – Martin Matula Oct 21 '11 at 07:53
  • Thanks. Just one thing, even if I don't set the content type am sending to Jersey it still works, i.e. `ClientResponse response = resource.put(ClientResponse.class, b1);` works as well. – skip Oct 21 '11 at 20:12
4

Include this dependencies in your POM.xml and run Maven -> Update

<dependency>
    <groupId>com.sun.jersey</groupId>
    <artifactId>jersey-json</artifactId>
    <version>1.18.1</version>
</dependency>
<dependency>
   <groupId>com.owlike</groupId>
   <artifactId>genson</artifactId>
   <version>0.99</version>
</dependency>
borchvm
  • 3,533
  • 16
  • 44
  • 45
  • It is a good way to save time as the Jersey API allows you to convert strings that you receive from the webservices objects – borchvm Jul 06 '15 at 18:25
2

You need to specify the @Provider that @Produces(MediaType.APPLICATION_XML) from B.class
An add the package of your MessageBodyWriter<B.class> to your /WEB_INF/web.xml as:

<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>
your.providers.package
</param-value>
</init-param>
cgalleguillosm
  • 344
  • 3
  • 7
  • I can't try `@Provider` as I don't have the access to that code at the moment. Will let you know. Thanks. – skip Nov 24 '11 at 13:38
1

You have to do two things to remove this error.

  1. The @xmlElement mapping in the model
  2. The client side:

    response = resource.type(MediaType.APPLICATION_XML).put(ClientResponse.class, b1); //consume

    or

    response = resource.accept(MediaType.APPLICATION_XML).put(ClientResponse.class, b1); //produce

Jens Erat
  • 37,523
  • 16
  • 80
  • 96
jaskirat Singh
  • 696
  • 1
  • 8
  • 17
  • http://stackoverflow.com/questions/17342218/getting-error-a-message-body-writer-for-java-class-java-util-arraylist-listjava ..can you solve this ?? – user2416728 Jun 27 '13 at 11:58
1

Adding reference to:

<dependency>
    <groupId>com.sun.jersey</groupId>
    <artifactId>jersey-json</artifactId>
    <version>${jersey1.version}</version>
</dependency>

As long as adding clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, true); on client creation solved the issue for me:

ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, true);
Client client = Client.create(clientConfig);
llatinov
  • 104
  • 4
0

This can also happen if you've recently upgraded Ant. I was using Ant 1.8.4 on a project, and upgraded Ant to 1.9.4, and started to get this error when building a fat jar using Ant.

The solution for me was to downgrade back to Ant 1.8.4 for the command line and Eclipse using the process detailed here

Community
  • 1
  • 1
Brad Parks
  • 66,836
  • 64
  • 257
  • 336
0

i was facing the same problem for a get method i was returning an "int" for the @get method Strangely when i change the return type to String the error was gone.Give it a try and if someone knows the logic behind it kindly share it

0

This solved my issue.

http://www.markhneedham.com/blog/2012/11/28/jersey-com-sun-jersey-api-client-clienthandlerexception-a-message-body-reader-for-java-class-and-mime-media-type-applicationjson-was-not-found/

Including following dependencies in your POM.xml and run Maven -> Update also fixed my issue.

<dependency>
    <groupId>com.sun.jersey</groupId>
    <artifactId>jersey-json</artifactId>
    <version>1.19.1</version>
</dependency>
<dependency>
   <groupId>com.owlike</groupId>
   <artifactId>genson</artifactId>
   <version>0.99</version>
</dependency>
choom
  • 45
  • 1
  • 11
0

This also happens if you're missing an empty public constructor for the Entity (could be for JSON, XML etc)..

vmhacker
  • 413
  • 4
  • 7
0

Make sure that all these libs are in your class path:

compile(group: 'com.sun.jersey', name: 'jersey-core',        version: '1.19.4') 
compile(group: 'com.sun.jersey', name: 'jersey-server',      version: '1.19.4') 
compile(group: 'com.sun.jersey', name: 'jersey-servlet',     version: '1.19.4')
compile(group: 'com.sun.jersey', name: 'jersey-json',        version: '1.19.4')
compile(group: 'com.sun.jersey', name: 'jersey-client',      version: '1.19.4')
compile(group: 'javax.ws.rs', name: 'jsr311-api', version: '1.1.1')
compile(group: 'org.codehaus.jackson', name: 'jackson-core-asl', version: '1.9.2')
compile(group: 'org.codehaus.jackson', name: 'jackson-mapper-asl', version: '1.9.2')
compile(group: 'org.codehaus.jackson', name: 'jackson-core-asl',   version: '1.9.2')
compile(group: 'org.codehaus.jackson', name: 'jackson-jaxrs',      version: '1.9.2')
compile(group: 'org.codehaus.jackson', name: 'jackson-xc',         version: '1.9.2')

Add "Pojo Mapping" and "Jackson Provider" to the jersey client config:

ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
clientConfig.getClasses().add(JacksonJsonProvider.class);

This solve to me!

ClientResponse response = null;

response = webResource
        .type(MediaType.APPLICATION_JSON)
        .accept(MediaType.APPLICATION_JSON)
        .get(ClientResponse.class);


if (response.getStatus() == Response.Status.OK.getStatusCode()) {

    MyClass myclass = response.getEntity(MyClass.class);
    System.out.println(myclass);
}
Jadson Santos
  • 199
  • 1
  • 2
  • 11