December 21, 2014
Hot Topics:

Leveraging the Restlet Routing System for Your Java Web Services

  • April 9, 2010
  • By Jerome Louvel, Thierry Boileau
  • Send Email »
  • More Articles »

This article is taken from the book "Restlet in Action" (Manning Publications; ISBN: 9781935182344), written by Jerome Louvel and Thierry Boileau. The authors provide an overview of Restlet applications and then present Restlet filters and routers, the two key elements of the Restlet routing system. They show how filters facilitate the pre-processing and post-processing of calls, and how the routers allow the dispatching of calls to one of the several target routes available.

As Restlet applications have multiple important purposes, it is essential to understand how they are structured and then used at runtime by Restlet projects. In Figure 1, we illustrate their overall design into three concentric layers, from the most generic and reusable on the edge to the most specific in the center.

Restlet application structure
Figure 1.
Restlet applications are structured into three concentric layers, processing inbound and outbound calls in logical steps.

An application can handle both inbound server calls and outbound client calls, which are depicted using arrows.

Inbound and Outbound Calls in Restlet Applications

An inbound call first goes though a service filtering layer, which is common to all resources contained in the application. This layer can handle things such as automatic decoding of compressed representations or support for partial representations. Then, the call goes through a user routing layer, where you will be able to do your own filtering, for example, for authentication purpose and to route the request to its target resource, typically based on its URI. Finally, the call can reach the resource handling layer, where a target resource will handle the request and reply with a response that will return to the original client, following the same path. This request processing flow that we summarized is followed by a single Java thread in charge of the given call.

In addition, client calls can be initiated by applications, typically, while handling server calls inside server resources, by creating client resources. A client resource sends a request through the upper layers, before it reaches the target resource (typically on a remote host), and finally comes back with a response. The user routing layer will have a chance to filter or dispatch the request, for example, to handle automatic authentication, while the upper service filtering layer will be able to provide services such as automatic decompression of representations received in responses.

The Restlet Routing System

The user routing layer can be pretty limited, providing just a simple Restlet subclass that traces incoming calls. How can you build a filter that blocks some IP addresses or route calls to target server resource based on their URI?

In this article we will introduce you to the Restlet routing system and answer the above questions. We will especially present Restlet filters and routers which are two key elements of this routing system, the first one facilitating the pre-processing and post-processing of calls, the second one allowing the dispatching of calls to one of the several target routes available, typically based on the target URI of the routed call.

Pre-processing and Post-processing Calls with a Filter

On the server side, after receiving calls, or on the client side, before sending calls, it is common to systematically apply some behavior that doesn't differ much based on the target resource URI of the call. The common name for such processing elements is 'filter.' Naturally, the Restlet Framework offers an abstract org.restlet.routing.Filter class, which is a subclass of org.restlet.Restlet.

Restlet subclasses are multi-threaded
It is important to mention that Restlet subclasses must support concurrent calls and be fully thread-safe. Writing code that behaves properly when executed by several threads (one for each call) is not easy and we highly recommend that you read the "Java Concurrency in Practice" for more on this topic.

In Figure 2, we illustrate three concurrent calls (A, B, and C) processed by the same Filter instance. Each thread supports a call (a request and response couple) and attempts to traverse the filter to reach the next Restlet. Sometimes, the call will get blocked by the filter and the thread will return immediately without going to the next Restlet. Call B is stopped by the filter as illustrated by the "stop" sign.

Restlet Filter
Figure 2.
Filter handling three concurrent calls, passing two of them to the next Restlet and blocking the third one

Like all Restlet subclasses, Filter's entry point is the final handle(Request, Response) method declared in the Uniform interface. This method works in three steps:

  1. First, it invokes the beforeHandle(Request, Response) method which lets the filter pre-process the call.
  2. This method returns a result flag, which indicates if the processing should continue to the next Restlet, skip the next Restlet, or stop immediately and return up the thread stack. In the first case, the filter then invokes the next Restlet that was attached to it by invoking the doHandle(Request, Response) method. If no Restlet is attached, then a "server internal error" (HTTP status 500) is set on the response.
  3. When the next Restlet returns, or if the pre-processing asks to skip the next Restlet, the afterHandle(Request, Response) method is invoked, giving the filter a chance to post-process the call. Post-processing typically is based on the response received, for example, to compress the representation returned for an HTTP GET method.

Let's now look at a concrete example in Listing 1 that provides a blocking filter based on IP addresses. You can see that we used a special thread-safe implementation of the Set<String> interface to store the list of blocked IP addresses.

Listing 1. An IP address blocking filter
public class Blocker extends org.restlet.routing.Filter {

    private final Set<String> blockedAddresses;                         #A

    public Blocker(Context context) {
        super(context);
        this.blockedAddresses = new CopyOnWriteArraySet<String>();
    }

    @Override                                                          
    protected int beforeHandle(Request request, Response response) {    #B
        int result = STOP;

        if (getBlockedAddresses()
                .contains(request.getClientInfo().getAddress())) {
            response.setStatus(Status.CLIENT_ERROR_FORBIDDEN,
                    "Your IP address was blocked");
        } else {
            result = CONTINUE;
        }

        return result;
    }

    public Set<String> getBlockedAddresses() {
        return blockedAddresses;
    }

}
#A The list of blocked IP addresses.
#B The pre-processing method comparing the client IP address with the list of blocked addresses.

In order to test this filter, we need to update our application, especially the createInboundRoot() method to return an instance of the Blocker filter instead of the Tracer class which contains the same logic as in Listing 2, but packaged as a separate class extending Restlet.

Listing 2. Providing the inbound root Restlet for the application
import org.restlet.Application;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.Restlet;
import org.restlet.Server;
import org.restlet.data.MediaType;
import org.restlet.data.Protocol;

public class MailServerApplication extends Application {
   
    public static void main(String[] args) throws Exception {     #A
        Server mailServer = new Server(Protocol.HTTP, 8182);
        mailServer.setNext(new MailServerApplication());
        mailServer.start();
    }

    @Override
    public Restlet createInboundRoot() {                          #B
        return new Restlet() {
            @Override
            public void handle(Request request, Response response) {
                String entity = "Method       : " + request.getMethod()
                        + "\nResource URI : " 
                        + request.getResourceRef()
                        + "\nIP address   : "
                        + request.getClientInfo().getAddress()
                        + "\nAgent name   : "
                        + request.getClientInfo().getAgentName()
                        + "\nAgent version: "
                        + request.getClientInfo().getAgentVersion();
                response.setEntity(entity, MediaType.TEXT_PLAIN);
            }
           };
    }
   }
#A Launches the application with an HTTP server.
#B Creates a root Restlet to trace requests.

You can see in Listing 3 how the filter is returned as the new inbound root and attached to a Tracer instance using the setNext(Restlet) method.

Listing 3. An IP address blocking filter
@Override
    public Restlet createInboundRoot() {
        Blocker blocker = new Blocker(getContext());
        // blocker.getBlockedAddresses().add("127.0.0.1");
        blocker.setNext(new Tracer(getContext()));
        return blocker;
    }

In order to test our filter, you can launch the updated application and point your browser to the http://localhost:8182/test/abcd URI. It should return the same result as with the previous listings. Now, uncomment the second line in the method above and restart your application. If you refresh your browser page, you should see this time the "Your IP address was blocked" message indicating that our filter worked!





Page 1 of 2



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Sitemap | Contact Us

Rocket Fuel