Blog

- February 29, 2016

There are several ways to optimize Restlet applications. We can leverage features of the HTTP protocol itself like caching or compression but also at the level of third-party tools like Restlet converters and template engines. This allows you to have an application that provides quicker responses and optimized contents.

Using caching

One possible optimization is not to serve related resources (like images, css…) when loading a particular resource with HTML content. An approach can be to use cache support provided by HTTP. In this case, responses will only contain status headers if the resource doesn’t change.

Here we will describe how to apply browser caching for all static elements loaded from a path with a subfolder called nocache. For these elements, headers for caching will be automatically added. For others, an expiration date of one month will be specified in the headers.

This feature can easily be brought to Restlet applications using filters within the method createInbountRoot of your application class. A filter containing processing to configure caching can be added in front of the Restlet Directory that serves static content, as described below:

router.attach("/static", new Filter(getContext(),
                        new Directory(getContext(), (...))) {
    protected void afterHandle(Request request, Response response) {
        super.afterHandle(request, response);
            [adding caching configuration here]
        }
});

Once the filter is added in the processing chain, we have to handle caching headers based on objects of types Representation and Response. The method noCache of the class CacheDirective automatically adds the related headers to configure no cache. For expiration date, the method setExpirationDate of the class Representation allows you to define the laps of time before reloading the element content. The following code describes the complete implementation of the filter:

router.attach("/static", new Filter(getContext(),
                         new Directory(getContext(), (...))) {
    protected void afterHandle(Request request, Response response) {
        super.afterHandle(request, response);
        if (response.getEntity() != null) {
            if (request.getResourceRef().toString(false, false)
                                                        .contains("nocache")) {
                response.getEntity().setModificationDate(null);
                response.getEntity().setExpirationDate(null);
                response.getEntity().setTag(null);
                response.getCacheDirectives().add(
                                              CacheDirective.noCache());
            } else {
                response.setStatus(Status.SUCCESS_OK);
                Calendar c = new GregorianCalendar();
                c.setTime(new Date());
                c.add(Calendar.DAY_OF_MONTH, 1);
                response.getEntity().setExpirationDate(c.getTime());
                response.getEntity().setModificationDate(null);
            }
        }
    }
});

We can see that such mechanisms are specific to static content with the Directory class. They can also apply to server resources.

Compressing content

Modern browsers support compression for received content. This allows you to reduce payload of exchanged data. Restlet supports this feature for server-side applications using the class Encoder. This class can be part of the processing chain in the same way that routers, authenticators and filters. You simply need to configure it within the method createInbountRoot of your application class, as described below:

Encoder encoder = new Encoder(
         getContext(), false, true, getEncoderService());
encoder.setNext(router);
return encoder;

Configuring specific converters

The converter feature of Restlet Framework allows you to automatically convert beans to and from representation contents transparently. Such beans can be directly used at the level of annotated methods of server resources. This is an important feature for structured payloads since we can work on data objects instead of raw text contents.

This feature commonly uses third-party tools. One of the most used converter corresponds to the one based on the Jackson library. It supports several formats like XML, JSON and YAML. Putting the corresponding Restlet extension (org.restlet.ext.jackson) and its dependencies is enough to use this converter. Restlet Framework automatically detects them within the classpath and registers them against the Restlet engine.

At this point, Jackson is used automatically. We can decide to go a bit further and provide some specific configuration. For such use cases, we need to get the instance of the registered converter. The following snippet describes how to get an instance of the Jackson converter:

private JacksonConverter getRegisteredJacksonConverter() {
    JacksonConverter jacksonConverter = null;
    List<ConverterHelper> converters
             = Engine.getInstance().getRegisteredConverters();
    for (ConverterHelper converterHelper : converters) {
        System.out.println(converterHelper.getClass());
        if (converterHelper instanceof JacksonConverter) {
            jacksonConverter = (JacksonConverter) converterHelper;
            break;
        }
    }
}

Now we have an instance of the converter, we can get an instance of its associated ObjectMapper. It allows you to configure the serialization and deserialization of the tool. In the following code, we describe how to set a serialization property not to include null values within the generated content:

private void configureJacksonConverter() {
    JacksonConverter jacksonConverter = getRegisteredJacksonConverter();

    if (jacksonConverter != null) {
        ObjectMapper objectMapper = jacksonConverter.getObjectMapper();
        objectMapper.getSerializationConfig().withSerializationInclusion(
                                       JsonSerialize.Inclusi‌​on.NON_NULL);
    }
}

This way the content will only include non null properties and its size will potentially be smaller.

Configuring template engines

If we need to dynamically create on the server side representation contents with formats like HTML and even XML or JSON, template engines can be helpful within server resources. For such use cases Restlet provides dedicated representations that correspond to dedicated sub-classes of the class Representation per library. Such is the case for the Freemarker library.

Such engines can be configured to make content generation more efficient. For example, we can configure Freemarker to load templates once and share them against all content generations. For this, we need to set the property cacheStorage with an instance of class StrongCacheStorage. This can be done within the Restlet application class and then be accessible for all server resources, as described below:

public class SampleApplication extends Application {
    (...)
    private Configuration configuration;

    public static Configuration configureFreeMarker(Context context) {
        Configuration configuration = new Configuration();
        ClassTemplateLoader loader = new ClassTemplateLoader(
                                      SampleAppApplication.class,
                                      "/org/myapp/sample/server/templates/");
        configuration.setTemplateLoader(loader);
        configuration.setCacheStorage(new StrongCacheStorage());
        return configuration;
    }

    public Configuration getConfiguration() {
        return configuration;
    }
}

The class TemplateRepresentation of the Freemarker extension of Restlet Framework can then be used within the server resources, as shown below:

public class MyServerResource extends ServerResource {

    private SampleApplication getSampleApplication() {
        return (SampleApplication)getApplication();
    }

    private Representation toRepresentation(Map<String, Object> map,
                                       String templateName, MediaType mediaType) {
        return new TemplateRepresentation(templateName,
                getSampleApplication().getConfiguration(), map, mediaType);
    }

    @Get("html")
    public Representation getHtml() {
        Map<String, Object> model = new HashMap<String, Object>();

        model.put("titre", "my title");
        model.put("users", getUsers());

        return toRepresentation(model,
            "myTemplate", MediaType.TEXT_HTML);
    }
}

Conclusion

As you can see, Restlet Framework provides several ways to optimize your server applications. It makes it possible for you to leverage mechanisms provided by the HTTP protocol itself and the capabilities of third-party tools used when implementing applications.

Such possibilities are directly integrated in both the Restlet Framework API and specific features like converters that allow you to configure third-party tools used.