Resource API refactoring

Requirements

Enhancement request

Related discussions

References

Analysis

Synthesis

  1. Client-side resources
  2. Server-side resources
  3. Dispatching of uniform method to matching Java method
  4. Extension methods (WebDAV, PATCH, etc.)
  5. Detection of available variants
  6. Dispatching to representation producing method based on selected variant
  7. Mapping between ResourceException and reponse status
  8. Automatic conversion between representations and objects (beans/pojos, common technical classes such as a DOM Document)
  9. Automatic validation of representations received
  10. Automatic validation of parameters received
  11. Client-side Restlet post-processing (eg. filters, routers, pre-authentication, cookie management, etc.)
  12. Server-side Restlet pre-processing (eg. filters, routers)
  13. WADL resources (client and server side)
  14. Atom resources (client and server side)
  15. RoR active resources (client and server side)
  16. .Net data services resources (client and server side)
  17. Database resources
  18. Support IoC frameworks (Spring and Guice)
  19. Support GWT
  20. Automatic convertion between XML and JSON (via converters)
  21. Support the separation of composite representations into multiple method parameters

Design

  • Unified API in Restlet 2.0 between Restlet 1.1 Resource class and JAX-RS 1.0 annotations to provide the best of both worlds
  • Use subclassing for resources foundation
  • Limited use of annotations to simplify variants declaration and method dispatching
  • Each resource should be able to optionally wrap another resource (for WADL enrichment for example)
  • Think about using additional annotations in the WADL extension to describe a resource (either JAX-RS API or Restlet API resources)
  • Use an internal "org.restlet.engine.Method" annotation to annotate actual annotations (such as org.restlet.resource.Get) in order to declare the method name and allow easy creation of extension methods.
  • Replaced the Resource#allow*() methods by leveraging the new Security API (granular authorization).
  • Use more flexible variants declaration mechanism using a Map<Method, Object> where objects can be instances of Variant, Dimension, MediaType, etc.

Architecture

diagrams

Class diagram

Here is a tentative class design:

Resource design

Annotations

We could also define a set of method level annotations:

Annotation

Description

Delete

Annotate methods that remove resources.

Get

Annotate methods that represent a variant of a resource

Options

Annotate methods that accept representations.

Post

Annotate methods that accept representations.

Put

Annotate methods that store representations.

Annotations parameter

All annotation have a single optional parameter. Its name is the default "value" name allowing a compact annotation syntax.

Here is the grammar for this parameter:

CHARACTER  = 'a-z' | 'A-Z' | '0-9'
EXTENSION  = CHARACTER [CHARACTER]*
SEPARATOR  = '+' | '|' | '*' | '(' | ')'
TOKEN      = EXTENSION | SEPARATOR
VARIANT    = TOKEN [TOKEN]*
INPUT      = VARIANT
OUTPUT     = VARIANT
ANNOTATION = INPUT [',' INPUT]* [':' OUTPUT]

Here are some valid values:

@Get("xml") : Returns a representation in the "text/xml" media type
String toString();

@Put("xml") : Stores representations in the "text/xml" media type after conversion to a DOM document
void store(Document doc)

@Put("xml:txt") : Stores representations in the "text/xml" media type after conversion to a DOM document and returns a plain text response
String store(Document doc)

[To be continued and detailled]

In order to validate the value of the annotations an annotation processor will be developped, checking the syntax of the value and the fact that it matches registered metadata extensions.

Sample code

Here is how a sample resource would look like with the refactored API. Note that both extension names and full MIME type would be supported. Extensions can be updated via the MetadataService.

import java.io.InputStream;

import org.restlet.ext.atom.Feed;
import org.restlet.resource.Delete;
import org.restlet.resource.Get;
import org.restlet.resource.Post;
import org.restlet.resource.Put;
import org.restlet.resource.Representation;
import org.restlet.resource.ServerResource;
import org.w3c.dom.Document;

public class TestResource extends ServerResource {

    @Get
    public Feed toAtom() {
        // ...
        return null;
    }

    @Get("xml")
    public Representation toXml() {
        // ...
        return null;
    }

    @Post("xml")
    public Representation accept(Document entity) {
        // ...
        return null;
    }

    @Put("atom")
    public void storeAtom(Feed feed) {
        // ...
    }

    @Put("cust")
    public void storeXml(InputStream stream) {
        // ...
    }

    @Delete
    public void removeAll() {
        // ...
    }

}

What is nice is that we would keep the high-level terminology that we have in Restlet 1.1 (accept, store, etc.) but make it available in a more flexible and declarative way.   The new ServerResource would only have lower-level methods, but with a more flexible signature than in Restlet 1.1:

  • void handle()
  • get() : Representation
  • get(Variant) : Representation
  • head() : Representation
  • options() : Representation
  • options(Variant) : Representation
  • post(Representation) : Representation
  • put(Representation) : Representation
  • delete() : Representation

Implementation

Comments (0)