Subresources in JAX-RS , Subresource locators , Dynamic dispatching

Subresources in JAX-RS

 In the previous chapter, we discussed the @Path annotation that identifies the URI path that a resource class or class method will serve requests for. A class annotated with the @Path annotation (at the class level) is called the root resource class. You can also use the @Path annotation on the methods of the root resource classes. If a resource method with the @Path annotation is annotated with request method designators such as @GET, @POST, @PUT, or @DELETE, it is known as a subresource method.

The concept of subresources is to have a root resource class that resolves a generic URI path and to have the @Path annotated methods in the class to further resolve the request. This helps you to keep the REST resource class implementation more structured and readable.

Let's consider a simple example to understand this topic better. Look at the following code snippet:

//imports are omitted for brevity 
@Path("hr") 
public class HRService { 
 
    @GET 
    @Path("departments") 
    @Produces(MediaType.APPLICATION_JSON) 
    public List<Department> findAllDepartmnets () { 
       
        List<Department> departments = 
            findAllDepartmentEntities(); 
        return departments; 
    } 
 
    @GET 
    @Path("departments/{id}") 
    @Produces(MediaType.APPLICATION_JSON) 
    public Department findDepartment(@PathParam("id") Short id) { 
 
        Department department = findDepartmentEntity(id); 
        return department; 
    } 
} 

When a client calls the HTTP GET method with the URI hr/departments, the JAX-RS runtime on the server resolves the hr portion of the URI first. In this example, the hrpath fragment resolves to the HRService class. Next, it identifies a subresource that matches the remaining part of the URI for the given HTTP request type. This part is resolved to the findAllDepartmnets() method. However, if the request URI is hr/departments/10, the URI path will be resolved to the findDepartment() subresource method.

Subresource locators in JAX-RS

Having discussed subresources, we will now see a subresource locator.

It is perfectly legal to use the @Path annotations on methods in a REST resource class without any resource method designators such as @GET@POST@PUT, or @DELETE. The objects returned by these methods will be used for resolving the incoming HTTP request further. These methods are called the subresource locators. These locators are useful when requests need to be further resolved dynamically by other objects. This gives you more flexibility in implementation.

In the following example, DepartmentResource is the root resource and the findManagerForDepartment() method is a subresource locator method. The findManagerForDepartment()method has only the @Path annotation in the code snippet. The EmployeeResource object returned by this method further resolves the HTTP request:

// Imports are omitted for brevity 
@Path("departments") 
public class DepartmentResource { 
    
    //Sub-resource locator method 
    @Path("{id}/manager") 
    public EmployeeResource  
        findManagerForDepartment(@PathParam("id")  
        Short deptId) { 
        //Find the department for id 
        Department department = findDepartmentEntity(deptId); 
        //Create the instance of Employee object 
        //This instance will be used for further resolving request 
        EmployeeResource employeeResource = new  
            EmployeeResource(department.getManagerId()); 
        return employeeResource; 
    } 
//Other methods are omitted for brevity 
} 
 
/** 
  *This class that defines the subresource used in  
  *DepartmentResource 
*/ 
//imports are omitted for brevity  
public class EmployeeResource{ 
    Short employeeId; 
    public EmployeeResource(Short employeeId){ 
        this.employeeId=employeeId; 
    } 
    //Resolves GET request to findEmployee() method 
    @GET  
    @Produces(MediaType.APPLICATION_JSON) 
    public Employee findEmployee(){ 
        Employee employee = findEmployeeEntity(employeeId); 
        return employee; 
    } 
    //Other methods are omitted for brevity 
 
} 

In this example, let's see what happens on the server when a client calls the HTTP GET method with the following URI: departments/10/manager.

The JAX-RS runtime on the server resolves the URI part of the HTTP request to the findManagerForDepartment() method defined on the DepartmentResource class. The runtime further resolves the request on the EmployeeResource object returned by the findManagerForDepartment() method and identifies the findEmployee() that matches the HTTP request type. The HTTP request will be then dispatched to the findEmployee() method defined on the EmployeeResource instance. 

In the preceding example, you are managing the life cycle of the subresource by creating an instance of EmployeeResource in the code and returning it from the findManagerForDepartment() method. You can ask the JAX-RS runtime to manage the subcomponent instance by returning the class instead of an instance, as given in the following code:

@Path("{id}/manager") 
public Class<EmployeeResource>  
    findManagerForDepartment(@PathParam("id") Short deptId) { 
    return EmployeeResource.class; 
} 

In this case, the runtime will manage the initialization of the subresource instance (EmployeeResource), including all dependency injections via Context and Dependency Injection (CDI), if any. If you are not familiar with CDI, go through the tutorial available at http://docs.oracle.com/javaee/7/tutorial/partcdi.htm.

Dynamic dispatching

In real-life scenarios, a subresource locator method can return different implementations of the subresource class on the basis of various conditions. This makes the implementation more flexible and dynamic.

Let’s say we need to operate on the DepartmentResource or EmployeeResource. This can be accomplished as follows, with the getResource method returning the DepartmentResourceor EmployeeResource class, based on the user-specified resource type. JAX-RS will introspect the returned type to dynamically dispatch the request to the target resource:

@Path("enterprise")
public class EnterpriseResource {

@Path("{resourcetype}")
public Class getResource(@PathParam("resourcetype") String resourceId) {

if (resourceId.equals("department") ) {
return DepartmentResource.class;
} else {
return EmployeeResource.class;
}
}
}



Comments

Popular posts from this blog

Understanding the JAX-RS resource life cycle

Generating a chunked output using Jersey APIs