3

I am using the jersey implementation of JAX-RS for the web service. I am very new to this JAX-RS.

I am trying to add a method in the service which accept an Employee object and returns the employee Id based on the Employee object values (there is a DB hit for this).

Following the Restful principles, I made the method as @GET and provided the url path as shown below:

@Path("/EmployeeDetails")
public class EmployeeService {
@GET
@Path("/emp/{param}")
public Response getEmpDetails(@PathParam("param") Employee empDetails) {

    //Get the employee details, get the db values and return the Employee Id. 

    return Response.status(200).entity("returnEmployeeId").build();

}
}

For testing purpose, I wrote this Client:

public class ServiceClient {

public static void main(String[] args) {

    ClientConfig config = new DefaultClientConfig();
    Client client = Client.create(config);
    WebResource service = client.resource(getBaseURI());

    Employee emp = new Employee();
    emp.name = "Junk Name";
    emp.age = "20";
    System.out.println(service.path("rest").path("emp/" + emp).accept(MediaType.TEXT_PLAIN).get(String.class));

}

private static URI getBaseURI() {
    return UriBuilder.fromUri("http://localhost:8045/AppName").build();
}

}

When I run it, I am getting the error: Method, public javax.ws.rs.core.Response com.rest.EmployeeService.getEmpDetails(com.model.Employee), annotated with GET of resource, class com.rest.EmployeeService, is not recognized as valid resource method.

Edit:

Model:

package com.model;

public class Employee {

public String name;
public String age;

}

Please let me know where is the issue, I am a beginner in this and struggling to understand the concepts :(

WhoAmI
  • 819
  • 4
  • 18
  • 35
  • Are you sure you don't want to pass in an `employeeId` and return an `Employee` object. – beny23 Feb 22 '13 at 12:41
  • Yes, I know that its unusual to pass the other details and get the ID but thats the requirement. If there are more than one employee, then need to fetch the oldest employee based on their joining date, hence I am sure that I need to pass the employee object and get the employee id. Can you please let me know how to make this work :( – WhoAmI Feb 22 '13 at 12:45
  • Add the definition of your `EmployeeService` class to the question, including any class level annotations. – Perception Feb 22 '13 at 12:48
  • Done. Please check and let me know the issue – WhoAmI Feb 22 '13 at 12:56

3 Answers3

6

JAX-RS cannot automatically convert a @PathParam (which is a string value), into an Employee object. Requirements for objects that can be automatically created from a @PathParam are:

  1. String (defacto, because the data is already a string)
  2. Objects with a constructor that accepts a (single) string as argument
  3. Objects with a static valueOf(String) method

For cases 2 & 3 the object would be required to parse the string data and populate its internal state. This is not normally done (because it forces you to make assumptions about the content type of the data). For your situation (just beginning to learn JAX-RS), its best to just accept the incoming @PathParam data as a String (or Integer, or Long).

@GET
@Path("/emp/{id}")
public Response getEmpDetails(@PathParam("id") String empId) {
    return Response.status(200).entity(empId).build();
}

Passing a complex object representation to a REST service in a GET method doesn't make much sense, unless its being used, eg, as a search filter. Based on your feedback, that is what you are trying to do. I've actually done this on a project before (generic implementation of search filters), the one caveat being that you need to strictly define the format of the search data. So, lets define JSON as the accepted format (you can adapt the example to other formats as needed). The 'search object' will be passed to the service as a query parameter called filter.

@GET
@Path("/emp")
public Response getEmployees(@QueryParam("filter") String filter) {
    // The filter needs to be converted to an Employee object. Use your
    // favorite JSON library to convert. I will illustrate the conversion
    // with Jackson, since it ships with Jersey
    final Employee empTemplate = new ObjectMapper().readValue(filter, Employee.class);

    // Do your database search, etc, etc
    final String id = getMatchingId(empTemplate);

    // return an appropriate response
    return Response.status(200).entity(id).build();
}

And in your client class:

final String json = new ObjectMapper().writeValueAsString(emp);
service
    .path("rest")
    .path("emp")
    .queryParam("filter", json)
    .accept(emp, MediaType.TEXT_PLAIN)
    .get(String.class)
Perception
  • 79,279
  • 19
  • 185
  • 195
  • The requirement was to receive the employee's other details, and return it's id. – zagyi Feb 22 '13 at 13:33
  • Theres no easy way to pass a (complex) object representation as a path parameter. Was the requirement to use @PathParam, or is it just to pass the `Employee` data to the web service? – Perception Feb 22 '13 at 13:39
  • `@Perception` No. it is just to pass the employee information to the web service parameter. I am new to this and hence did not knew about other options than using @PathParam. Is there some other option than using @PathParam to achieve the same? Thanks for the help. – WhoAmI Feb 22 '13 at 13:45
  • 1
    @WhoAmI - I made some assumptions about your requirements, and adjusted my answer. – Perception Feb 22 '13 at 14:10
  • Thanks for the reply. Unfortunately that is not what I am trying to achieve. I want to pass employee name and age in a employee model to the method present in the service. That method will return the employeeId based on the model data details present in DB. If there are more than one employee with same name and age, then I need to return the employeeId of the oldest employee(these things I can take care at the DB side, not a problem). This is what I am trying to achieve. I really appretiate your help and I believe that unfortunately the requirement is quite a bit confusing :( – WhoAmI Feb 22 '13 at 15:14
  • 1
    Sounds like your requirement is to implement a sort of search. See updated answer, again. – Perception Feb 22 '13 at 16:20
  • btw, if I want to send XML rather than JSON from my client then how to create the XML in client? Seems that Jackson is only for JSON, hence is there something which might help me to create XML out of that object and send it to my service? – WhoAmI Feb 23 '13 at 05:33
  • You can use [JAXB](http://www.mkyong.com/java/jaxb-hello-world-example/) to easily marshal objects to XML. Good luck with the coding! – Perception Feb 23 '13 at 06:15
  • Thanks a lot...I just wanted to know is there something similar to JAckson which jersey provides or not... Signing off this thread :) – WhoAmI Feb 23 '13 at 06:37
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/25004/discussion-between-perception-and-whoami) – Perception Feb 23 '13 at 06:40
3

I got the same issue but I just fixed it jersey jars used in your application should have same versions.

Super Hornet
  • 2,839
  • 5
  • 27
  • 55
1

It's quite strange that you want to serialize an entity (Employee) into an url path segment. I'm not saying it's impossible, but strange, and then you have to make sure that the Employee class satisfies the following criteria (which is from the javadoc of @PathParam)

The type of the annotated parameter, field or property must either:
... (irrelevant part)
Have a constructor that accepts a single String argument.
Have a static method named valueOf or fromString that accepts a single String argument (see, for example, Integer.valueOf(String)).

You might want to pass the Employee object in the request body raher than in a path param. To chieve this, annotate the Employee class with @XmlRootElement,remove the @PathParam annotation from the method's empDetails argument, and change @GET to @POST.

zagyi
  • 17,223
  • 4
  • 51
  • 48
  • Yes, thats the point. Thats what I was thinking like how to pass employee object in URL without serializing it, please bear with me since I am a beginner and today is my second day in it. If I try to put in in the body of request and return the request, then how will the code look like? Can you please post a sample code for the same? – WhoAmI Feb 22 '13 at 13:38
  • You can easily find good examples/tutorials, just google for "jax-rs tutorial". It's easier to use them, because you might come across problems that are already explained there in details. – zagyi Feb 22 '13 at 14:00
  • Thanks for the inputs. I tried this link: `http://www.mkyong.com/webservices/jax-rs/download-xml-with-jersey-jaxb/` where they are accepting a String and returning the XML which is the reverse to what I am trying to achieve. Still not able to find out what you suggested :( Can you please post some links where I can find what you suggested? Thanks for the help – WhoAmI Feb 22 '13 at 15:09
  • 1
    If your object is not too complex, it might be easier to pass that couple of string properties as query parameters, instead of object serialization. You can find examples on the usage of `@QueryParam` e.g. at http://www.mkyong.com/webservices/jax-rs/jax-rs-queryparam-example/ – zagyi Feb 22 '13 at 15:18
  • yes, that I know and it will be the last option. Can you provide me the link for what you suggested so that the method will accept an object as a parameter? I want to give it a try before moving to my last option. Thanks for the help. – WhoAmI Feb 22 '13 at 15:20
  • See my update about `@GET` -> `@POST`, which might have been a problem. – zagyi Feb 22 '13 at 15:40
  • This could be useful (it's a WebSphere doc, but uses standard jax-rs, I thiknk): http://pic.dhe.ibm.com/infocenter/wasinfo/v8r0/index.jsp?topic=%2Fcom.ibm.websphere.base.doc%2Finfo%2Faes%2Fae%2Ftwbs_jaxrs_xmlcontent_step2.html – zagyi Feb 22 '13 at 15:45
  • But using @Post is not legal in this case right? I am fetching the data but not creating a new record right? – WhoAmI Feb 22 '13 at 15:48
  • 1
    It is absolutely legal. You define your api as you wish, and receiving a message body in a get request won't even work, AFAIK. – zagyi Feb 22 '13 at 15:52
  • yes I agree with your argument since GET cant have a body. But again, according to the Restful style, GET should be used for only retrieving data right? That is my concern that it is a violation to the Restful architecture right? – WhoAmI Feb 22 '13 at 15:57
  • Well, I'd definitely go for get with query params. See this question: http://stackoverflow.com/questions/4268707/what-rest-put-post-delete-calls-should-return-by-a-convention – zagyi Feb 22 '13 at 16:15