Search code examples
javaweb-servicesrestnetbeansglassfish-3

Getting data through REST webservice gives exception


I'm trying to create a webservice which uses REST to pass on data to a client. I'm now stuck on 2 problems, one on the client side, and one on the server side. I've been trying a lot of things and I got even more stuck then I was before. I hope any of you can help me.

I tried approaching the webservice through the browser and throught the application. If I URL directly to the server, I get the following error message:

SEVERE: A message body writer for Java class java.util.ArrayList, and Java type java.util.Collection<domain.Tweet>, and MIME media type text/plain was not found
SEVERE: The registered message body writers compatible with the MIME media type are:
text/plain ->
  com.sun.jersey.core.impl.provider.entity.StringProvider
  com.sun.jersey.core.impl.provider.entity.ReaderProvider
*/* ->
  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.StreamingOutputProvider
  com.sun.jersey.core.impl.provider.entity.SourceProvider$SourceWriter
  com.sun.jersey.server.impl.template.ViewableMessageBodyWriter
  com.sun.jersey.json.impl.provider.entity.JSONJAXBElementProvider$General
  com.sun.jersey.json.impl.provider.entity.JSONArrayProvider$General
  com.sun.jersey.json.impl.provider.entity.JSONObjectProvider$General
  com.sun.jersey.json.impl.provider.entity.JSONWithPaddingProvider
  com.sun.jersey.core.impl.provider.entity.XMLRootElementProvider$General
  com.sun.jersey.core.impl.provider.entity.XMLListElementProvider$General
  com.sun.jersey.json.impl.provider.entity.JSONRootElementProvider$General
  com.sun.jersey.json.impl.provider.entity.JSONListElementProvider$General
  com.sun.jersey.json.impl.provider.entity.JacksonProviderProxy
  com.sun.jersey.moxy.MoxyMessageBodyWorker
  com.sun.jersey.moxy.MoxyListMessageBodyWorker

If I run the client application, I get the following error message:

Exception in thread "AWT-EventQueue-0" com.sun.jersey.api.client.UniformInterfaceException: GET http://localhost:8080/KwetterSOAP/resources/rest/user/Hans/tweets returned a response status of 500 Internal Server Error
    at com.sun.jersey.api.client.WebResource.handle(WebResource.java:688)
    at com.sun.jersey.api.client.WebResource.access$300(WebResource.java:74)
    at com.sun.jersey.api.client.WebResource$Builder.get(WebResource.java:508)
    at service.RestClient.getTweets(RestClient.java:52)
    at kwettermonitor.KwetterFollowing.btnZoekFollowersActionPerformed(KwetterFollowing.java:103)
    at kwettermonitor.KwetterFollowing.access$000(KwetterFollowing.java:20)
    at kwettermonitor.KwetterFollowing$1.actionPerformed(KwetterFollowing.java:54)
    at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2018)
    at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2341)
    at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
    at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
    at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252)
    at java.awt.Component.processMouseEvent(Component.java:6505)
    at javax.swing.JComponent.processMouseEvent(JComponent.java:3321)
    at java.awt.Component.processEvent(Component.java:6270)
    at java.awt.Container.processEvent(Container.java:2229)
    at java.awt.Component.dispatchEventImpl(Component.java:4861)
    at java.awt.Container.dispatchEventImpl(Container.java:2287)
    at java.awt.Component.dispatchEvent(Component.java:4687)
    at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4832)
    at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4492)
    at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4422)
    at java.awt.Container.dispatchEventImpl(Container.java:2273)
    at java.awt.Window.dispatchEventImpl(Window.java:2713)
    at java.awt.Component.dispatchEvent(Component.java:4687)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:707)
    at java.awt.EventQueue.access$000(EventQueue.java:101)
    at java.awt.EventQueue$3.run(EventQueue.java:666)
    at java.awt.EventQueue$3.run(EventQueue.java:664)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:87)
    at java.awt.EventQueue$4.run(EventQueue.java:680)
    at java.awt.EventQueue$4.run(EventQueue.java:678)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:677)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:211)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:128)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:117)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:113)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:105)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:90)

The webservice has been created as following :

@Path("/rest")
@Stateless
public class RESTService {

    @Inject @Named(value = "kwetterService")
    private KwetterService service;

    @GET
    @Path("/user/{user}")
    @Produces(MediaType.TEXT_PLAIN)
    public User getUser(@PathParam("user") String userName)
    {
        return service.findByName(userName);
    }

    @GET
    @Path("/user/{user}/tweets")
    @Produces(MediaType.TEXT_PLAIN)
    public Collection<Tweet> getTweets(@PathParam("user") String userName)
    {
        System.out.println("Service: " + service);
        User user = service.findByName(userName);
        System.out.println(userName);
        System.out.println(user);
        return user.getTweets();
    }

The client is as following:

public class RestClient {
    private WebResource webResource;
    private Client client;
    private static final String BASE_URI = "http://localhost:8080/KwetterSOAP/resources";

    public RestClient() {
        DefaultClientConfig config = new DefaultClientConfig();
        client = Client.create(config);
        webResource = client.resource(BASE_URI).path("rest");
    }

    public User getUser(String userName) throws UniformInterfaceException {
        final WebResource.Builder userResource = webResource
           .path(String.format("/user/%s", userName))
           .accept(MediaType.TEXT_PLAIN) ;
        return userResource.get(User.class);
    }

    public Collection<Tweet> getTweets(String userName) throws UniformInterfaceException {
        final WebResource.Builder tweetResource = webResource
           .path(String.format("/user/%s/tweets", userName))
           .accept(MediaType.TEXT_PLAIN);
        return tweetResource.get(new GenericType<Collection<Tweet>>(){});
    }

The point I get a little bit confused over as well, is where to put the @XML... annotations. I've annotated the service as a Root Element:

@Named(value = "kwetterService")
@Stateless
@XmlRootElement
public class KwetterService {

I didn't annotate the used method:

public User findByName(String name)
    {
        return userDAO.getUser(name);
    }

UserDAO approaches data in the database. I've added nothing for REST/XML here. I made User a XMLRootElement as well:

@Entity
@XmlRootElement
@Table(name="Users")
public class User implements Serializable {

At the method getTweets(), I added the XMLElement annotation:

@XmlElement
    public Collection<Tweet> getTweets() {
        List<Tweet> temp = new ArrayList<Tweet>(tweets);
        Collections.reverse(temp);
        return temp;
    }

My own guess is that my whole annotation of methods and classes is wrong. Any errors you can see?


Solution

  • Remember that at its core, JAX-RS is built on top of HTTP, and HTTP doesn't know or care about Java objects. When you return objects from a resource method they have to be translated into data that can be consumed by the requesting client. This translation is done by entities called MessageBodyWriters, and all JAX-RS implementation are required to come with a prepackaged set of them. Unfortunately, there is no requirement that the implementation be able to translate your objects into TEXT_PLAIN. From the stacktrace you posted, you can actually see there is no provider capable of handling that media type:

    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.StreamingOutputProvider
    com.sun.jersey.core.impl.provider.entity.SourceProvider$SourceWriter
    

    One of the translations that all JAX-RS implementations must provide though, is one from Java objects to XML. So you can safely use that as your data format. Showing changes only, you will need to merge into your existing code:

    Java Bean:

    @Entity
    @XmlRootElement
    @Table(name="Users")
    public class User implements Serializable {
    
        @XmlWrapperElement("tweets")
        public Collection<Tweet> getTweets() {
            List<Tweet> temp = new ArrayList<Tweet>(tweets);
            Collections.reverse(temp);
            return temp;
        }
    }
    

    Server:

    @Path("/rest")
    @Stateless
    public class RESTService {
        @GET
        @Path("/user/{user}")
        @Produces(MediaType.APPLICATION_XML)
        public User getUser(@PathParam("user") String userName);
    
        @GET
        @Path("/user/{user}/tweets")
        @Produces(MediaType.APPLICATION_XML)
        public Collection<Tweet> getTweets(@PathParam("user") String userName);
    }
    

    Client:

    public class RestClient {
        public User getUser(String userName) throws UniformInterfaceException {
            final WebResource.Builder userResource = webResource
               .path(String.format("/user/%s", userName))
               .accept(MediaType.APPLICATION_XML) ;
            return userResource.get(User.class);
        }
    
        public Collection<Tweet> getTweets(String userName)
            throws UniformInterfaceException {
            final WebResource.Builder tweetResource = webResource
               .path(String.format("/user/%s/tweets", userName))
               .accept(MediaType.APPLICATION_XML);
            return tweetResource.get(new GenericType<Collection<Tweet>>(){});
        }
    }