Search code examples
restjerseyjava-ee-6glassfish-3rest-client

Taking advantage of GenericEntity in Jersey


I'm writing RESTful Web service in Java. The technologies that I use:

  • GlassFish 3 (based on Java 6)
  • JDK v7
  • Jersey (part of the application server)
  • Eclipse EE Kepler

The annotated POJO:

     @XmlRootElement(name = "entity")
    @XmlType(propOrder={"stValue", "dValue", "list"})
    @XmlAccessorType(XmlAccessType.FIELD)
    public final class EntityJAXBxml {

        @XmlAttribute
        private int id

 = 0;
    @XmlElement
    private String stValue = "";
    @XmlElement
    private double dValue = 0.0;
    @XmlElement
    @XmlElementWrapper(name="elements")
    private List<Integer> list = null;

    public EntityJAXBxml(){}    

    public EntityJAXBxml(int id, String stValue, double dValue,
            List<Integer> list) {
        super();
        this.id = id;
        this.stValue = stValue;
        this.dValue = dValue;
        this.list = list;
    }
... Getters, setters

That's my service method:

@GET
@Produces(MediaType.APPLICATION_XML)
@Path("/receiveXMLInGenericEntity")
public Response receiveXMLInGenericEntity(){
    EntityJAXBxml entity = new EntityJAXBxml(1, "String_value", 193.7, util.generateIntList());
    EntityJAXBxml entity_1 = new EntityJAXBxml(2, "New_string_value", 439.5, util.generateIntList());

    List<EntityJAXBxml> list = new ArrayList<>(2);
    list.add(entity);
    list.add(entity_1);

    GenericEntity<List<EntityJAXBxml>> generic = new GenericEntity<List<EntityJAXBxml>>(list){};        
    return Response.ok().entity(generic).build();
}

When I access the resource via URI on the Web browser, it prints the XML document. Thus, so far it works fine. Problems come in when I want to get the response from the service via non-browser client. For example, the following is my client implementation:

private void receiveXMLInGenericEntity(String strUrl, String method){
    HttpURLConnection connect = null;
    try {
        URL url = new URL(strUrl);
        connect = (HttpURLConnection)url.openConnection();

        connect.setRequestProperty("Accept", MediaType.APPLICATION_XML);// Accept from server
        connect.setRequestMethod(method);//GET

        connect.connect();
        Class cls = new GenericEntity<List<EntityJAXBxml>>(new ArrayList<EntityJAXBxml>()){}.getClass();

        GenericEntity<List<EntityJAXBxml>> generic = (GenericEntity<List<EntityJAXBxml>>)JAXB.unmarshal(connect.getInputStream(), cls);
        List<EntityJAXBxml> entity = generic.getEntity();

        System.out.println("GenericEntity: "+entity.size());
    }
    catch(IOException e){ e.printStackTrace();}
}

And it gives me the following exception:

 Exception in thread "main" javax.xml.bind.DataBindingException: com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
drill.rest.client.ClientXmlV6$1 is a non-static inner class, and JAXB can't handle those.
    this problem is related to the following location:
        at drill.rest.client.ClientXmlV6$1

    at javax.xml.bind.JAXB.unmarshal(JAXB.java:226)
    at drill.rest.client.ClientXmlV6.receiveXMLInGenericEntity(ClientXmlV6.java:199)
    at drill.rest.client.ClientXmlV6.main(ClientXmlV6.java:68)
Caused by: com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
drill.rest.client.ClientXmlV6$1 is a non-static inner class, and JAXB can't handle those.
    this problem is related to the following location:
        at drill.rest.client.ClientXmlV6$1

    at com.sun.xml.bind.v2.runtime.IllegalAnnotationsException$Builder.check(IllegalAnnotationsException.java:106)
    at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(JAXBContextImpl.java:466)
    at com.sun.xml.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:298)
    at com.sun.xml.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:141)
    at com.sun.xml.bind.v2.runtime.JAXBContextImpl$JAXBContextBuilder.build(JAXBContextImpl.java:1163)
    at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:145)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:248)
    at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:235)
    at javax.xml.bind.ContextFinder.find(ContextFinder.java:432)
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:637)
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:584)
    at javax.xml.bind.JAXB$Cache.<init>(JAXB.java:112)
    at javax.xml.bind.JAXB.getContext(JAXB.java:139)
    at javax.xml.bind.JAXB.unmarshal(JAXB.java:223)
    ... 2 more

That means that the following way of getting Class type value is wrong:

Class cls = new GenericEntity<List<EntityJAXBxml>>(new ArrayList<EntityJAXBxml>()){}.getClass();

But I need that type value, because it's needed input at the following method:

JAXB.unmarshal()

Now, here it comes my second version of client, and now taking advantage of Jersey client API:

private void receiveXMLInGenericEntityJerseyClient(String strUrl, String method){
    Client client = Client.create();
    WebResource resource = client.resource(strUrl);

    resource.accept(MediaType.APPLICATION_XML);
    resource.method(method);

    GenericType type = new GenericType(new ArrayList<EntityJAXBxml>(Collections.EMPTY_LIST).getClass()){};      
    GenericEntity<List<EntityJAXBxml>> generic = resource.get(type);
}

It comes with this exception:

    Aug 12, 2016 6:45:48 PM com.sun.jersey.api.client.ClientResponse getEntity
SEVERE: A message body reader for Java class java.util.ArrayList, and Java type class java.util.ArrayList, and MIME media type application/xml was not found
Aug 12, 2016 6:45:48 PM com.sun.jersey.api.client.ClientResponse getEntity
SEVERE: The registered message body readers compatible with the MIME media type are:
application/xml ->
  com.sun.jersey.core.impl.provider.entity.XMLJAXBElementProvider$App
  com.sun.jersey.core.impl.provider.entity.DocumentProvider
  com.sun.jersey.core.impl.provider.entity.SourceProvider$StreamSourceReader
  com.sun.jersey.core.impl.provider.entity.SourceProvider$SAXSourceReader
  com.sun.jersey.core.impl.provider.entity.SourceProvider$DOMSourceReader
  com.sun.jersey.core.impl.provider.entity.XMLRootElementProvider$App
  com.sun.jersey.core.impl.provider.entity.XMLListElementProvider$App
  com.sun.jersey.core.impl.provider.entity.XMLRootObjectProvider$App
*/* ->
  com.sun.jersey.core.impl.provider.entity.FormProvider
  com.sun.jersey.core.impl.provider.entity.MimeMultipartProvider
  com.sun.jersey.core.impl.provider.entity.StringProvider
  com.sun.jersey.core.impl.provider.entity.ByteArrayProvider
  com.sun.jersey.core.impl.provider.entity.FileProvider
  com.sun.jersey.core.impl.provider.entity.InputStreamProvider
  com.sun.jersey.core.impl.provider.entity.DataSourceProvider
  com.sun.jersey.core.impl.provider.entity.XMLJAXBElementProvider$General
  com.sun.jersey.core.impl.provider.entity.ReaderProvider
  com.sun.jersey.core.impl.provider.entity.DocumentProvider
  com.sun.jersey.core.impl.provider.entity.SourceProvider$StreamSourceReader
  com.sun.jersey.core.impl.provider.entity.SourceProvider$SAXSourceReader
  com.sun.jersey.core.impl.provider.entity.SourceProvider$DOMSourceReader
  com.sun.jersey.json.impl.provider.entity.JSONJAXBElementProvider$General
  com.sun.jersey.core.impl.provider.entity.XMLRootElementProvider$General
  com.sun.jersey.core.impl.provider.entity.XMLListElementProvider$General
  com.sun.jersey.core.impl.provider.entity.XMLRootObjectProvider$General
  com.sun.jersey.core.impl.provider.entity.EntityHolderReader
  com.sun.jersey.json.impl.provider.entity.JSONRootElementProvider$General
  com.sun.jersey.json.impl.provider.entity.JSONListElementProvider$General
  com.sun.jersey.moxy.MoxyMessageBodyWorker
  com.sun.jersey.moxy.MoxyListMessageBodyWorker

Exception in thread "main" com.sun.jersey.api.client.ClientHandlerException: A message body reader for Java class java.util.ArrayList, and Java type class java.util.ArrayList, and MIME media type application/xml was not found
    at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:550)
    at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:524)
    at com.sun.jersey.api.client.WebResource.handle(WebResource.java:686)
    at com.sun.jersey.api.client.WebResource.get(WebResource.java:196)
    at drill.rest.client.ClientXmlV6.receiveXMLInGenericEntityJerseyClient(ClientXmlV6.java:216)
    at drill.rest.client.ClientXmlV6.main(ClientXmlV6.java:69)

In the end, what I want is just to read the output from the service at my client-end-point. I want to use the GenericEntity object in order to have better understanding of Jersey/Java EE ins-and-outs. Any ideas how to get around this problem?


Solution

  • Here you go, that is a solution, at least in terms of jersey-client.jar API. I needed to add the following lines:

    List<EntityJAXBxml> g = resource.get(new GenericType<List<EntityJAXBxml>>(){});
        EntityJAXBxml entity = g.get(1);
        System.out.println(g.size()+" : "+entity.getdValue()+" : Element count: "+entity.getList().size());
        //Also valid
        //ClientResponse response = resource.get(ClientResponse.class);
        //List<EntityJAXBxml> entity = response.getEntity(new GenericType<List<EntityJAXBxml>>(){});
    
        //EntityJAXBxml g = entity.get(1);
        //System.out.println(entity.size()+" : "+g.getdValue()+" : Element count: "+g.getList().size());
    

    And then you can read generic entity at the client side.