JavaWeb-based JavaLeveraging the Restlet Routing System for Your Java Web Services

Leveraging the Restlet Routing System for Your Java Web Services





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!

Overview of the Available Filters

In addition to the base Filter class, the Restlet API comes with several filters ready to use, as illustrated in the Figure 3. First, we have the Extractor providing a way to extract values from request cookies, query parameters or posted forms, and to store them as request attributes for easier processing down the road, for example, using the Validator filter, which can check for the presence of attributes with a given name and the format of their string value.

Then, we find two security related filters, the Authenticator, to verify the credentials given by a user or to ask him to provide them, and the Authorizer, to enforce access policies.

org.restlet.routing.Filter subclasses
Figure 3.
Class diagram showing some common org.restlet.routing.Filter subclasses

Note that those filters should only be used if their behavior is useful for a set of target resources; otherwise, it is better to add this logic inside your resources directly. Finally, there is a fifth filter called TemplateRoute that isn’t displayed in the figure because it is rarely used directly. Instead, you generally leverage it via the Router class, which is our next topic.

Dispatching Calls Based on URIs with a Router

So far, we illustrated how inbound or outbound calls could be filtered in order to apply pre-processing or post-processing. What is missing from this puzzle is a way to direct calls to target resources, to route them based on properties like the target URI. This is the purpose of the org.restlet.routing.Router class, a direct subclass of Restlet, which is composed of a list of routes and a set of properties that define how the routing of an incoming call to one of the routes actually happens. Of course, like all Restlet subclasses, a Router must be thread safe to support the routing of several concurrent calls.

In Figure 4, we illustrated how three calls (A, B, and C), supported by three concurrent threads, enter a Router instance to end into one of the three attached routes (1, 2, and 3) but not necessarily at the same time. In this example, call A reaches route 1, while calls B and C reach route 2. If you pay attention to the form of the figure used for routes, you will recognize the cylinder that we used for filters.

The actual class used is TemplateRoute in the same package, which defines the next Restlet like all filters, but also a score(Request, Response) method. As its name implies, this method computes an affinity score based on the matching of the URI of the target resource (contained in the Request#resourceRef property) with a given URI template. An example of URI template is http://localhost:8182/accounts/{accountId}.

Restlet router handling concurrent calls
Figure 4.
Router handling three concurrent calls and dispatching them to attached routes

In order to illustrate what we just learned, let’s run a simple example based on the Tracer and Blocker filters that we previously developed and attach them to a Router instance (see Listing 4).

Listing 4. Illustrating URI-based routing

@@Override
    public Restlet createInboundRoot() {
        Tracer tracer = new Tracer(getContext());

        Blocker blocker = new Blocker(getContext());
        blocker.getBlockedAddresses().add("127.0.0.1");
        blocker.setNext(tracer);

        Router router = new Router(getContext());
        router.attach("http://localhost:8182/", tracer);
        router.attach("http://localhost:8182/accounts/", tracer);
        router.attach("http://localhost:8182/accounts/{accountId}", blocker);

        return router;
    }

In this scenario, we create two filters, one Tracer instance and one Blocker instance pointing to the tracer. Then, based on URI templates, we define which filter will be invoked when the request’s target URI matches a given route. Note that TemplateRoute instance doesn’t need to be explicitly created; instead, we can rely on the handy attach(String, Restlet) method. Let’s start our updated application and test a few URIs in our browser, as detailed in Table 1.

Table 1. Results of a routing example.

URI retrieved Result observed
http://localhost:8182/ Trace results are displayed
http://localhost:8182/?param=value Trace results are displayed, including the query string
http://localhost:8182/123 Error: “The server has not found anything matching the request URI”
http://localhost:8182/accounts/ Trace results are displayed
http://localhost:8182/accounts/123 Error: “Your IP address was blocked”

One of the nice features of the Router and, especially, the TemplateRoute is that the URI variables are automatically extracted and stored in the request’s attributes map. In our last example URI, the string 123 corresponding to the {accountId} variable in the URI template http://localhost:8182/accounts/{accountId} will be stored in an attribute named accountId. This will be very convenient when we actually handle the call in a ServerResource subclass to retrieve the underlying domain object from the database.

In some cases, it is necessary to customize the way the URI matching happens. The Router class has a full range of options to take care of this need, such as a defaultMatchingMode property indicating if only the beginning of the URI must match (see the org.restlet.routing.Template#STARTS_WITH constant) or if all the URI must match (see the EQUALS constant). The latter case is the default mode since Restlet 2.0, so be careful if you upgrade from the previous version.

Another property is defaultMatchQuery, which indicates if the query string at the end of target URIs (the characters after the optional “?” character) should be considered during matching. By default, its value is false as the order of query parameters is rarely enforceable. Restlet developers tend to prefer handling them inside the resource classes. Keep also in mind that it is possible to customize the characters that are matched by the URI template variables leveraging the Template and Variable classes.

Also, when the router evaluates each route, it is possible to define when it considers a given route as the one to select and pass the call to. This is the purpose of the routingMode property, which can have one of the following values (those constants are defined in the Router class):

  • Best match
  • First match (default since Restlet 2.0)
  • Last match
  • Random match
  • Round robin
  • Custom

This opens the way for many interesting usages, such as writing a load-balancer of calls to several instances of a back-end web service. But there is more: in addition to Restlet instances, you can also attach server resource classes directly to a Router using an attach(String, Class) method, as illustrated in Figure 5. But what happen exactly inside this method?

Restlet router dispatching concurrent calls
Figure 5.
Router dispatching concurrent calls to three target server resources

First, a TemplateRoute instance is created and added to the list of routes of the router, and then a special Restlet subclass called org.restlet.resource.Finder is attached to the route. This class will store the class name of the target resource and create a new instance of it for each incoming call. This is very similar to the factory design pattern. With the introduction of the Finder class, we see how the transition from the user routing layer to the resource handling layer will happen.

Summary

In this article, we learned how Restlet applications are structured in three concentric layers and the detailed design of each layer. In addition, we explored the Restlet routing system, which allows the pre-processing and post-processing of both inbound and outbound calls with the Filter class and the dispatching of calls to attached routes by the Router class.

About the Author

Jerome Louvel is the co-founder of Noelios Technologies and the creator of Restlet.

Thierry Boileau is the co-founder of Noelios Technologies and has been a core developer of the Restlet Framework since 2006.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories