0

I am pretty new to REST programming.

Below is my class which is intended to return XML/JSON but I am having some difficulties to make it return proper values. I tried returning Response, JsonArray and Object of my POJO class but its not working. I looked several threads but not able to figure out what exactly is the problem.

The resource class:

public class UserService {

    UserDBHandler userDBHandler; 
    Friend f;

    @GET
    @Path("users/{userId}/friends")
//  @Produces(MediaType.TEXT_PLAIN)
    @Produces({ MediaType.APPLICATION_XML,MediaType.APPLICATION_JSON})
    public Friend  getFriends(@PathParam("userId") String userId) throws SQLException, ClassNotFoundException
    {
        System.out.println("userId : " +userId);
        userDBHandler =  new UserDBHandler();
        f = new Friend();



        ArrayList<String> userList = userDBHandler.fetchUsers(userId);

        System.out.println("Array size: "+userList.size());
        Iterator<String> iterator = userList.iterator();

        while(iterator.hasNext())
        {
            f.setUri(iterator.next());
            System.out.println(f.getUri());
        }

        //JsonObject  object = Json.createObjectBuilder().add("frienduri",f.getUri()).build();
        //ResponseBuilder response = Response.ok(f);

        //return Json.createArrayBuilder().add(object).build()
        //return response.build();
        return f;
    }
}

The POJO class:

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;

@XmlRootElement
public class Friend{

    private String friendURI;
    private String event;
    private String uri;

    String getUri() {
        return uri;
    }
    void setUri(String uri) {
        this.uri = uri;
    }
    String getFriendURI() {
        return friendURI;
    }
    void setFriendURI(String friendURI) {
        this.friendURI = friendURI;
    }
    String getEvent() {
        return event;
    }
    void setEvent(String event) {
        this.event = event;
    }
}

This is what I get when I return the Response or the Friend object:

**<?xml version="1.0" encoding="UTF-8" standalone="yes"?><friend/>**

And when returning JsonArray this is what I get:

[{"frienduri":{"string":"b@b.com","chars":"b@b.com","valueType":"STRING"}}]

Another problem I am facing is: if I create a constructor I get below error:

A MultiException has 1 exceptions. They are: 1. java.lang.NoSuchMethodException: Could not find a suitable constructor in repository.resources.UserService class.

My dev environment is Tomcat 8 , JDK 1.8, Eclipse Luna.

I am not using Maven or web.xml instead I have an Application class.

I am using jaxrs-ri-2.13 and jackson jars:

  • jackson-core-asl-1.9.2
  • jackson-jaxrs-1.9.2
  • jackson-mapper-asl-1.9.2
  • jackson-xc-1.9.2

Json jars

  • javax.json-api-1.0
  • javax.json-1.0.4

Thanks

Paul Samsotha
  • 188,774
  • 31
  • 430
  • 651
user3275095
  • 1,425
  • 4
  • 20
  • 32
  • 1
    This is not a complete question. The result you are showing for xml would _not_ be the result with the code you're showing. The top element would be ``, not ``. You don't say what you want. I have no idea why you are attempting to return JSONArray and Friend (they have absolutely no relation). Also Maven has absolutely nothing to do with `web.xml/Application`. Also post what JAX-RS implementation you are using, and what jars you have. You will need more than standard jars for JSON support. – Paul Samsotha Nov 28 '14 at 03:47
  • Its indeed , I lost the output so wrote it manually. Sorry about that. I mentioned Maven just because earlier I was getting some MessageWriter error and some threads suggested to add some dependencies in maven. For messagewriter error I imported few jackson jars. I'll edit the question to list the jars. – user3275095 Nov 28 '14 at 03:58

1 Answers1

1

First thing you should understand is that JAX-RS is a specification, and that jar you have jaxrs-ri-2.13 is an implementation (actually the reference implementation) from Jersey. Knowing the implementation is important in helping to solve your problem. So you should mention this in your post (through a tag or explicitly).

Second, for serialization, JAX-RS requires MessageBodyReaders and MessageBodyWriters. Jersey distribution has support for xml out the box, but for JSON we need to add a module with for support. Jackson by itself does not provide support for this. We need a provider module like jersey-media-jason-jackson. Using Maven is the easiest way to get this module, as it is dependendent on other jars. If I create a new Maven project, with only this dependency, this is the complete list, of artifacts the the module is dependent on

enter image description here

The Jersey distribution has some of these artifacts already. I am not sure if all of them are included in the bundle jar, but based on the main (un bundled) Jersey distribution, the un-shaded jars are not included

enter image description here

You will need to go looking for those jars and add them to your project. Like I said, I don't know if the Jersey Bundle jar comes with the others, packages, bu you can get the complete distribution here that uses individual jars. If using the single jar doesn't work, I would look into using the separate jars (all of them).

Adding these jars should give you Friend to JSON support.


<?xml version="1.0" encoding="UTF-8" standalone="yes"?><friend/>

You need to make your Friend accessor methods public. The writer cannot find the getXxx method as it's not in the same package. You should also make the setXxx methods public.


Another problem I am facing is: if I create a constructor I get below error:

A MultiException has 1 exceptions. They are: 1. java.lang.NoSuchMethodException: Could not find a suitable constructor in repository.resources.UserService class

Yup. The resource class is created by Jersey, which handles the lifecycle of our request scoped resource classes, and requires a no-arg constructor. Adding a constructor with arguments, cancels the implicit no-arg constructor. But even if you create a no-arg constructor and you other arg constructor, the arg constructor will never be called, unless you explicitly create the instance of your resource class as a singleton.


UPDATE

Regarding the singelton instance, I did not find the same in tutorials. Can you please tell me the way to do that?

When you extend Application, you can override getClasses() (which will be container managed request scoped resource class) and getSingletons which will be a single instance for the entire application

@ApplicationPath("..")
public class MyApplication extends Application {
    private Set<Class<?>> classes = new HashSet<>();
    private Set<Object> singletons = new HashSet<>();

    public MyApplication() {
        singletons.add(new UserResource(new UserDBHandler()));
    }

    @Override
    public Set<Class<?>> getClasses() {
        return classes;
    }
  
    @Override
    public Set<Object> getSingletons() {
        return singletons;
    }
}

But I am not sure if you really want UserResource to be a singleton. This means that you want this resource to be stateful for the entire application, which may not be desired. Instead you can use an injection framework, supported by Jersey, in order to inject the UserDBHandler into the UserResource class. Something like

@Path("...")
public class UserResource {
    @Inject
    private UserDBHandler handler;
}

@ApplicationPath("..")
public class MyApplication extends Application {
    private Set<Class<?>> classes = new HashSet<>();

    public MyApplication() {
        classes.add(UserResource.class);
        singletons.add(new AppBinder());
    }

    @Override
    public Set<Class<?>> getClasses() {
        return classes;
    }

    class AppBinder extends org.glassfish.hk2.utilities.binding.AbstractBinder {
        @Override
        public void configure() {
            bind(UserDBHandler.class).to(UserDBHandler.class);
        }
    }
}

See more at Custom Injection. Also I just tested this with the single jaxrs-rs jar and it does not come with the needed classes. You will need to download the separate Jersey distribution (mentioned above) will all the single jars, and add all those jars to your project, for this to work, or at least include the hk jars, which are also required by the Jackson provider anyway.

Community
  • 1
  • 1
Paul Samsotha
  • 188,774
  • 31
  • 430
  • 651
  • Thanks for the comprehensive answer. Making getters and setters did the job. Now, getting the proper XML. Will test for Json also. Regarding the singelton instance, I did not find the same in tutorials. Can you please tell me the way to do that? Thanks. – user3275095 Nov 28 '14 at 04:59
  • Please see my **UPDATE** – Paul Samsotha Nov 28 '14 at 05:25
  • Adding the other jackson jars had no other effect, output is still [{"friendid":{"string":"b@b.com","chars":"b@b.com","valueType":"STRING"}}] However returning json in response object is working fine. – user3275095 Nov 28 '14 at 05:28
  • Are you still trying to return a `JSONArray`? Just return `Friend`. Or `List` if you want a JSON array as the result – Paul Samsotha Nov 28 '14 at 05:29
  • Thanks for the update. Is it just because I have Application subclass I have to make singleton instance? Because I have created another sample app which uses web.xml instead and that does not give this error. – user3275095 Nov 28 '14 at 05:33
  • I am just checking different options which works when. I am a learner so just trying. Also, in some tutorials I read that its best practice to return Response and some say JSONArray. – user3275095 Nov 28 '14 at 05:38
  • 1
    There is different support for JSON. If you use `JsonArray`, then you are trying to use Java's JSONP API. That is different than using Jackson. Using Jackson allows us to get JSON from your domain objects. Using JSONP, we need to create the JSON structures ourselve using the API's `JsonObject`, `JsonArray`, etc. With Jackson, we can simply return regular Java objects like `Friend` or `List` instead of `JsonObject` or `JsonArray`. Whether you return `Response` or `Friend`, the outcome will be the same (with conditions). I prefer to return `Response` – Paul Samsotha Nov 28 '14 at 05:41
  • To igure nulls, with Jackson, you can simple annotate the POJO class with `@JsonInclude(Include.NON_NULL)` – Paul Samsotha Nov 28 '14 at 06:03
  • Thanks for making so much efforts to help me. Can you please comment on my previous comment "Is it just because I have Application subclass I have to make singleton instance? Because I have created another sample app which uses web.xml instead and that does not give this error." – user3275095 Nov 28 '14 at 06:04
  • Also I suggest keeping the [Jersey User Guide](https://jersey.java.net/documentation/latest/index.html) close. You will find many of your answers there. It's structured well and easy to find topics of interest – Paul Samsotha Nov 28 '14 at 06:04
  • Are you still using an arg-constuctor, with no no-arg constructor? – Paul Samsotha Nov 28 '14 at 06:05
  • for the time being I have not declared the constructor as what you explained about singleton was bit heavy for me.. :) – user3275095 Nov 28 '14 at 06:07
  • Oh, well your original question was about an error when you create a constructor in `UserResource` (that was my understanding). I under the assumption you meant creating a constructor with an argument. And as you know all Java classes come implicitly with a no-arg constructor. Once you create a constructor with a arg, you cancel out that implicit no-arg constructor. In order for Jersey to construct you `UserResource` it needs a no arg constructor. The reason i said to use a singleton, is that is the only way we can construct the `UserResource` ourselves, with an argument – Paul Samsotha Nov 28 '14 at 06:11
  • 1
    ... Doing that, Jersey will not ever try to create the `UserResource`, as we create it ourselves. If we let Jersey create it, we are implicitly saying that the class should _not_ be a singleton, in which case Jersey will create a new instance for each request. Where as with a singleton, the same instance will be used for each request, which may not be desired, depending on the application semantics – Paul Samsotha Nov 28 '14 at 06:14