JAX RS: Sub Resources

In this post, I am going to explain about the JAX RS Subresources with an example.

JAX RS Resource classes can partially process a request and provide an another “sub” resource object that can process the remainder of the request.  Those classes will be called as Subresources.

Subresources are used to represent the relationships between the resources. If a resource is part of another resource, then we can use Subresources. An example for this is Employee and Address resources. Here the Employee object contains the Address object.

If you want to design a readable API, then Subresources is a perfect choice.

If you want to design a flexible API, then you should use fewer Subresources.

The Subresources can’t be used separately and also if you have more level, then its very hard to remember the URL path.

Now let’s see it with an example, Assume that we have a JAX RS web service which returns the “Employee” and “Address” details. Here we can design the Employee as root resource and the Address as Subresource.

Here are the value classes for Employee and Address.

Employee.java



public class Employee {

    private String id;

    private String firstName;

    private String lastName;

    private Address address;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Employee{");
        sb.append("id='").append(id).append('\'');
        sb.append(", firstName='").append(firstName).append('\'');
        sb.append(", lastName='").append(lastName).append('\'');
        sb.append(", address=").append(address);
        sb.append('}');
        return sb.toString();
    }
}

Address.java



public class Address {

    private String streetAddress;

    private String city;

    private String state;

    private String zip;

    public String getStreetAddress() {
        return streetAddress;
    }

    public void setStreetAddress(String streetAddress) {
        this.streetAddress = streetAddress;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }

    public String getZip() {
        return zip;
    }

    public void setZip(String zip) {
        this.zip = zip;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Address{");
        sb.append("streetAddress='").append(streetAddress).append('\'');
        sb.append(", city='").append(city).append('\'');
        sb.append(", state='").append(state).append('\'');
        sb.append(", zip='").append(zip).append('\'');
        sb.append('}');
        return sb.toString();
    }
}

The below things should be kept in mind while creating the Subresources.

  1. Sub Resource class should not have the @Path annotation. In our example, AddressResource is the Sub Resource class and it does contain the @Path annotation.
  2. Subresource method should not have the HTTP method and it should return an object of the subresource class. In this example, getEmployeeAddress is the subresource method and it returns the AddressResource Object

Refer below our EmployeeResource class. I use a HashMap to store the Employee details as this is just an example. The getEmployeeData() method return an employee object and the createEmployee create an employee object.

The method getEmployeeAddress() returns the AddressResource. So when we access ‘/api/employee/{ID}/address’ the JAX-RS runtime will map the request to AddressResource class and call the appropriate method.

EmployeeResource.java


import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

@Path("employee")
public class EmployeeResource {

    private static Map empLocalCache = new HashMap();

    static{
        //For testing purpose only.
        createTestData();
    }

    public static void createTestData() {
        Employee employee = new Employee();
        employee.setId("123");
        employee.setFirstName("test");
        employee.setLastName("test");
        Address address = new Address();
        address.setStreetAddress("streetAddress");
        address.setCity("Columbus");
        address.setState("OH");
        address.setZip("43202");
        employee.setAddress(address);
        empLocalCache.put("123", employee);
    }

    @GET
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Employee getEmployeeData(@PathParam("id") String id) {
        return empLocalCache.get(id);
    }

    @Path("{id}/address")
    public AddressResource getEmployeeAddress(@PathParam("id") String id) {
        return new AddressResource(id);
    }

    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public Employee createEmployee(Employee employee) {
        employee.setId(UUID.randomUUID().toString());
        empLocalCache.put(employee.getId(), employee);
        return employee;
    }

    public class AddressResource {

        private String id;

        AddressResource(String id){
            this.id = id;
        }

        @GET
        @Produces(MediaType.APPLICATION_JSON)
        public Address getAddress() {
            Optional optional = Optional.ofNullable(empLocalCache.get(id));
            return optional.isPresent()? optional.get().getAddress(): null;
        }
    }
}

Testing
The first step is to create an employee details with Address data. So we have to call /api/employee(POST) method with the employee data in the body.


URL: /api/employee
Method: POST
Header: Content-Type:application/json
Body:
{
    "firstName": "test",
    "lastName": "test",
    "address": {
        "streetAddress": "streetAddress",
        "city": "Columbus",
        "state": "OH",
        "zip": "43202"
    }
}

Response:

{
    "id": "cfd59155-a4cf-4fdd-83ae-5198c506d56a",
    "firstName": "test",
    "lastName": "test",
    "address": {
        "streetAddress": "streetAddress",
        "city": "Columbus",
        "state": "OH",
        "zip": "43202"
    }
}

Next step is to get the address of this employee with sub resource method.


URL: /api/employee/cfd59155-a4cf-4fdd-83ae-5198c506d56a/address
Method: GET
Response:

{
    "streetAddress": "streetAddress",
    "city": "Columbus",
    "state": "OH",
    "zip": "43202"
}

One thought on “JAX RS: Sub Resources

  1. Pingback: Java: Zip collections using Guava library | Bala's Blog

Leave a comment