Guy Yafe
Guy Yafe

Reputation: 1021

Jersey: Unable to Get Sub-Sub-Resource

I have a web application implementing REST API using Jersey. The web container id Tomcat
Here is a summary of the API:

/rest/patients
Gets a list of patients' metadata.

/rest/patients/{id}
Gets detailed data about a specific patient.

/rest/patients/{id}/visits
Gets a list of visits` metadata for a specific patient.

/rest/patients/{id}/visits/{visitId}
Gets detailed data about a specific visit of a specific patient.

My problem is that I can't get the sub-sub resources. For example, when I request /rest/patients/1 the detailed data of patient #1 is received correctly.
But when I request /rest/patients/1/visits I get 404 error, and the flow doesn't even enter the getVisits() method.
It looks like that when a request for a specific patient id received (patients/{id}), Jersey is directing it correctly from PatientsMetadataResource to PatientsResource.
But when a visits sub-sub-resource is being requested (patients/{id}/visits), Jersey doesn't direct it into the PatientsResource.

So how can I direct a sub resource along with all of its sub-sub resources into the same class?

Code for PatientsMetadataResource (The name is a bit vague, and I need to change it):

@Path("/patients")
public class PatientsMetadataResource {

  @GET
  @Produces(MediaType.APPLICATION_JSON)
  public Response getPatients(@QueryParam("page") int pageIndex) {
    //.... Loads, Builds and returns the patients' metadata list correctly
  }

  @GET
  @Produces(MediaType.APPLICATION_JSON)
  @Path("/{uid:\\d+}")
  public PatientResource getPatient(@PathParam("uid") int uid) {
        return new PatientResource(uid);
  }

}

Code for PatientResource:

public class PatientResource {

  private final int uid;

  public PatientResource(int uid) {
    this.uid = uid;
  }

  @GET
  @Produces(MediaType.APPLICATION_JSON)
  public Response getPatient() {
    //Returns the patient correctly
    System.out.println("A Patient was asked");
    Patient patient = PersistentDataProvider.loadPatientByUid(uid);
    return Response.ok(patient).build();
  }

  @GET
  @Path("/visits")
  @Produces(MediaType.APPLICATION_JSON)
  public VisitsResource getVisits(@PathParam("uid") int patientUid) {
    //The flow doesn't even enter here. A 404 is being returned instead.
    System.out.println("Visits were asked");
    return new VisitsResource(patientUid);
  }
}

Code for Jersey part in web.xml:

<servlet>
    <servlet-name>Jersey REST Service</servlet-name>
    <servlet-class>
      com.sun.jersey.spi.container.servlet.ServletContainer
    </servlet-class>
    <init-param>
      <param-name>com.sun.jersey.config.property.packages</param-name>
      <param-value>il.co.site_building.dvardy.resources</param-value>
    </init-param>
    <init-param>
      <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
      <param-value>true</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Jersey REST Service</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>

Upvotes: 1

Views: 1153

Answers (1)

Paul Samsotha
Paul Samsotha

Reputation: 208944

Sub-Resource Locators aren't supposed to have HTTP method annotations

// @GET    <--- Remove this
// @Produces(MediaType.APPLICATION_JSON)
@Path("/{uid:\\d+}")
public PatientResource getPatient(@PathParam("uid") int uid) {
   return new PatientResource(uid);
}

Their main purpose is simply to forward request to the sub-resource class, not GET/POST/etc anything. When Jersey sees that HTTP method annotation, it no longer gets treated as a sub-resource locator.

Also you don't need need to pass the id. It will get passed accordingly

@Path("parent")
class ParentResource {
    @Path("{id}")
    public ChildResource  getChild() {
      return new ChildResource();
    }
}

class ChildResource {
    @GET
    public Response get(@PathParam("id") long id) {}

    @GET
    @Path("something")
    public Response something(@PathParam("id") long id) {}
}

Here GET 'parent/1' goes to ChildResource.get, passing the path param and GET parent/1/something goes to ChilsResource.something, passing the path param

Upvotes: 2

Related Questions